# Breadboard This project uses 3 74HC595 serial to parallel shift registers, a 7-segment display multi unit, an 8x8 LED matrix, an active buzzer, an NPN transistor, a joystick, a handful of resistors and a potentiometer to make a playable Snake game. # Lessons Learned * Compiling, uploading, and debugging Arduino code from the CLI * Managing mutexes in Arduino to handle thread locking * How to write data to a 74HC595 shift register without using the Arduino HAL * How to drive a 7-segment display * How to drive an 8x8 LED matrix * You don't need lots of pixels, or even colors, to make it fun ## Compiling, uploading, and debugging from the CLI The Arduino IDE is ... not great. Its editor is crummy; I edit my code in Emacs and just use the IDE to upload, monitor and debug it. And frankly its debugger is not that great - I find most GUI debuggers to be kind of clunky, and this one especially. It's great for finding and adding libraries and things like that, but I don't like being constrained inside of it. Luckily the IDE is mostly just calling `arduino-cli` behind the scenes so everything the IDE does, we can do from the CLI. Setting up the arduino-cli is reasonably simple, but it quickly falls apart when you want to start debugging. To set up the ESP32S3 dev board: ``` $ arduino-cli core update-index \ --additional-urls https://espressif.github.io/arduino-esp32/package_esp32_index.json $ arduino-cli core install esp32:esp32 \ --additional-urls https://espressif.github.io/arduino-esp32/package_esp32_index.json $ arduino-cli board list Port Protocol Type Board Name FQBN Core /dev/ttyACM0 serial Serial Port (USB) Unknown ``` To attach to the dev board when it's connected over USB Serial ``` # Get the board FQBN from the board list $ arduino-cli board list all esp32 $ arduino-cli board attach -b esp32:esp32:esp32s3 -p serial:///dev/ttyACM0 . ``` To compile a project in the current directory ``` arduino-cli compile -b esp32:esp32:esp32s3 . ``` .. Then watch lots and lots of errors because the Arduino IDE has hidden the knowledge of header files and compiler flags from you. * Apparently `.ino` files are magic, and get a bunch of stuff added to them for free, like the correct header files (like `Arduino.h`) for whatever you're using. * Beware the fact that Arduino is C++ by default - if you make a `.c` file and expect to be able to call `Serial.xxx()`, that's not gonna work. Your code has to be `.cpp` files, like it or not. * Your board has hidden compile options that you're used to setting in the Arduino IDE. For example USB CDC on boot options. If you don't set this at all, you'll get mysterious errors about Serial missing, etc. You need to inspect the board parameters and pass them to your build command appropriately. * You don't get to control the link order on the output files. * If you don't specify the output directory, it goes to some cache directory off in the middle of nowhere ``` $ mkdir -p build $ arduino-cli compile --fqbn esp32:esp32:esp32s3:CDCOnBoot=cdc --output-dir build/ . Sketch uses 320680 bytes (24%) of program storage space. Maximum is 1310720 bytes. Global variables use 22504 bytes (6%) of dynamic memory, leaving 305176 bytes for local variables. Maximum is 327680 bytes. $ ls build/ 08-74HC595-Snake.ino.bin 08-74HC595-Snake.ino.elf 08-74HC595-Snake.ino.merged.bin esp32.esp32.esp32s3 08-74HC595-Snake.ino.bootloader.bin 08-74HC595-Snake.ino.map 08-74HC595-Snake.ino.partitions.bin ``` Sweet. Now to upload it ``` $ arduino-cli upload -p /dev/ttyACM0 -b esp32:esp32:esp32s3:CDCOnBoot=cdc --build-path build/ esptool v5.3.0 Serial port /dev/ttyACM0: Connecting.... Connected to ESP32-S3 on /dev/ttyACM0: Chip type: ESP32-S3 (QFN56) (revision v0.2) Features: Wi-Fi, BT 5 (LE), Dual Core + LP Core, 240MHz, Embedded PSRAM 8MB (AP_3v3) Crystal frequency: 40MHz MAC: 1c:db:d4:59:f8:c4 Uploading stub flasher... .... snip ... Verifying written data... Hash of data verified. Hard resetting via RTS pin... New upload port: /dev/ttyACM0 (serial) ``` Now let's monitor the serial: ``` $ arduino-cli monitor -p /dev/ttyACM0 Using default monitor configuration for board: esp32:esp32:esp32s3 Monitor port settings: baudrate=9600 bits=8 dtr=on parity=none rts=on stop_bits=1 Connecting to /dev/ttyACM0. Press CTRL-C to exit. ``` Let's debug it. The process requires spinning up OpenOCD and connecting GDB to it. Figuring out where OpenOCD got installed is not necessarily straightforward ... it depends on your toolchain, version, etc ... There is some googling involved. But once you find it: ``` cd ~/.arduino15/packages/esp32/tools/openocd-esp32/v0.12.0-esp32-20251215/share/openocd/scripts/ ../../../bin/openocd -f interface/esp_usb_jtag.cfg -f target/esp32s3.cfg ``` Now you can connect gdb to it. I use a `.gdbinit` file to help set up GDB for monitoring this specific hardware environment, doing things like mapping a custom reset function: ``` $ cat .gdbinit # 1. Connect to the running OpenOCD server target remote :3333 # 2. Configure GDB for Espressif-specific environments set remote hardware-watchpoint-limit 2 set remote hardware-breakpoint-limit 2 set mem inaccessible-by-default off # 3. Handle FreeRTOS multi-threading without crashing GDB handle SIGTRAP noprint nostop pass # 4. Define a custom macro to safely reset the ESP32-S3 define reset monitor reset halt flushregs end # 5. Execute a fresh reset and set initial breakpoint reset thbreak setup continue ```
