The I2C EEPROM Programmer is a firmware target with one job: writing personality modules. Every creature has a small I2C EEPROM (a 24C256) sitting in a DIP8 socket on its controller board, and that chip is what tells the controller firmware who the creature is when it boots — its name, serial number, USB identity, and logging level. Think of it as a name tag that also carries configuration.
The programmer itself is refreshingly simple. I flash this target onto a board, plug it into my Mac, and it shows up as a plain USB serial port. A little shell runs on the board itself, so there’s no software to install on the computer — any serial terminal will do. Usually I drive it from my Creature Configurator, a small SwiftUI app that builds the binary blob and can hand it straight to the programmer.
Hardware
| I2C bus | i2c1 |
| SDA / SCL | GPIO 2 / GPIO 3 |
| Bus speed | 100 kHz |
| EEPROM address | 0x50 (standard for 24-series parts) |
| Page size | 64 bytes |
Three status LEDs show what’s happening: one for “USB connected,” one that flashes on data received, and one on data transmitted. The board enumerates with VID 0x0666, PID 0x0010, and the product string “i2c EEPROM Programmer.”
Connecting
screen /dev/tty.usbmodem* 115200
Type H and Enter for the command list. It’s a CDC serial device, so it ignores the host baud rate — any speed works, and it’ll run on just about any modern computer without drivers.
Commands
| Command | What it does |
|---|---|
H |
Help — lists commands, free heap, firmware version |
I |
System info as JSON (version, free heap, uptime) |
L<size> |
Load <size> bytes of binary data (no space after the L) |
B |
Burn the loaded data to the EEPROM |
V |
Verify the EEPROM against the loaded data |
P |
Print the loaded data as a hex dump |
R |
Reboot the programmer |
ESC |
Reset all buffers and return to idle |
A programming session
The flow is load → (optionally preview) → burn → verify. You send L with a byte count, the programmer answers GO_AHEAD, and you stream the raw bytes; it replies OK when it has them all. Scripted end to end, it looks like this:
-> L256
<- GO_AHEAD
-> <256 raw bytes>
<- OK
-> B
<- OK
-> V
<- OK Data verified successfully!
Burns happen in 64-byte pages with a short delay between them for the EEPROM’s internal write cycle. At 100 kHz that’s roughly 4 KB/s — but a typical personality module is well under 100 bytes, so in practice it’s instant.
The data format
Personality modules use a compact binary format. Multi-byte integers are big-endian, and strings are length-prefixed (one length byte, then ASCII, no null terminator). It opens with the magic bytes HOP! — a small rabbit-flavored touch — followed by the USB VID and PID, a version field, a logging level, and then the length-prefixed serial number, product name, and manufacturer name:
48 4F 50 21 HOP! magic
06 66 VID = 0x0666
00 20 PID = 0x0020
01 00 Version 1.0 (BCD)
03 Logging level = 3 (INFO)
06 43 43 30 30 30 31 Serial: "CC0001"
0A 52 65 64 20 44 72 61 67 6F 6E Product: "Red Dragon"
03 41 43 57 Manufacturer: "ACW"
FF No custom strings
If something goes wrong
If a transfer gets garbled, send ESC (0x1B) to reset the buffers and start over, or R to reboot the board. Worst case, unplug it and plug it back in — there’s nothing on the programmer that can’t be recovered by starting fresh.