OpenCarMind is an open-source OBD2 logger and dashboard for the ESP32, with TinyML on the board. It turns an ESP32-S3 board and a Bluetooth OBD-II adapter into a car data logger that runs a neural network on the chip. It needs no phone, no account and no internet connection. Everything it records goes to an SD card in the car and, if you pair the board, to the desktop app on your own computer.
It ships two on-board models, and you choose between them in the desktop app. CarMind E.V is fixed and tested. CarMind R.E.V is my first model that keeps learning in your car: the board itself updates part of the model from what your car reports while you drive, with no computer or cloud in the loop.
Version 2.5.1. Licence: MIT. Website: https://alveeerahman.github.io/OpenCarMind/. Downloads for the firmware and the desktop app are on the Releases page.
- Choose your model
- What you get
- Local-first
- How it works
- The screens
- Dashboard
- Desktop app
- Hardware
- Build and flash
- The models
- Status
- Next board: ESP-Mosaico with the ESP32-S31
- Limitations and future work
- Planned
- References
- Repository layout
- Credits and licence
| CarMind E.V | CarMind R.E.V | |
|---|---|---|
| Status | released in 2.4.0, the default | new in 2.5.0, improved in 2.5.1 |
| Size on the chip | about 89 KB | about 66 KB |
| Parameters | 38,679 | 19,815 |
| Time per run on the ESP32-S3 | about 3.5 ms in the car, 2.9 ms on the bench | about 2.9 ms in the car (median), 2.2 ms on the bench |
| Learns in your car | the model is fixed. The tank size and fill-up calibration learn with either model | its fuel estimate and an airflow constant that also gives the engine size, so its torque estimate learns your engine too. Plus the tank size and fill-up calibration, like E.V |
| Steady cruising with your foot on the gas | can be shown as braking | corrected in 2.5.1 |
| Fill-up calibration | yes | yes |
| Best for | a fixed, tested model | older cars, roughly pre-2020, that have no fuel-rate PID and can lose the airflow (MAF) reading |
From E.V to R.E.V in seven steps, each with the number it moved. How each was measured is in the detailed README.
Many older cars do not report their fuel rate, so the board works it out from the airflow sensor. When that reading is missing, a fixed model can only offer its factory estimate. R.E.V learns your car while a fuel-rate or airflow reading is there and uses what it learned when it is not.
To switch, connect the board to the desktop app with its USB cable while the car is parked, open the Board page and pick the model under "On-board model". The board keeps the choice after a restart, and what R.E.V learned stays on the SD card while E.V runs. More in What R.E.V learns.
Renders, not photos, with example values. Left: page 5 with E.V. Middle: page 5 with R.E.V, which adds a third job, "Learning". Right: the model selector on the app's Board page and R.E.V's learning status on its Fuel page.
- Every standard Mode 01 field the car reports (70 are decoded), battery voltage, the VIN, and stored, pending and permanent trouble codes. One CSV row per poll cycle.
- A small neural network on the chip. It classifies the last 5 seconds of driving as stopped, braking, accelerating or cruising, and estimates three values the OBD port does not report: brake pressure, flywheel torque and fuel rate.
- A save to the SD card every 10 seconds and a power-off line on page 3: green "Safe to turn off" once everything is on the card, red with the reason otherwise. The drive ends by itself 15 seconds after the engine computer stops answering.
- Fuel economy in km/L, or mpg in the US and the UK, from the best source your car has. A car that gives nothing shows "--", never a made-up number.
- Units that follow you. The first time the board joins Wi-Fi it reads the country of the access point and switches to miles and mpg in the US or the UK.
- Fill-up calibration. Enter fill-ups in the desktop app, and the board corrects its fuel figures from what you actually pumped.
- A fault engine that ranks trouble codes by severity and groups codes that share a cause.
- Dark and light screen themes, by time of day or fixed.
- A soft real-time scheduler that times every job against its deadline and restarts the board if a step hangs.
- Optional Wi-Fi, for the clock only: one HTTPS request to one time server you can choose.
- A desktop app for Windows, macOS and Linux that imports drives, charts them, shows fuel per drive, switches the model and takes your fill-ups.
Most car loggers stream your driving to a server and do the thinking there. OpenCarMind does the thinking on the board, the way TinyML work runs networks on microcontrollers[1]–[3]. R.E.V's learning also happens on the board, and what it learns stays on your SD card. Your data is plain CSV, JSON and text on a card you hold.
Wi-Fi is optional and does one thing: while the car is parked on a known network, the board asks one HTTPS time server for the time. It checks the server's certificate, sends nothing else, and listens on no port. You can compile Wi-Fi out with OCM_WIFI_CLOCK_SYNC.
The board asks the engine computer for every field it supports, shows the live values on its screen and writes each drive to the SD card as its own session. At home, the desktop app imports new drives over the USB cable, or you take the card out.
When an OBD row arrives, the board cuts the last 5 seconds into five one-second bins and runs the model in its own task, so logging never waits for it. Every row adds a line to the drive's .ml.csv file, and the newest answer shows on page 5. With R.E.V, the same run also feeds its learner.
Every periodic job has a period and a deadline. The scheduler in main/sched_mon.c times each run and, once a minute, checks the load against the rate-monotonic bound[4] and each job's worst-case response time[5]. A step that never returns resets the board, and after three bad restarts in a row it starts in safe mode with logging still on. R.E.V's learning runs at the lowest priority on a small budget of about 2 % of one core, so it never holds up the OBD poll or the model.
Pages 1, 3, 4 and 5 in the dark and light themes (renders, not photos). Page 2 is plain text and has its own picture at docs/images/details.png.
The dark theme is pure black, because on an LCD any tint in the background glows through the backlight. Text and colours are bright enough to read at a glance, and every status colour comes with a word.
Tap BOOT to change page. Hold it for 2 seconds to power off. The screen says "Turning off" while it saves and closes the drive, then "Off, safe to unplug", and the backlight and LED go dark. Hold it for 8 seconds to forget the saved adapter.
- Dashboard: RPM, speed, engine temperature, battery, fuel economy and fuel level, with a CPU bar and an estimated power bar.
- Details: adapter, VIN, protocol, trouble codes, clock, SD state and fuel used.
- Internals: twelve extra fields, up to eight custom PIDs, and recent events, including "Safe to turn off".
- Faults: ranked trouble codes with severity, description and likely cause, plus the connection and MIL status line.
- System: instructions per second on each core (hardware counters), PSRAM in use, chip temperature, uptime, the jobs and their deadlines, what the model sees and its three estimates.
The screen is light from 06:00 to 17:00 and dark otherwise. theme_mode, the hours and the time zone are set in config.json (see docs/CONFIGURATION.md).
tools/ocm_report.py turns a session folder from the SD card into one HTML report per drive, and Dashy shows them on your own computer or home server. Nothing is sent to the internet.
python3 tools/ocm_report.py /Volumes/<card name>/obd dashboard/site
cd dashboard && docker compose up -d # Dashy on http://localhost:4000The app in desktop/ runs on Windows, macOS and Linux. Over the board's USB cable it pairs with the board (you type a code from the board's screen once), sets Wi-Fi, time zone, time server and settings, imports drives and flashes new firmware, so the SD card never has to come out. It also switches the on-board model and keeps your fill-up log. docs/DASHBOARD.md has the setup.
- Board: Waveshare ESP32-S3-LCD-1.47B (the pin map in
main/board.hmatches it). - Adapter: a Bluetooth LE adapter that speaks ELM327 commands (see below). Classic Bluetooth adapters will not work, because the S3 has no classic radio.
- A FAT-formatted SD card.
Only the LELink has been driven with this board. The other rows come from vendor documents and other open-source OBD projects. docs/ADAPTERS.md has prices and sources. The Amazon links are plain product pages with no affiliate tag, all opened on 3 November 2026.
| Adapter | Bluetooth | Built-in profile | Status | Amazon |
|---|---|---|---|---|
| LELink², LELink | LE | FFE0 / FFE1 | tested | B0755N61PW, B00QJRYMFC |
| Veepeak OBDCheck BLE+, BLE | LE | FFF0 / FFF1 / FFF2 | expected | B076XVQMVS, B073XKQQQW |
| OBDLink CX (sold as the Bimmercode edition, which is the only CX model) | LE 5.1, encrypted | FFF0 / FFF1 / FFF2, pairs on demand | expected | B08NFLL3NT |
| Carista OBD2, Carista EVO | LE | FFF0 / FFF1 / FFF2 | expected | B00YVHGTBM, B0CM9W4MCW |
| Vgate iCar Pro BLE 4.0 | LE | 18F0 / 2AF0 / 2AF1 | expected | B06XGB4873 |
| Vgate vLinker MC+ | LE and Classic | E7810A71 / BEF8D6C9 | expected | B088LW211V |
| Vgate vLinker FS, MS | LE and Classic | E7810A71 / BEF8D6C9 | switch to "BLE+BT" in the VgateFwUpdater app first | B0BZTXV6MZ, B0D6YQX4R8 |
| Panlong OBD2 Scanner (PL-B04) | LE, works with Car Scanner on iOS | generic fallback | expected for the "For iOS Android" option only. The "For Android Only" option is likely Classic. The page title does not name the model | B0C1GDJ9ZJ |
| DA200 (Bluetooth 5.4, "AI Repair Tips") | LE, PIC18F25K80 ELM327-type chip (maker's spec sheet) | generic fallback | expected. The page title does not name the model, check for DA200 on the box | B0FH2GJTP8 |
| ELM327 BLE clones (for example RODEDIO) | LE on iOS | FFF0 or FFE0 | expected for most. Buy one whose listing says Bluetooth LE for iOS | B0GWQKLTMR |
| Adapters running the Deep OBD firmware | LE | FFE0 / FFE1 / FFE2 | expected | not sold on Amazon. Layout from the Deep OBD source, BtLeGattSppAndroid.cs |
| FIXD | Bluetooth | unknown | unverified | B013RIQMEO |
| BlueDriver | Bluetooth | private protocol | not expected | B00652G4TS |
| V800 full-system scanners (Panlong PL-V800 and white-label copies) | Bluetooth | private app (INNOOBD+) | not expected. The title says "Engine Transmission ABS SRS", not V800 | B0GQ3D57PK |
| OBDLink MX+, OBDLink LX, KONNWEI KW902 | Classic only (LX: Bluetooth 3.0, KW902: Android and Windows only) | none | will not work. The KW902 listing showed "currently unavailable" on 3 Nov 2026 | B07JFRFJG6, B00H9S71LW, B073F57QT3 |
You need ESP-IDF v6.1 and Python 3.
. $IDF_PATH/export.sh
idf.py set-target esp32s3
idf.py build
idf.py -p /dev/cu.usbmodemXXXX flash monitorThe first build downloads ESP-DL, LVGL, led_strip and cJSON through the IDF component manager. If you built an earlier version in the same folder, delete sdkconfig first. If the board stays in download mode after flashing, reset it with python -m esptool --chip esp32s3 -p <port> --after watchdog-reset chip-id.
On first boot the board connects to the strongest nearby adapter that looks like an OBD adapter and remembers it. If yours is not found, add its name to OBD_BLE_NAME_PREFIXES in idf.py menuconfig under OBD Logger Configuration. Settings, SD card files, the Wi-Fi list and the CSV columns are in docs/CONFIGURATION.md.
Both models are small quantised 1-D convolutional networks run by ESP-DL[6]: 8-bit weights and 16-bit activations, which makes them small and fast enough for a microcontroller. With every OBD row the selected model classifies the last 5 seconds and estimates brake pressure, flywheel torque and fuel rate. They were trained on public data, the HCRL Driving Dataset[7] and the Vehicle Energy Dataset[8], plus drives from my own car. Scores on held-out drives and unseen cars are in the detailed README.
The model card puts every step from E.V to the final R.E.V in one table: size, speed, classifier and fuel-learner scores, before and after, with a key to each metric and where it was measured.
Two additional drives serve as a held-out test set. They cover frequent stops, an extended idle, steady cruising with and without cruise control, and hard acceleration and braking. No model on main saw these drives during training or tuning. They are scored as data from an unseen vehicle: no per-vehicle settings carry over, and every learner starts from its factory state.
Method and a comparison with routine driving: docs/TEST_DRIVES.md.
R.E.V's learners have been tested in replay on recorded drives from public test cars, through the firmware's own code with faulty readings mixed in, and on two drives in my car. On cars it never saw, learning in the car made its fuel estimate clearly better than the factory one when the airflow reading was missing. The tables are in the detailed README.
E.V is trained so that the version on the chip scores like the one on a computer. R.E.V starts from E.V's design, is pruned to about half the parameters, and learns from E.V as its teacher on a wider set of public drives. The training code and data stay in a private repository. The shipped files are in ml/onboard and ml/onboard/rev, and docs/MODEL.md lists their inputs and outputs.
The network itself never changes in the car, which protects it from forgetting what it learned in training[9]. Small parts on top of it learn, each only from something the car gives it to check against.
| Output | In the car | Learns from |
|---|---|---|
| Fuel rate | learns | the car's own fuel rate, or fuel worked out from the airflow sensor |
| Airflow constant (also the engine size) | learns | airflow, engine load and rpm while the airflow sensor works |
| Fill-up calibration, both models | learns | the litres you pump, entered in the app |
| Fuel tank size, both models | learns | the fuel used against the drop in the fuel level |
| Engine torque | learns, through the engine size | the energy in the fuel minus friction, using the engine size R.E.V learns. Planned: a learned torque read-out on cars that report torque (see Limitations) |
| Manoeuvre class | fixed | nothing yet: the car gives no label for it |
| Brake pressure | fixed | nothing: no car reports it |
The last layer, which turns the network's features into a fuel rate, is refit from your car's own readings. A refit goes live only if it beats the current one on recent driving, and the board goes back to the factory version if learning ever makes things worse. Since 2.5.1 the learner ignores implausible readings and gives less weight to unusual ones[10], so a faulty sensor cannot spoil what it learned. When the car reports neither fuel rate nor airflow, the board uses whichever estimate has been most accurate on your car so far and shows it in amber. What R.E.V learned is saved to the SD card while you drive.
On R.E.V's first drives in my car, the screen said "braking" while I held a steady speed with my foot on the gas. E.V does the same. I opened up R.E.V with explainable-AI methods, Grad-CAM[11] and a probe on its internal features, and found that the network's features already told cruising from braking. Only the last layer that picks the class had it wrong. Correcting that one layer fixed it, and everything R.E.V had already learned in the car stayed valid.
Each dot is a 5-second window of my driving, placed by what the network sees. The points are the same on both sides. Only the layer that reads them changed. The Grad-CAM maps and the scores are in the detailed README.
- 2.5.0. R.E.V ships: half of E.V's size, and it learns your car's fuel use, airflow and tank in the car.
- First drives. The learners worked, and the classifier called steady cruising "braking".
- Explainable AI showed the error was in the classifier's last layer, not in what the network sees.
- A corrected last layer fixed it without touching what R.E.V had learned.
- Fault tests showed that a few bad sensor readings could stop the fuel learner.
- A fuel learner that shrugs off bad readings, checked on public cars and against two published meta-learning methods. It came out ahead, so it ships in 2.5.1.
The numbers for each step are in the detailed README.
Checked on the bench or in code: the build, the host unit tests (including R.E.V's learners with faulty readings), the switch to miles and mpg, the 10-second save, "Safe to turn off" and the time server rules. CI builds the desktop app on Windows, macOS and Linux. I have driven the board with the drive-end, power-off, fuel economy and units features, and R.E.V on two drives in my car.
Not checked yet: R.E.V over many drives and other cars, the corrected R.E.V on the road, the HTTPS clock on the board, other boards, and any adapter other than the LELink. The checks that need a car are in docs/FIELD_CHECKS.md, with tables for you to fill in. Known security limits are in SECURITY.md.
The next board is Espressif's ESP-Mosaico[12], built on the ESP32-S31, with a 480 × 480 touch screen, Bluetooth Classic, a motion sensor and a small battery. I have one on order, and the concept above (a render with example values) shows CarMind R.E.V on it. The full board comparison is in the detailed README.
The two chips do not share machine code, but OpenCarMind builds for both from the same source through ESP-IDF. A test build for the S31 compiled with only the board's pins and one wake-up call changed. The models need to be exported again for the S31, with the same trained weights.
The Mosaico's accelerometer can label braking and acceleration in the car, so the manoeuvre classifier could finally learn there, not only the fuel estimate. Its larger memory can keep past drives so new ones do not erase what the model knew, and its battery lets an update finish after the ignition is off.
OpenCarMind will ship a release for the ESP-Mosaico that runs CarMind R.E.V only, from the same source tree as the S3 version. Work starts when my board arrives.
- Torque is a physics estimate, not a measurement, checked against one public car and for petrol engines only. R.E.V learns the engine size it uses, about 2.1 L from my car's last two drives, but not the torque itself. Cars that report torque (PIDs 0x62 and 0x63) could give a learned torque read-out its label, the way the fuel read-out learns now, and then the physics would not be needed. That is planned, not built or tested. My Sentra and most pre-2020 cars report no torque, so there is no label, and physics, or the ESP-Mosaico's accelerometer as a label, stays needed there.
- The classifier and brake pressure do not learn in the car yet. Accelerometer labels on the ESP-Mosaico and a self-check that forecasts the next moment are the routes being worked on.
- No Wi-Fi upload yet. It is being reworked so a lost packet on weak home Wi-Fi does not stall every file, as in QUIC[13]. Until then drives come over the USB cable.
- The clock is only as exact as one second, and it needs a known Wi-Fi network within reach.
- The ESP-Mosaico port, and a build without a display.
- Code-signed installers.
- A test drive of the corrected R.E.V, and learning each car's closed-throttle position on the board.
- A manoeuvre forecast as a self-check, so the classifier can learn in the car.
- More regularised models, to fit more cars.
[1] J. Lin, W.-M. Chen, Y. Lin, J. Cohn, C. Gan and S. Han, "MCUNet: Tiny deep learning on IoT devices," in NeurIPS, 2020. arXiv:2007.10319
[2] R. David et al., "TensorFlow Lite Micro: Embedded machine learning on TinyML systems," in Proc. MLSys, 2021. arXiv:2010.08678
[3] C. Banbury et al., "MLPerf Tiny benchmark," in Proc. NeurIPS Datasets and Benchmarks Track, 2021. arXiv:2106.07597
[4] C. L. Liu and J. W. Layland, "Scheduling algorithms for multiprogramming in a hard-real-time environment," Journal of the ACM, vol. 20, no. 1, pp. 46–61, 1973. doi:10.1145/321738.321743
[5] M. Joseph and P. Pandya, "Finding response times in a real-time system," The Computer Journal, vol. 29, no. 5, pp. 390–395, 1986. doi:10.1093/comjnl/29.5.390
[6] Espressif Systems, "ESP-DL: Espressif deep-learning library for AIoT applications." github.com/espressif/esp-dl
[7] B. I. Kwak, J. Woo and H. K. Kim, "Know your master: Driver profiling-based anti-theft method," in Proc. PST, 2016, pp. 211–218. arXiv:1704.05223
[8] G. Oh, D. J. LeBlanc and H. Peng, "Vehicle Energy Dataset (VED), a large-scale dataset for vehicle energy consumption research," IEEE Transactions on Intelligent Transportation Systems, 2020. arXiv:1905.02081, data and licence (Apache-2.0) at github.com/gsoh/VED
[9] J. Kirkpatrick et al., "Overcoming catastrophic forgetting in neural networks," PNAS, vol. 114, no. 13, pp. 3521–3526, 2017. arXiv:1612.00796
[10] B. Kovačević, Z. Banjac and I. Kostić Kovačević, "Robust adaptive filtering using recursive weighted least squares with combined scale and variable forgetting factors," EURASIP Journal on Advances in Signal Processing, 2016. doi:10.1186/s13634-016-0341-3
[11] R. R. Selvaraju, M. Cogswell, A. Das, R. Vedantam, D. Parikh and D. Batra, "Grad-CAM: Visual explanations from deep networks via gradient-based localization," in Proc. ICCV, 2017. arXiv:1610.02391
[12] Espressif Systems, "ESP-Mosaico," esp-dev-kits documentation. docs.espressif.com
[13] J. Iyengar and M. Thomson, Eds., "QUIC: A UDP-based multiplexed and secure transport," RFC 9000, IETF, 2021. rfc-editor.org
| Path | What it holds |
|---|---|
main/ |
firmware source |
ml/onboard/ |
the shipped models (CarMind E.V, and CarMind R.E.V in rev/) |
samples/replay/ |
two replay sessions from a public dataset, in the board's file format |
schema/ |
JSON schema of the session files |
tests/host/ |
unit tests that run on a computer, including R.E.V's learners |
tools/ |
ocm_report.py and the model header generator |
desktop/ |
the desktop app (Rust and Tauri) |
dashboard/ |
Docker Compose file and Dashy configuration |
docs/ |
configuration, model notes, field checks, screen designs and figures |
OpenCarMind is released under the MIT licence (see LICENSE). ESP-IDF, ESP-DL, LVGL and NimBLE keep their own licences. The training data of the shipped models is not part of this repository. The replay samples also use the carOBD dataset, https://github.com/eron93br/carOBD. See samples/replay/README.md for its terms.


























