- TypeScript 78.4%
- C++ 11.4%
- CSS 8.8%
- JavaScript 0.8%
- HTML 0.3%
- Other 0.3%
Legal hygiene for the GPL-3.0 project and its bundled MIT component:
- Restore the MIT license notice (Travis Lesicka, "Copyright (c) 2017 Servant707")
to firmware/M93Cx6.{h,cpp} — it was missing, which the MIT terms require. Add
firmware/M93Cx6.LICENSE with the full text.
- Add a GPL-3.0 header to firmware.ino crediting the original roffe/eep and noting
the bundled MIT library.
- Add SPDX GPL-3.0-or-later + copyright headers to the first-party TypeScript
sources and stylesheet.
- Note the third-party MIT component in the README and firmware CHANGELOG.
Firmware recompiled: byte-identical to the embedded src/firmware.hex (comments
only). All quality gates green.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
||
|---|---|---|
| .gitea/workflows | ||
| deploy | ||
| firmware | ||
| public | ||
| src | ||
| .dockerignore | ||
| .gitignore | ||
| .prettierignore | ||
| .prettierrc.json | ||
| docker-compose.yml | ||
| Dockerfile | ||
| eslint.config.js | ||
| index.html | ||
| LICENSE.md | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| tsconfig.json | ||
| vitest.config.ts | ||
Saab CIM Tool
A browser-based tool to read, edit, validate and flash the EEPROM of a Saab CIM (Column Integration Module) using an Arduino as a hardware adapter.
Everything runs client-side in the browser — no installer, no native backend.
Serial access uses the Web Serial API;
the CIM data format, all checksums and the firmware flasher are implemented in
TypeScript. Opening and editing .bin files works in any browser; talking to the
adapter needs a Chromium-based one (see Browser support).
This is a browser rewrite of the original Go/Fyne desktop tool roffe/eep by roffe (see Credits). The complete CIM library was ported to TypeScript (
src/lib/cim.ts) and verified against real dumps, so the app has no dependency on any private package.
Features
EEPROM operations (Web Serial)
- Read / Write / Erase the CIM EEPROM with a live progress bar.
- Verify-after-write: the written data is read back and compared (MD5 + CRC32).
- Robust read mode: uses the firmware's
Rcommand to re-read unstable cells until two reads agree — fewer failed reads on aged EEPROMs. - In-browser firmware flasher (STK500v1 / optiboot) — flash the Arduino
adapter without
avrdude. Theavrdudecommand stays available as a fallback.
File handling
- Open and edit
.bindumps, multiple at once in file tabs. - Drag & drop one or more
.binfiles anywhere onto the window. - Save edited dumps (XOR-encoded, ready to flash).
- JSON export of the decoded contents as a report.
Inspection & editing
- Validation of every section against its CRC-16/MCRF4XX checksum, with the two data banks compared for consistency — a healthy dump is reported at a glance.
- Identity panel: VIN (with live ISO 3779 check-digit validation and model year), PIN, S/N sticker, steering-angle-sensor (SAS) toggle, ISK, PSK, part numbers, MD5/CRC32 fingerprint and programming dates.
- Key editor: list, add and delete keys with transponder-type detection.
- Virginize (unmarry): strip VIN, PIN, keys and fault codes so a CIM can be re-married to a car with Tech2.
- Colour-coded hex viewer with a region legend and hover tooltips mapping every byte to its field.
- Compare two open dumps byte-by-byte: differing bytes are highlighted and the affected fields are listed.
UI
- Dark / light theme.
- Multilingual (English / German), switchable in the header. Adding a language
is just another dictionary in
src/i18n.ts. - Installable PWA that works offline after the first load
(
public/sw.js).
Hardware & wiring
You need:
- an Arduino UNO (or Nano),
- a breadboard and jumper wires,
- a SOP8 test clip with a flat cable.
Wire the EEPROM to the Arduino as follows:
_____
Chip Select (cs) --|1 8|-- Vcc (pwr)
Serial Clock (sk) --|2 7|--
Data In (di) --|3 6|-- Org Select (org)
Data Out (do) --|4 5|-- Vss / GND
¯¯¯¯¯
Vcc (pwr) -> Pin 9 Data In (di) -> Pin 11
Vss (gnd) -> GND Data Out (do) -> Pin 12
Chip Select (cs) -> Pin 10 Org (org) -> Pin 8
Serial Clock (sk) -> Pin 7
The red wire of the SOP8 clip marks pin 1 and must point to the corner with the
small dot on the EEPROM. Full instructions are in the in-app help (? top-right).
Usage
- Flash the adapter firmware onto the Arduino (Sidebar → Flash adapter, or via
avrdude, see Firmware). - Wire the SOP8 clip to the CIM's EEPROM as above.
- Open the app, click Connect and pick the Arduino's serial port.
- Read the EEPROM, inspect/edit it, then Write it back.
To just inspect a dump, use Open (or drag a .bin in) — no hardware required.
Browser support
| Task | Requirement |
|---|---|
| Read / write / flash | Chrome, Edge or Opera on desktop (Web Serial) |
| Open / edit / save files | Any modern browser |
Firmware
The Arduino firmware lives in firmware/ and the compiled image is
embedded in the app at src/firmware.hex (current version
v2.1.0; see firmware/CHANGELOG.md).
Flash it from the app, or with avrdude:
avrdude -c arduino -P <port> -b 115200 -p atmega328p -D -U flash:w:firmware.hex:i
The v2.1.0 firmware keeps the field-proven EEPROM bit-bang timing byte-for-byte
and improves the serial layer (decoupled command buffer, safer parser, i
identify and R robust-read commands, smaller flash/RAM footprint).
Verified on real hardware: the STK500v1 flasher was checked against an Arduino Uno (ATmega328P, CH340) — sync, signature (
1E 95 0F), a full flash offirmware.hexand a byte-exact read-back. The flashed firmware boots and reportsv2.1.0. Only the Web Serial transport itself (vs. Nodeserialport) is untested; the bytes on the wire are identical.
Project structure
src/
main.ts App shell, state, all views wired together
i18n.ts Translations (en/de) + t()
dom.ts Tiny hyperscript helper + inline SVG icons
style.css Design system (theming, layout)
lib/
cim.ts CIM 512-byte parser, validation, editing, serialization
checksum.ts CRC-16/MCRF4XX, CRC-32, MD5
bytes.ts Hex / byte helpers
regions.ts Byte-range map for the hex viewer
serial.ts Web Serial adapter (read/write/erase protocol)
stk500.ts STK500v1 firmware flasher
intelhex.ts Intel HEX parser
views/ hex, diff, help, shared UI (toast/dialogs)
public/ manifest, icon, service worker
firmware/ Arduino firmware source + M93Cx6 library + CHANGELOG
Development
npm install
npm run dev # dev server on http://localhost:5173
npm run build # production build to dist/
| Script | Purpose |
|---|---|
npm run dev |
Vite dev server |
npm run build |
Type-check + production build |
npm test |
Run the Vitest suite |
npm run coverage |
Tests with coverage thresholds |
npm run typecheck |
tsc --noEmit |
npm run lint |
ESLint |
npm run format |
Prettier check (use format:fix to apply) |
The app has no runtime dependencies — everything is build-time tooling and the output is a static bundle.
Deployment (Docker)
A multi-stage image builds the static bundle and serves it with nginx:
docker compose up -d --build
This publishes the app on 127.0.0.1:8080 (override with the CIMTOOL_PORT
environment variable) so you can put it behind your own reverse proxy.
HTTPS is required. The Web Serial API only works in a secure context, so the app must be reached over HTTPS (or
http://localhostfor testing). Over plain HTTP, reading/writing/flashing is unavailable and only file inspection works — terminate TLS at your reverse proxy.
An example NGINX server block for the proxy host is in
deploy/reverse-proxy.example.conf; the
in-container nginx config is deploy/nginx.conf.
Quality & CI
- Unit tests (Vitest) cover the pure-logic core: CRC-16/CRC-32/MD5 against known test vectors, parse→serialize round-trip identity, XOR-encoding detection, validation, key editing and the Intel HEX parser. The parser was additionally checked against a real CIM dump (clean validation, byte-exact round-trip).
- Coverage thresholds are enforced on that core (
vitest.config.ts); the DOM UI and hardware I/O layers are intentionally excluded as they aren't meaningfully unit-testable. - ESLint + Prettier keep the code consistent.
- CI (
.gitea/workflows/ci.yaml) runs on every push and pull request: a static analysis job (format, lint, type check) and a tests & audit job (coverage +npm audit).
Credits
This project is a browser-based rewrite of roffe/eep
by roffe — the original author. The Arduino
firmware, the M93Cx6 EEPROM handling and the reverse-engineered CIM data format
all originate from that project; this repository ports the tooling to the web and
extends the firmware. Many thanks to roffe for the original work.
License
GNU GPL v3 — the same license as the original roffe/eep.
Third-party components
The bundled Arduino M93Cx6 EEPROM library
(firmware/M93Cx6.h,
firmware/M93Cx6.cpp) by Travis Lesicka is licensed
separately under the MIT License, reproduced in
firmware/M93Cx6.LICENSE.