This walkthrough starts with a named DBC signal and ends with a tiny mobile-friendly web app. It demonstrates the intended SignalScope workflow without adding application logic to the CAN core.
The display itself is passive. The optional rule step uses the bundled synthetic frame so you can learn the mutation workflow on a bench or simulator.
CAN frame → SignalScope gateway/cache → DBC decode → HTTP JSON → your HTML
Your page will show:
- the current oil temperature;
- whether the sample is live, stale, or unavailable;
- the source CAN ID and direction;
- the age of the latest frame.
The complete example is in examples/oil-temperature-dashboard/.
Connect and boot SignalScope as described in Getting started. Confirm that the bus-ready states are healthy and raw frame counts increase. Do not create a rule yet.
For a real vehicle, load a DBC that contains the actual oil-temperature signal for that network. The name might be OilTemperature, EngineOilTemp, or something else; a text label is not standardized across DBC authors.
For a simulator or learning frame, keep the bundled default.dbc. It defines:
BO_ 801 EngineStatus: 8 Sensor
SG_ OilTemperature : 0|8@1+ (1,-40) [-40|215] "degC" Dashboard
Decimal CAN ID 801 is hexadecimal 0x321. Raw byte 130 decodes to 90 °C.
In Signal explorer, search for oil and select the likely result. Check:
- CAN ID;
- bit start and length;
- endian and signed settings;
- factor and offset;
- unit;
- live value and age.
On a real network, compare the value with another trusted data source and with the physical behavior of the system. Watch it warm gradually. If it jumps randomly, wraps, or stays implausibly fixed, stop and verify the DBC definition.
SignalScope serves your files and API from the same origin, so the browser needs no API key, CORS setup, npm package, or external server.
async function readOilTemperature() {
const response = await fetch('/api/signal_catalog?q=OilTemperature&limit=16', {
cache: 'no-store'
});
const result = await response.json();
const signal = result.signals.find(item => item.name === 'OilTemperature');
if (!signal || !signal.valid) {
return { state: 'unavailable' };
}
return {
state: signal.live ? 'live' : 'stale',
value: signal.value,
ageMs: signal.ageMs,
canId: signal.canIdHex,
direction: signal.direction
};
}Poll at a human-interface rate such as 500–1000 ms. The CAN task and cache operate independently; making the browser poll every millisecond does not improve the underlying signal.
Copy examples/oil-temperature-dashboard/index.html into your own project or use it as the starting point for data/index.html. It is intentionally one file and heavily commented.
After changing files under data/, upload LittleFS again:
pio run -e lilygo-t2can -t uploadfsYou are now building an app on SignalScope: the core still owns CAN and decoding, while your page decides what to show.
Only do this on the synthetic 0x321 learning network or after independently establishing the correct rule for your own controlled system.
Open the rule workstation's Set a signal mode, select OilTemperature, and choose a static value of 90. The UI converts it using the DBC:
raw = (90 - (-40)) / 1 = 130
The equivalent package row is:
STATIC,0x321,A_TO_B,0,8,1,130
The Signal mode can also stage a manually adjustable dynamic BIT_RANGE with dynamic=1. That rule can be changed at runtime, but it exists only in RAM and is not a persistent .ssrules recipe. For this walkthrough, keep the static selection so the workstation can generate the STATIC row above.
Now follow the state changes deliberately:
- Stage — the candidate appears, but forwarded bytes do not change.
- Apply — the complete staged table becomes active in RAM.
- Observe the output and confirm the mutation marker and expected decoded value.
- Revert/clear staging if it is not correct.
- Install startup package only after it is proven. Installation validates the entire package, activates it, and stores
/rules/active.ssrulesin one transaction.
Reboot once and confirm that the installed package loads. If you only applied a draft, the rule should not survive reboot.
The workstation's other modes are for different jobs. Raw bits stages a RAM-only RAW_MASK through the candidate table. Advanced package generates editable SOURCE_INT, SOURCE_SELECT_INT, bind, counter, sequence, XOR-checksum, and CRC-8/AUTOSAR-checksum recipes. Those recipes do nothing until the complete package is installed successfully.
Natural next steps include:
- add engine speed and vehicle speed cards from the same catalog;
- let users choose any catalog signal and persist its stable CAN-ID/name identity;
- graph only live samples and break the line when data becomes stale;
- record a short session for troubleshooting;
- add a native extension if the app must calculate values while no browser is connected;
- publish a runtime value from that extension and feed it into a dynamic
.ssrulesrule.
That progression is the point of SignalScope: begin with observation and HTML, then add only the embedded behavior your idea actually requires.