can_bus_toolkit
A CAN bus toolkit for Flutter on Android. It connects to USB CAN adapters over USB OTG and supports monitoring and transmitting frames, candump logging (including from a foreground service), bus statistics, ISO-TP, OBD-II, UDS, and DBC signal decoding and encoding.
The diagnostic and DBC parts are pure Dart. They run on any CanBus implementation,
including the built-in virtual bus, so you can develop and test without hardware.
Features
The package has four libraries. Import package:can_bus_toolkit/can_bus_toolkit.dart
for all of them, or import just one module.
core.dart (pure Dart, no Flutter)
CanFramewith 11-bit and 29-bit IDs, RTR and error frames, and the candumpID#DATAformat.- A
CanBusinterface for sending, periodic sending, filters, state, statistics and closing. VirtualCanBusandVirtualCanNetworkfor tests and demos, with optional ACK and bitrate-mismatch simulation.- Statistics: frames per second, per-ID rates, and bus load with worst-case bit stuffing.
- A
candump -Llog reader and writer. CanLogReplayerwith speed control, looping and pause. By default it skips OBD/UDS diagnostic frames.
usb.dart (Android USB host)
- PEAK PCAN-USB, SLCAN (CANable and clones) and gs_usb (candleLight, experimental).
- Listen-only mode, hardware timestamps, error counters and bus state where the adapter supports them.
- Attach and detach events, the USB permission flow, and optional auto-reconnect.
- Native frame batching, so high-rate buses don't flood the platform channel.
- Background logging to a candump file from a
connectedDeviceforeground service. - Custom VID/PID registration for clone adapters.
obd.dart (pure Dart)
- ISO-TP (ISO 15765-2) with flow control, block size, STmin and padding.
- An OBD-II (SAE J1979) client:
- modes 01, 02, 03, 04, 07, 09 and 0A
- 11-bit and 29-bit addressing, and multi-ECU answers
- PID polling
- bitrate and addressing auto-detection that listens before it transmits
- A UDS (ISO 14229) client: session control, read/write DID, DTCs, security access, tester present, and "response pending" handling.
ObdEcuSimulatorthat answers OBD and UDS requests on any bus.- Every service that changes ECU state needs
allowWrites: true.
dbc.dart (pure Dart)
- A DBC parser that reports errors with line and column.
- Decode and encode for Intel and Motorola byte order, signed, float and double signals, value tables, and multiplexed messages.
- Comments, attributes and node definitions.
Install
flutter pub add can_bus_toolkit
Requirements: Android 7.0 (API 24) or later, a phone or tablet with USB host (OTG) support, and an OTG cable or adapter.
Quick start
Monitor a bus over USB
import 'package:can_bus_toolkit/can_bus_toolkit.dart';
Future<UsbCanBus?> startMonitor() async {
final devices = await CanUsb.listDevices();
if (devices.isEmpty) return null;
final device = devices.first;
if (!await CanUsb.requestPermission(device)) return null;
// Listen-only does not transmit or acknowledge anything. Use it first on a vehicle.
final bus = await CanUsb.open(
device,
config: const CanConfig(bitrate: CanBitrate.k500, listenOnly: true),
);
bus.frames.listen((frame) => print(frame.toCandumpString()));
bus.statistics.listen(
(s) => print('${s.framesPerSecond.round()} frames/s, '
'${s.busLoadPercent.toStringAsFixed(1)} % load'),
);
return bus; // Call bus.close() when done.
}
OBD-II live data
import 'package:can_bus_toolkit/can_bus_toolkit.dart';
Future<void> readVehicle(UsbCanDevice device) async {
// Probes 500 and 250 kbit/s listen-only first, then tries 11-bit and 29-bit.
final detection = await ObdClient.autoDetect(
(bitrate, {required listenOnly}) => CanUsb.open(
device,
config: CanConfig(bitrate: bitrate, listenOnly: listenOnly),
),
);
if (detection == null) return;
final obd = ObdClient(detection.bus, addressing: detection.addressing);
print('VIN: ${(await obd.readVin()).first}');
print('Stored DTCs: ${(await obd.readStoredDtcs()).first}');
// Engine speed, vehicle speed and coolant temperature every 500 ms.
obd.poll([0x0C, 0x0D, 0x05]).listen((sample) => print(sample.value));
}
Decode signals with a DBC file
import 'package:can_bus_toolkit/can_bus_toolkit.dart';
import 'package:flutter/services.dart' show rootBundle;
Future<void> decodeBus(CanBus bus) async {
final db = DbcDatabase.parse(await rootBundle.loadString('assets/vehicle.dbc'));
bus.frames.listen((frame) {
final message = db.decode(frame);
if (message == null) return;
print(message); // EngineData: EngineSpeed = 1726 rpm, CoolantTemp = 83 degC
final rpm = message['EngineSpeed']; // physical value as double, or null
});
}
Without hardware
final network = VirtualCanNetwork();
final ecu = ObdEcuSimulator(network.createBus())..start();
final obd = ObdClient(network.createBus());
print((await obd.readPid(0x0C)).first); // Engine speed in rpm
The example app has a demo vehicle mode that exercises every screen without an adapter.
Supported adapters
| Adapter | USB ID | Protocol | Status |
|---|---|---|---|
| PEAK PCAN-USB | 0C72:000C | PCAN-USB (vendor protocol) | Tested on a 250k bench (Android 16, firmware 2.8); vehicle testing pending |
| CANable, slcan firmware | 16D0:117E | SLCAN (Lawicel) over CDC-ACM | Experimental |
| STM32 virtual COM port SLCAN clones | 0483:5740 | SLCAN over CDC-ACM | Experimental |
| candleLight / CANable gs_usb firmware | 1D50:606F | gs_usb | Experimental |
| Other clones | any | SLCAN or gs_usb | Register with CanUsb.registerDevice |
PCAN-USB means the original PEAK PCAN-USB with USB ID 0C72:000C. PCAN-USB FD and PCAN-USB Pro use different protocols and are not supported yet.
Capability matrix
| Adapter | Listen-only | Loopback | Error counters | HW timestamps | Bus state |
|---|---|---|---|---|---|
| PCAN-USB | yes (firmware 4+) | no | yes | yes | yes |
| SLCAN | yes (L command) |
no | no | no | best effort (F command) |
| gs_usb | if the device reports it | if the device reports it | yes | if the device reports it | yes |
UsbCanBus.capabilities reports what the connected device actually supports.
CanUsb.open rejects options the adapter can't provide.
Platforms
| Platform | USB transport | core, obd, dbc |
|---|---|---|
| Android 7.0+ | yes | yes |
| iOS | no | yes, with your own CanBus |
| Web, desktop | no | yes, with your own CanBus |
iOS has no public API that lets apps talk to arbitrary USB devices. Accessories need
Apple's MFi program, and DriverKit is limited to iPadOS on M-series iPads, with
entitlements granted by Apple. For iOS, use a Bluetooth LE or Wi-Fi CAN adapter and
implement CanBus over its protocol (for example, SLCAN over a BLE serial service or
over TCP). The OBD-II, UDS, ISO-TP, DBC, logging and statistics code then works
unchanged. Built-in BLE/Wi-Fi transports are on the roadmap.
Wiring
-
A CAN bus needs at least two active nodes. Every frame must be acknowledged by another node. An adapter that transmits alone on a cable sees ACK errors and goes error-passive. On a bench, use a second adapter or an ECU. In listen-only mode the adapter never acknowledges, so it can't be the only other node.
-
Terminate both ends with 120 ohm between CAN_H and CAN_L. With power off, you should measure about 60 ohm between CAN_H and CAN_L. Vehicles are already terminated, so don't add a resistor at the OBD-II port.
-
OBD-II to DB9 (PCAN-USB and most DB9 adapters, CiA 303-1 pinout):
OBD-II (J1962) pin Signal DB9 pin 6 CAN_H 7 14 CAN_L 2 4 and 5 Chassis and signal ground 3 16 Battery +12 V do not connect Never connect OBD pin 16 to the adapter. Some adapters (PCAN-USB with its internal jumper set) put 5 V on DB9 pin 9, and battery voltage there can damage the adapter.
Step-by-step hardware tests are in docs/HARDWARE_TEST.md.
Safety
Transmitting on a vehicle network can change how ECUs behave, set fault codes, or disturb safety systems.
- Connect in listen-only mode first to confirm the bitrate. Transmitting at the wrong bitrate floods the bus with error frames.
- Clearing DTCs, UDS session changes, writing DIDs and security access are blocked
unless you pass
allowWrites: true.UdsClient.requestwithoutallowWritesonly sends read-only services. This guards against accidents. It is not access control. - Don't use the app while driving, and don't transmit on a moving vehicle.
- Logs can contain the VIN and other vehicle identifiers, which count as personal data in many jurisdictions (for example, under GDPR). Store and share them accordingly.
This software is provided under the MIT license without warranty. You are responsible for how you use it on real vehicles and equipment.
Android setup
The plugin declares android.hardware.usb.host as optional. To make your app
installable only on devices with USB host, declare it as required:
<uses-feature android:name="android.hardware.usb.host" android:required="true" />
CanUsb.requestPermission asks for USB permission at runtime. No manifest permission
is needed for USB access.
Background logging
CanUsb.startBackgroundLogging runs a foreground service of type connectedDevice.
Declare the service and permissions in your app's AndroidManifest.xml. If they are
missing, the call throws CanConfigException with this snippet:
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<application>
<service
android:name="com.ahmadhussain.can_bus_toolkit.logging.BackgroundLoggingService"
android:exported="false"
android:foregroundServiceType="connectedDevice" />
</application>
</manifest>
- Start logging while the app is in the foreground. Android 12 and later block foreground-service starts from the background.
- On Android 13 and later, request
POST_NOTIFICATIONSbefore starting. Without it, logging still runs, but the notification is hidden. - Apps on Google Play that use a foreground service type must declare it in the Play Console (App content > Foreground service permissions).
- Logs are written to app-internal storage (
CanUsb.logDirectory()). They are not readable by other apps.
Open the app when an adapter is plugged in (optional)
Add an intent filter to your activity, and a device filter in
android/app/src/main/res/xml/device_filter.xml:
<intent-filter>
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
</intent-filter>
<meta-data
android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
android:resource="@xml/device_filter" />
<resources>
<usb-device vendor-id="3186" product-id="12" /> <!-- PCAN-USB -->
<usb-device vendor-id="5840" product-id="4478" /> <!-- CANable slcan -->
<usb-device vendor-id="7504" product-id="24687" /> <!-- candleLight gs_usb -->
</resources>
If the user picks "always open" in the system dialog, Android also grants USB permission for that device.
Roadmap
- CAN FD (PCAN-USB FD, gs_usb FD)
- SAE J1939 (transport protocol, address claiming, PGN decoding)
- CANopen (NMT, SDO, PDO)
- iOS and other platforms through BLE and Wi-Fi adapters
Related
Android-CANBus-OTG-Debugger is a native Android CAN debugger app by the same author, and the origin of this package's PCAN-USB work.
License
MIT. See LICENSE.
Libraries
- can_bus_toolkit
- CAN bus toolkit for Flutter on Android: USB OTG transport, logging, statistics, ISO-TP, OBD-II, UDS and DBC decoding.
- core
- Core CAN bus types: frames, the
CanBusinterface, virtual buses, statistics, candump logging and log replay. - dbc
- DBC file parsing and CAN signal decoding/encoding. Pure Dart; works with frames
from any
CanBus. - obd
- ISO-TP (ISO 15765-2), OBD-II (SAE J1979) and UDS (ISO 14229) clients that run on
top of any
CanBus, plus a simulated ECU for tests and demos. - usb
- USB OTG transport for Android: PEAK PCAN-USB (primary), SLCAN/CANable and gs_usb/candleLight (experimental).