Lab 1 — flashing & bring-up: from a sealed box of boards to a settling curve

Nine ESP32-S3 boards arrive with nothing on them. This sheet takes you from that to eight nodes regulating one shared bath and a gauge drawing the curve — without guessing. Read the build sheet first; this is the software half of the same job.

THE ONE RULE: The regulating scalar never becomes a packet. Flashing changes nothing about this — you load the same lonely firmware onto every node, and no board learns anything about any other.

1. What you need

QtyItemNotes
9ESP32-S3 N16R8 (DevKitC-1 layout)8 nodes + 1 gauge; the gauge is deliberately not one of the eight
1USB-C cable that carries dataa charge-only cable enumerates nothing; this is a common time-sink
jedas-side toolingesptool, mpremote, pyserial, matplotlib
pip install --user esptool mpremote pyserial matplotlib

2. Get MicroPython

Download page: https://micropython.org/download/ESP32_GENERIC_S3/

Take the "Support for Octal-SPIRAM" (SPIRAM_OCT) variant. The plain generic S3 build auto-detects quad SPIRAM but does not talk to octal PSRAM; the N16R8 boards have octal PSRAM and need the octal variant. Grabbing the wrong build is the single most common failure on this board — the board will flash and boot, then behave oddly or short on memory.

3. Flash one board

Find the serial device. This board's USB-serial bridge enumerates as CDC-ACM, so look for /dev/ttyACM*, not ttyUSB*:

ls /dev/ttyACM* /dev/ttyUSB*

Permissions, the durable way — add yourself to dialout, then log out and back in (a one-off chmod on the device is only a stopgap):

sudo usermod -aG dialout $USER

Erase, then write the octal build. The offset is 0 on the S3 — not 0x1000 as on the classic ESP32:

esptool --chip esp32s3 -p /dev/ttyACM0 erase-flash
esptool --chip esp32s3 -p /dev/ttyACM0 -b 921600 \
    write-flash -z 0 ESP32_GENERIC_S3-SPIRAM_OCT-<version>.bin

If the board will not enter the bootloader on its own: hold BOOT, tap RESET, release BOOT, then flash. These boards have two USB-C ports (one bridged UART, one native USB). If one port will not take the flash, try the other with the manual BOOT/RESET entry above — try, and if it does not go, try the other port.

Verify you have a live MicroPython:

mpremote connect /dev/ttyACM0

That should drop you at a REPL. Ctrl-D soft-resets the board; Ctrl-X exits mpremote.

4. Load the lab software

Every board runs one file, copied in as main.py. A node:

mpremote connect /dev/ttyACM0 fs cp firmware/node_lab1.py :main.py

The gauge:

mpremote connect /dev/ttyACM0 fs cp firmware/gauge.py :main.py

Every node gets the identical node_lab1.py — there is no node id, no address, nothing to configure per board. Label the boards 1–8 with tape for your own bookkeeping; the firmware neither knows nor needs to know which one it is, because noding tells no one anything. The one gauge board gets gauge.py instead.

5. Bench-test the first node before flashing the rest

This mirrors build sheet §5 — do it once, on one node, before you commit the whole batch.

6. Run and log

With the box closed and all nine boards powered, log the gauge from jedas. The logger auto-detects the port; --list shows candidates and --out names the file:

python3 tools/gauge_logger.py --out run1.csv

While it runs, type an event note and press Enter to mark it — e.g. killed node 3. The marker lands in the CSV with a timestamp and is drawn as a labelled vertical line on the plot. This is what makes the perturbation runs in build sheet §6 legible — the curve stops being a squiggle and starts being an argument.

Plot any time — it is safe to run against a log still being written, so check the shape at minute 5 rather than waiting out the whole run:

python3 tools/plot_settling.py run1.csv

Tools: gauge_logger.py · plot_settling.py

7. Honest expectations

None of these are bugs — do not let a class mistake the physics for a fault.

8. Pin reference

The S3 pin law, as established in the S3 migration. Lab 1 senses on a 1-Wire pin (GPIO 4) and drives its optional heater on GPIO 16; labs 2–3 read an analog rail on ADC1 (GPIO 5). Carried here so you flash against the right sheet:

SignalPinWhere
DS18B20 data (sense)GPIO 4lab 1
Load PWMGPIO 16lab 1 (only if HEATER = "pwm")
ADC1 senseGPIO 5labs 2–3

Reserved on the N16R8 — never wire to these: GPIO 26–32 are SPI flash and GPIO 33–37 are octal PSRAM; touching them crashes the cache. Safe GPIOs are 1–18, 21, 38–42, 47.