Guides·CAN
Import a DBC file into an LVGL project
Turn a CAN database into typed signals, bind them to LVGL widgets, and watch live values in the simulator — no glue code.
A .dbc file describes your CAN or J1939 network: message IDs, signal layouts, scaling factors, units. Instead of hand-writing parsers and formatters, Protok Studio reads the database directly and exposes every signal as a typed source you can drop onto a widget. This guide walks through the whole path — from raw .dbc to a live gauge in the simulator.
1. Drop the DBC onto the canvas
- Open your project and switch to the canvas view.
- Drag a
.dbcfile from Explorer onto the canvas (or use the file manager import). - Studio parses the file and lists every message and signal — with name, byte layout, factor, offset, and unit already resolved.
Nothing is copied: the project keeps a reference to the database, so when your system engineer pushes an updated .dbc, bindings stay intact and renamed signals are flagged instead of silently breaking.
J1939 works out of the box
2. Bind a signal to a widget
- Place a widget — a gauge, bar, label or arc — on a screen.
- Open its binding HUD and pick a signal from the imported database.
- Choose the target property:
valuefor gauges,textfor labels (with a format string),colorfor threshold indication.
Bindings are typed: Studio knows a signal is uint16 scaled to km/h, so a text binding can offer "{value:.0f} km/h" formatting without any script.
3. Drive it from a real trace
Load an .asc or .dpl CAN log in the simulator panel and press play. The WASM runtime decodes frames with your DBC, pushes signals through bindings, and the LVGL screen updates exactly as it would on the panel — including timing jitter and out-of-range behavior. This is the fastest way to validate layout and scaling before hardware arrives.
4. What lands in generated code
On export, Studio emits a static C99 decoder table from the DBC and a dispatcher that feeds bound widget properties — zero allocations, no dynamic lookups:
/* CAN message dispatch table — only messages referenced by bindings. */
typedef struct {
uint32_t pgn;
uint8_t target_sa;
bool (*handler)(const uint8_t *data, uint8_t dlc);
const char *name;
} pl_can_msg_entry_t;
static const pl_can_msg_entry_t pl_can_msg_table[] = {
{ 0x0F004, 0xFF, EEC1_on_frame, "EEC1" }, /* EngineSpeed → rpm_gauge */
};
/* ISR-safe: latest frame per entry + dirty flags, polled by the UI worker.
* J1939 PDU1 masking and factor/offset scaling are generated for you. */Where to go next