EmbedLink Secure Bootloader Application for Wireless Firmware Update
The ELP Bootloader enables firmware updates over the ELP communication protocol. It operates as an ELPX application (PID=0xFA) that manages flash memory operations on embedded nodes.
| Name | Value | Description |
|---|---|---|
ELPX_BOOTLOADER_PID |
0xFA |
Protocol ID |
ELPX_BOOTLOADER_VERSION |
0xFF01 |
Bootloader version |
ELPX_BOOTLOADER_KEY |
0xFF0011FF |
Security key for BOOT command |
ELPX_BIN_PART_SIZE |
1024 |
Part size (bytes) |
ELPX_BIN_SUBPART_SIZE |
16 |
Subpart size (bytes) |
ELPX_BIN_MAX_SUBPART_IDX |
64 |
Max subparts per part (1024/16) |
Firmware is transferred in a hierarchical structure:
Firmware Binary
├── Part 0 (1024 bytes)
│ ├── Subpart 0 (16 bytes)
│ ├── Subpart 1 (16 bytes)
│ ├── ...
│ └── Subpart 63 (16 bytes)
├── Part 1 (1024 bytes)
│ ├── Subpart 0 (16 bytes)
│ └── ...
└── Part N (≤1024 bytes)
└── ...
- Part: 1024-byte block written to flash at once
- Subpart: 16-byte chunk transferred in single ELP packet (payload limit is 26 bytes)
- Transfer: 64 PARTLOAD commands fill one part buffer, then PROGRAM writes to flash
┌─────────┐ ┌─────────┐ ┌─────────────┐ ┌─────────┐ ┌──────────┐
│ BOOT │───►│ ERASE │───►│ PROGRAM │───►│ VERIFY │───►│ FIRMWARE │
│ (lock) │ │ (flash) │ │ (load+write)│ │ (CRC) │ │ (jump) │
└─────────┘ └─────────┘ └─────────────┘ └─────────┘ └──────────┘
| Phase | State Flow | Description |
|---|---|---|
| BOOT | READY → COMPLETED | Lock bootloader with firmware parameters |
| ERASE | READY → BUSY → COMPLETED | Erase flash sectors |
| PROGRAM | READY → BUSY → READY (repeat) | Load subparts and program parts |
| VERIFY | READY → COMPLETED | Verify CRC32 of written firmware |
| FIRMWARE | - | Jump to application (no return) |
Get current bootloader state.
Request: [PID][CMD]
Response: ELPX_BOOTLOADER_STATUS_O
Get bootloader information.
Request: [PID][CMD]
Response: ELPX_BOOTLOADER_INFO_O
typedef struct {
uint16_t b_version; // Bootloader version
uint32_t b_sector_size; // Flash sector size
uint32_t b_part_size; // Part size (1024)
uint32_t b_fws; // Firmware area start address
uint32_t b_fwl; // Firmware area max length
} ELPX_BOOTLOADER_INFO_O; // 18 bytesLock bootloader and set firmware parameters.
Request: [PID][CMD][ELPX_BOOTLOADER_BOOT_I]
Response: ELPX_BOOTLOADER_STATUS_O
typedef struct {
uint32_t key; // Must be ELPX_BOOTLOADER_KEY
uint32_t fwid; // Firmware ID
uint32_t fws; // Firmware start address
uint32_t fwl; // Firmware length (bytes)
uint32_t fwcrc; // Firmware CRC32
} ELPX_BOOTLOADER_BOOT_I; // 20 bytesErase flash firmware area. Returns progress callbacks during operation.
Request: [PID][CMD]
Response: Multiple ELPX_BOOTLOADER_STATUS_O (progress + completion)
Load 16-byte subpart into part buffer.
Request: [PID][CMD][ELPX_BOOTLOADER_PARTLOAD_I]
Response: ELPX_BOOTLOADER_PARTLOAD_O
typedef struct {
uint16_t part_idx; // Part index (0, 1, 2, ...)
uint16_t subpart_idx; // Subpart index (0-63)
uint8_t subpart[ELPX_BIN_SUBPART_SIZE]; // 16 bytes data
} ELPX_BOOTLOADER_PARTLOAD_I; // 20 bytes
typedef struct {
uint16_t phase;
uint16_t state;
uint32_t completed; // Bytes transferred so far
uint32_t total; // Total firmware length
uint16_t part_idx; // Expected next part
uint16_t subpart_idx; // Expected next subpart
} ELPX_BOOTLOADER_PARTLOAD_O; // 16 bytesWrite loaded part to flash.
Request: [PID][CMD]
Response: ELPX_BOOTLOADER_STATUS_O
Verify firmware CRC32.
Request: [PID][CMD]
Response: ELPX_BOOTLOADER_VERIFY_O
typedef struct {
uint32_t fwid; // Firmware ID from flash
uint32_t fws; // Firmware start from flash
uint32_t fwl; // Firmware length from flash
uint32_t fwcrc; // Firmware CRC from flash
uint8_t fwv; // Firmware valid (1=yes, 0=no)
} ELPX_BOOTLOADER_VERIFY_O; // 17 bytesJump to application. No response (node reboots).
Request: [PID][CMD]
Response: None
Reset bootloader state.
Request: [PID][CMD]
Response: None
MASTER NODE
│ │
│─── INFO ─────────────────────►│
│◄── INFO_O (b_fws, b_fwl) ─────│
│ │
│─── BOOT (key,fwid,fws,fwl,crc)│
│◄── STATUS_O (COMPLETED) ──────│
│ │
│─── ERASE ────────────────────►│
│◄── STATUS_O (progress) ───────│ (multiple)
│◄── STATUS_O (COMPLETED) ──────│
│ │
│ ┌─── For each part ───────┐ │
│ │ │ │
│ │ ┌─ For each subpart ──┐ │ │
│ │ │ │ │ │
│ │ │─ PARTLOAD (idx,data)│►│ │
│ │ │◄ PARTLOAD_O (sync) ─│ │ │
│ │ │ │ │ │
│ │ └─────────────────────┘ │ │
│ │ │ │
│ │─── PROGRAM ────────────►│ │
│ │◄── STATUS_O (ok) ───────│ │
│ │ │ │
│ └─────────────────────────┘ │
│ │
│─── VERIFY ───────────────────►│
│◄── VERIFY_O (fwv=1) ──────────│
│ │
│─── FIRMWARE ─────────────────►│
│ (node jumps to app) │
The PARTLOAD response includes expected indices for next transfer:
part_idx: Expected part indexsubpart_idx: Expected subpart index
Master must verify these match before continuing. If sync fails, master should retry or abort.
uint32_t crc32(const uint8_t *data, uint32_t len) {
uint32_t crc = 0xFFFFFFFF;
for (uint32_t i = 0; i < len; i++) {
crc ^= data[i];
for (uint8_t j = 0; j < 8; j++) {
if (crc & 1)
crc = (crc >> 1) ^ 0xEDB88320;
else
crc >>= 1;
}
}
return ~crc;
}# Flash firmware to all discovered nodes
python elpx_bootloader.py firmware.bin
# Flash to specific node
python elpx_bootloader.py firmware.bin --node 0x10
# Interactive mode
python elpx_bootloader.py- NACK: Transport layer retransmits automatically (up to 3 retries)
- Sync Fail: PARTLOAD response indices don't match - retry or abort
- CRC Mismatch: VERIFY returns
fwv=0- reflash required - Phase Error: Commands received in wrong phase return no response