# Firmware Update via the J2534 DLL

> **Library 2.0.0.213** · SAE J2534 v04.04 API

How to flash **ScanDoc device firmware** using the J2534 DLL, and how to build and run
the example program [`examples/ota_update.c`](../../examples/ota_update.c).

This updates the firmware of the ScanDoc device (the VCI adapter) itself — not the
vehicle ECU.

---

## What you need

* The J2534 DLL for your platform:
  `libj2534_v04_04.dylib` (macOS) · `libj2534_v04_04.so` (Linux) · `j2534sd_v04_04_*.dll` (Windows)
* The header `include/j2534_ota.h` (declares the OTA functions and return codes)
* A firmware file (`update.bin`) for your device — supplied by Quantex
* The device reachable over LAN, BLE, or USB

---

## The API

Two functions (declared in `j2534_ota.h`). `OtaUpdate` does the whole job — it reads the
file and streams it to the device; you only pass the file path and an optional progress
callback.

```c
// Progress callback: current = block just sent (1-based), total = total blocks.
typedef void (*ota_progress_cb_t)(uint32_t current, uint32_t total, void *user_data);

// Flash a firmware file. Returns 0 (OTA_RESULT_OK) on success, negative on error.
int32_t OtaUpdate(uint32_t device_id, const char *firmware_path,
                  ota_progress_cb_t progress_cb, void *user_data);

// Cancel an in-progress update (rarely needed — OtaUpdate cleans up on error itself).
int32_t OtaAbort(uint32_t device_id);
```

On Windows these exports are `__stdcall`. Resolve them like the rest of the J2534 API
(link the DLL, or `GetProcAddress`/`dlsym`) — see
[`docs/sdk/GETTING_STARTED.md`](GETTING_STARTED.md).

---

## How to flash, step by step

```c
uint32_t dev;

// 1. Open the device (LAN here; see "Connection string" below for BLE / USB)
if (PassThruOpen("sd0;n192.168.1.100", &dev) != STATUS_NOERROR) { /* handle */ }

// 2. (optional) read the current version
char fw[80], dll[80], api[80];
PassThruReadVersion(dev, fw, dll, api);

// 3. Flash. Blocks until done; calls on_progress after each block.
int32_t rc = OtaUpdate(dev, "update.bin", on_progress, NULL);

if (rc == OTA_RESULT_OK) {
    // Success: the device is rebooting. Close, wait, then reopen with retries.
    PassThruClose(dev);                 // safe even though the device is gone
    sleep_seconds(10);                  // give it time to reboot
    for (int i = 0; i < 6; i++) {
        if (PassThruOpen("sd0;n192.168.1.100", &dev) == STATUS_NOERROR) break;
        sleep_seconds(3);
    }
    PassThruReadVersion(dev, fw, dll, api);   // confirm the new version
    PassThruClose(dev);
} else {
    char err[256];
    PassThruGetLastError(err);          // human-readable reason
    PassThruClose(dev);                 // on error the device did NOT reboot
}
```

```c
void on_progress(uint32_t current, uint32_t total, void *user_data) {
    printf("\rflashing %u/%u (%u%%)", current, total, total ? current * 100 / total : 0);
    fflush(stdout);
}
```

### Things to know

* **The device reboots after a successful flash.** The connection drops; wait ~10 s and
  reopen, retrying a few times (it is briefly unreachable while it boots).
* **Reboot the device between two consecutive updates.** Right after an update the new
  firmware is not yet "committed", and a second update started without a reboot can fail
  at the start (`OTA_RESULT_ERR_COMM`). A power-cycle clears this.
* **A flash is safe to interrupt.** Firmware is written to a spare partition and only
  activated at the very end, so a failed or aborted update leaves the running firmware
  intact.

---

## Build and run the example

[`examples/ota_update.c`](../../examples/ota_update.c) is a complete program that does
everything above. The firmware argument is a local file **or** an `http(s)` URL (URLs
are fetched with `curl`).

**Build** (from the repository root):

```bash
# macOS: the dylib's load path is @executable_path, so the binary must sit next to the
# dylib — build straight into the folder that contains it (here: build/).
cc examples/ota_update.c -o build/ota_update -L build -lj2534_v04_04

# Linux:
cc examples/ota_update.c -o build/ota_update -L build -lj2534_v04_04 -Wl,-rpath,'$ORIGIN'
```

**Run** — `ota_update <connection> <firmware-file-or-url>`:

```bash
build/ota_update "sd0;n192.168.1.100"           ./update.bin            # LAN, local file
build/ota_update "sd0;bN49999"                  ./update.bin            # BLE
build/ota_update "sd0;c/dev/tty.usbserial-XXXX" https://.../update.bin  # USB, from URL
```

**Expected output:**

```text
opening "sd0;bN49999" ...
before: fw=FW:81;HW:0000FF05;SN:49999 dll=2.0.0.178
flashing ./update.bin ...
  OTA: 739/739 (100%)
OtaUpdate OK — device is rebooting...
after:  fw=FW:82;HW:0000FF05;SN:49999
done.
```

> macOS BLE: the terminal you run from needs Bluetooth permission
> (System Settings → Privacy & Security → Bluetooth).

---

## Android

The JNI layer exposes the same flow as `ptOtaUpdate(devId, firmwarePath,
callback)` / `ptOtaAbort(devId)` — signatures, result objects and the
progress listener are documented in [ANDROID.md](ANDROID.md#firmware-update-ota).
Return codes below apply unchanged.

## Connection string

`PassThruOpen` takes `sd0;<type><address>`:

| Transport | Format | Example |
| --- | --- | --- |
| LAN / Wi-Fi | `sd0;n<ip>` | `sd0;n192.168.1.100` |
| BLE | `sd0;b<name>` | `sd0;bN49999` |
| USB-UART | `sd0;c<port>` | `sd0;c/dev/tty.usbserial-DP04XHM7` |

---

## Return codes

`OtaUpdate` / `OtaAbort` return `0` on success or a negative `ota_result_t`
(`j2534_ota.h`). Call `PassThruGetLastError()` for a text description.

| Code | Name | Meaning |
| --- | --- | --- |
| `0` | `OTA_RESULT_OK` | success |
| `-500` | `ERR_FILE` | cannot open / read the firmware file |
| `-501` | `ERR_HEADER` | not a valid firmware file |
| `-502` | `ERR_COMM` | communication error (see "Reboot between updates") |
| `-503` | `ERR_BEGIN` | device rejected the start of the update |
| `-504` | `ERR_DATA` | device rejected a data block |
| `-505` | `ERR_END` | device rejected the end of the update |
| `-506` | `ERR_CRC` | too many CRC errors |
| `-507` | `ERR_TIMEOUT` | communication timeout |
| `-508` | `ERR_DEVICE` | invalid `device_id` (not opened) |
| `-509` | `ERR_ALLOC` | out of memory |

---

## Legacy devices (ScanDoc / Compact)

Older devices (serial starting with `SD` or `S`) use a different, lower-level path —
`StartBoot`, `BootAbout`, `BootWriteFlash` (declarations in `include/j2534_dll.hpp`).
Nano / Max devices (`N`, `C`, `M`) use the OTA API above. If you need to flash a legacy
device, contact Quantex for the procedure.
