Platform · Multi Stacks
Vehicle and industrial-IoT communication, declared as data files.
CAN, J1939, UDS, Modbus, ISOBUS — same engine, one JSON declaration format, one runtime. Stacks run concurrently on the same physical interfaces with independent lifecycles. Protocol changes are JSON edits, not firmware releases.
Part of the no-code engine layer — configure stack behaviour from JSON, no recompile required. Developer-first: full JSON declaration format via the MOS4 SDK.
The declarative model
Four bullets — one engine.
Every Multi Stacks deployment, vehicle bus or industrial IoT, is declared as four moving parts. Periodic strategy ships out of the box; advanced sequences emerge by composing with MSP (the signal-processing engine) and MEP (the event-processor).
1 · Protocol
The transport and framing — CAN, CAN-FD, J1939, ISO-TP, DoIP, UDS, K-Line, ISOBUS, Modbus RTU/TCP, CANopen. Selection is a JSON field; no recompile to add a transport.
2 · Question + Response
What command, PID (parameter ID), PGN (parameter group number), or register to send and how to decode the answer. Decode expressions are inline — bytes in, named signal out.
3 · Broadcast
Read-only and unsolicited message handling — passive sniffing, event-style decoding without a request. The same JSON declares the decode for unsolicited frames.
4 · Strategy
What sequences to run and when. Periodic out of the box. Signal-driven via MSP, event-driven via MEP — the rest of MOS4 supplies the advanced strategy without escaping the declarative model.
Protocol coverage
16 protocol families in one unified API.
| Protocol | Transport | Typical segment |
|---|---|---|
| CAN Classical | CAN | Passenger, LCV, off-highway |
| CAN-FD | CAN | Modern passenger, EVs |
| ISO-TP | CAN segmented | UDS diagnostic layer |
| DoIP | Ethernet | Modern ECU (Electronic Control Unit) diagnostics |
| UDS | ISO-TP / DoIP | Unified diagnostic services |
| OBD-II | ISO-TP / K-Line | Light-duty emissions (ISO 15031) |
| OBFCM | OBD-II / UDS | EU fuel/energy reporting (2021/392) |
| J1939 | CAN | Heavy-duty trucks, buses |
| J1587/J1708 | RS-485 | Legacy heavy-duty |
| J1850 VPW/PWM | Single-wire | Legacy passenger (GM, Chrysler) |
| K-Line | ISO 9141-2 | Older passenger vehicles |
| CANopen | CAN | Industrial, off-highway machinery |
| ISOBUS | CAN (ISO 11783) | Agricultural implements (read-only) |
| Modbus RTU/TCP | RS-485 / Ethernet | Industrial sensors, gensets, PLCs |
| GMLAN | CAN | Legacy GM passenger |
| TP 2.0 | CAN | VAG-group passenger |
Modbus ASCII is experimental — not claimed for production use. ISOBUS is read-only acquisition; VT display authoring is not supported.
Bring your bus and protocol mix — we'll fit Multi Stacks.
Worked example · vehicle (J1939)
Truck dashcam scenario — engine load via J1939.
A heavy-duty fleet dashcam needs engine load and road speed at 10 Hz, alongside diagnostic-trouble-code reads on demand. The whole thing is a single JSON stack.
Minimal J1939 stack JSON
{
"ecu_id": "EngineECU",
"address": "0x00",
"commands": [
{
"name": "engine_load_pct",
"pgn": "0xF003",
"spn": 92,
"period_ms": 100,
"decode": "A * 100 / 255"
},
{
"name": "road_speed_kph",
"pgn": "0xFEF1",
"spn": 84,
"period_ms": 100,
"decode": "(A * 256 + B) / 256"
}
],
"dtc": { "service": "uds", "read": "0x19", "clear": "0x14" }
} Adding a PGN, changing a period, or wiring a new DTC service is a JSON edit and a commit. No recompile, no firmware flash.
Worked example · industrial IoT (Modbus)
Polling a PLC over Modbus RTU.
Same four bullets, different protocol. A factory genset controller exposes input registers; one Modbus stack polls them at 1 Hz, decodes them as named signals, and broadcasts an event when a threshold is crossed.
Minimal Modbus stack JSON
{
"device_id": "Genset-01",
"transport": "modbus_rtu",
"slave_id": 7,
"commands": [
{
"name": "coolant_temp_c",
"function": "read_input_registers",
"address": 30001,
"count": 1,
"period_ms": 1000,
"decode": "A / 10"
},
{
"name": "fuel_level_pct",
"function": "read_input_registers",
"address": 30002,
"count": 1,
"period_ms": 1000,
"decode": "A"
}
],
"broadcast": { "on_threshold": "fuel_level_pct < 10", "emit": "low_fuel" }
} Modbus RTU and Modbus TCP/MBAP share the same JSON format. Switching from one to the other is a single JSON field.
Strategy composition
Multi Stacks driven by MSP or by MEP.
The Strategy bullet is intentionally simple — periodic, with optional broadcast triggers. Advanced sequences emerge when MSP (signal-driven) or MEP (event-driven) is the strategy.
MSP drives Multi Stacks (signal-driven)
An MSP graph watches a decoded signal — say, road speed crossing 80 kph — and triggers a Multi Stacks request (e.g. UDS read of fuel-economy snapshot). The response flows back through the same stack.
An MSP graph monitors a signal threshold and triggers a Multi Stacks UDS request; the Multi Stacks response flows back into MSP.
flowchart LR Sig[MSP graph<br/>signal threshold] -->|trigger| MS[Multi Stacks<br/>send UDS request] MS -->|response| Sig
MEP drives Multi Stacks (event-driven)
An MEP rule fires on a named event (engine ON, geofence cross, OTA download complete) and runs a Multi Stacks action sequence — for example, clear DTCs after a service visit, or read a one-shot ECU snapshot.
An EventBus event triggers an MEP rule, whose action runs a Multi Stacks sequence.
flowchart LR Evt[EventBus<br/>named event] -->|trigger| MEP[MEP rule] MEP -->|action| MS[Multi Stacks<br/>run sequence]
Multi-stack mode
N stacks · one set of physical interfaces.
Each stack runs with an independent lifecycle and export path on the same physical interfaces. Multiple stacks sharing a bus do not require a scheduler — the unified protocol API arbitrates access and enforces isolation.
Three independent stacks share one unified protocol API. Stack A is an OBD-II JSON stack reading passenger PIDs. Stack B is a J1939 JSON stack reading truck PGNs. Stack C is a Modbus JSON stack reading RS-485 sensors. All three feed the unified protocol API, which arbitrates access and enforces independent lifecycles. The manager dispatches to three physical interfaces — CAN0, CAN1, and RS-485 — without bus ownership conflicts. Each stack exports independently: Stack A to MQTT topic A, Stack B to MQTT topic B, Stack C to the policy engine.
flowchart TD S1[Stack A — OBD-II JSON<br/>passenger PIDs] S2[Stack B — J1939 JSON<br/>truck PGNs] S3[Stack C — Modbus JSON<br/>RS-485 sensors] V[Unified protocol API<br/>arbitration · lifecycles] S1 --> V S2 --> V S3 --> V V --> Iface1[CAN0] V --> Iface2[CAN1] V --> Iface3[RS-485] S1 -->|export| MQ1[MQTT topic A] S2 -->|export| MQ2[MQTT topic B] S3 -->|export| MEP[Policy engine]
| Method | Cadence | Description |
|---|---|---|
| SubscribeData | configurable | Decoded, named signal values for MSP and MEP consumption |
| SubscribeBusLoad | ~1 Hz | CAN utilisation metrics per physical interface |
| SubscribeCanFrames | frame-rate | Raw frame tap — merged stream from all active stacks |
The raw frame tap streams in real time for consumption by MSP, the MQTT bridge, and the MOS4 database service.
Capabilities
What ships out of the box.
22 CI-validated default stacks, production-grade DTC handling, J2534-2 passthrough, and built-in circuit-breaker resilience — all available without writing a scheduler or managing bus arbitration manually.
22 CI-validated default stacks
OBD-II, UDS, J1939, ISOBUS, OBFCM, Modbus, CANopen, and others are included and validated on every CI push via Python/pytest standalone tests. Add your own stack as a JSON file alongside them.
DTC reading + clearing
Diagnostic Trouble Codes are read and cleared across the supported stack via OBD-II (On-Board Diagnostics II) service 03/04 or UDS (ISO 14229 unified diagnostic services). Declared in the stack JSON — no custom code required.
J2534-2 passthrough
A PassThru JSON/Protobuf API with J2534-2 extended API support lets existing scan tools and ECU flashers reach the vehicle through the same gateway — no dedicated J2534 dongle needed.
Circuit-breaker resilience
After repeated communication failures with a specific ECU, that stack backs off automatically and reports its health status to the cloud controller. Other stacks on the same gateway continue running normally — no retry storm, no cascading failure.
Identification + OE stack push
The right stack for every vehicle, automatically.
Before any polling begins, the runtime identifies the vehicle and selects the appropriate OE stack to load. The pushed OE stack standardises the data — naming, format, units, and frequency — across OE DTCs, dashboard telemetry, and other OE data surfaces.
VIN identification
The VIN is read from the vehicle during connection. The runtime maps it to the corresponding OE stack definition and loads it before the first polling cycle begins. No manual configuration at the vehicle level.
Identification logic
When VIN alone is insufficient, custom identification logic — declared in the stack configuration — handles edge cases: vehicle variants, retrofits, or mixed-fleet configurations. The result is always a stack selection.
Per-vehicle config
Identification can also be driven by a per-vehicle configuration pushed from the cloud. This supports fleets where VIN-based resolution is either unavailable or overridden by operator assignment.
This identification process is autonomous — the device resolves the correct stack locally from the identification result, with no cloud round-trip required for routine operation. The cloud pushes the OE stack definition once; the device applies it on every connection.
Customers can extend the stack catalogue with their own stacks — bought, IP-owned, or reverse-engineered — using their own identification logic. Custom stacks are scoped to their fleet only; they coexist alongside the default OE stacks without conflict.
For cloud-driven remote diagnostics — where a human or AI is the master rather than the device — see our engineering team about the remote diagnostics surface.
Where this fits
One pillar of the operations layer.
Remote diagnostics is one of five pillars in the operations layer — together with observability, safety, OTA, and lifecycle. Read the full set at /platform/operations.
ECU simulation
Hardware-free testing on a virtual CAN bus.
The MOS4 ECU simulator runs one or more virtual ECUs on a software CAN interface or DoIP and responds to UDS, OBD-II, and ISO-TP with configurable signal strategies: constant, ramp, sine, random, or lookup table. CI pipelines run full UDS/OBD-II regression suites with no physical bench.
The standalone CLI validates and loads stacks and provides an interactive REPL with no MOS4 runtime dependency. Ideal for integrating stack validation into your own CI pipeline.
Language support
Six languages for stack extensions.
Customers can extend stack behaviour — custom identification logic, signal transforms, and action handlers — using any of six languages. Lua 5.4 is the lowest-floor scripting option: no Rust toolchain required.
Rust
Native micro service authoring. Full type safety, zero-copy EventBus integration.
Python
Scripting and data-science workloads. Pytest-based stack validation CI runs without hardware.
C / C++
Existing embedded codebases and vendor SDKs. Container isolation applies the same resource budget.
Go
Network-facing micro services and gateway adapters. Standard OCI container delivery.
JavaScript / TypeScript
UI micro services, web-based dashboards, and MEP action scripting.
Lua 5.4
Lowest-floor scripting option. Extend stack behaviour without a Rust toolchain. Hot-reloadable action handlers via the config service.
FAQ
Frequently asked questions
-
Is Multi Stacks the same as OBDStacks?
Yes. OBDStacks is the legacy name; Multi Stacks is the canonical name as of 2026-05-05. The same engine, the same JSON stack format, the same default-stacks/ catalogue.
-
How many protocols does the runtime support simultaneously?
Up to 16 protocol families share one unified protocol API. Multiple stacks run concurrently with independent lifecycles and export paths on the same physical interfaces.
-
What is a declarative stack?
A stack is a JSON data file specifying ECUs (Electronic Control Units), Commands, Actions, Exports, Conditions, and Expressions. Updating a PID (parameter ID), changing a sampling period, or adjusting a response policy is a JSON edit and a commit — not a firmware release.
-
Does Modbus ASCII work in production?
Modbus ASCII is an experimental feature — do not rely on it for production deployments. Modbus RTU (RS-485) and Modbus TCP/MBAP are both production-ready.
-
Can I test a stack without real ECU hardware?
Yes. The MOS4 ECU simulator runs one or more virtual ECUs on a software CAN interface and responds to UDS, OBD-II, and ISO-TP with configurable signal strategies. CI pipelines run full regression suites with no physical bench.
-
What does DTC support cover?
DTC reading and clearing across the supported stack — via OBD-II service 03/04 or UDS diagnostic information services.
-
How does Multi Stacks compose with MSP and MEP?
A Multi Stacks "Strategy" is periodic out of the box. For signal-driven sequences, an MSP graph publishes a trigger and Multi Stacks sends the corresponding request. For event-driven sequences, an MEP rule fires on a named event and runs a Multi Stacks action. Both compositions stay declarative.
Bring your protocol matrix.
List the asset families you ship; we'll match them to the right stacks and show the concurrent-lifecycle setup on a development device.