Skip to content

Hardware

PurpleSentinel edited this page Oct 11, 2026 · 5 revisions

Hardware

What the device is made of, how it is wired, and which parts of that are verified rather than assumed. The authoritative detail lives in docs/HARDWARE_BOM.md and docs/HARDWARE_INTEGRATION.md; this page is the orientation and the warnings.

Read this before plugging anything in

The Waveshare has two physically identical SH1.0 4-pin sockets, one UART and one I2C. The UART block's rear label reads UART, GND, 3v3, TX, RX. The I2C block's pin order has never been reported. If its power ordering differs, a board-to-board cable puts 3.3 V onto a data line and something dies.

Check the label before connecting the receiver. This is the only step in the project that can destroy hardware, and it is still open as #9.

The parts

Ref Part Why this one
H1 Waveshare ESP32-S3-Touch-AMOLED-2.41-B 600×450 AMOLED, touch, ESP32-S3R8 with 8 MB PSRAM and 16 MB flash, QMI8658 IMU, RTC, microSD, supplied case
H2 SparkFun GPS-17285 — u-blox NEO-M9N, SMA, Qwiic 25 Hz navigation rate on four constellations, 3.3 V logic, UART and I2C, UBX
H3 Taoglas Magma X AA.170.301111 Covers all four constellations the M9N tracks, including BeiDou B1 at 1561 MHz, which common 1575–1610 MHz antennas miss
H4 High-endurance microSD, 8–32 GB Session traces and event logs, FAT32
H6 Fused 5 V vehicle USB supply Never connect raw vehicle 12 V to the Waveshare board

Confirmed from the board itself: chip revision v0.2, 16 MB flash, and the fitted card reports as SN32G, 30 GB, FAT.

Two part choices that look wrong until explained

The antenna band, not its gain, is what decides it. BeiDou B1 sits at 1561.098 MHz, below the 1575–1610 MHz window that most "GPS/GLONASS/Galileo" antennas cover. Fit one of those and BeiDou is quietly absent — the receiver still works, so nothing looks broken, while a quarter of the constellations silently stop contributing.

A newer u-blox part would be a downgrade. The M9N does 25 Hz on four constellations. The M10 manages 10 Hz on four, and 20 Hz only with three and a raised CPU clock. For lap timing, fix rate is the whole point, so the older generation is the right one.

How it is wired

The shared I2C bus

On GPIO47 (SDA) / GPIO48 (SCL)
Touch controller
QMI8658 IMU
RTC
IO expander (AMOLED enable, tearing effect, touch interrupt)
GNSS receiver at 0x42, joining via Qwiic

The receiver is the one participant on that bus with a deadline. At 25 Hz a fix is 40 ms and the receiver's buffer holds roughly 410 ms, so the margin is about ten missed polls — which is less comfortable than it sounds on a device whose UI task has starved before. Bus contention is a real design consideration, not a theoretical one.

Both of the breakout's connectors are Qwiic on the same bus, and its I2C jumper carries the pull-ups.

Other assignments

Function Pins Bus
AMOLED QSPI CS / CLK / D0–D3 GPIO9 / 10 / 11–14 SPI2_HOST
AMOLED reset GPIO21
Touch reset GPIO3
microSD CS / SCLK / MOSI / MISO GPIO2 / 4 / 5 / 6 SPI3_HOST, SPI mode
UART socket GPIO43 / 44 UART0 — also the default console

The UART socket can serve the console or the receiver, not both

This is the constraint most likely to catch someone out.

GPIO43/44 is UART0, which is the firmware's primary console. The staged transport plan in ADR-005 moves GNSS to UART for vehicle use, because UART is pushed by DMA while I2C must be polled. That puts the receiver on the same two pins the console speaks on.

If both are used at once, log output is transmitted into the receiver and the console driver owns the pin the receiver answers on. So the console has to move to USB Serial/JTAG before GNSS takes the UART socket.

Two practical consequences:

  • Do not plan to use UART0 as a permanent diagnostic tap. It is wanted for the receiver. Boot logs are obtainable over USB instead — see below.
  • The breakout's UART is plated through-holes only, so the UART stage needs soldering. I2C is solderless, which is why bring-up uses it. Leave the breakout's SPI solder jumper open: closing it disables the UART on those lines.

The module's UART default is 38400 baud, 8N1, per the data sheet. The 115200 figure in SparkFun's hookup guide is their serial monitor, not the module.

Getting diagnostics off the board

make firmware-capture-boot captures a cold boot over the board's own USB-Serial-JTAG port — the full log from the ROM banner to the application, with no extra hardware.

Two traps, both of which produce silence rather than an error:

  • Opening the port and waiting captures nothing. Boot output is written and discarded before a host attaches, so the listener has to hold the port before the reset.
  • Pulsing RTS alone boots the ROM download stub, which prints nothing. RTS is wired to EN (reset) and DTR to GPIO0 (boot select), and pyserial asserts both on open. Releasing RTS with GPIO0 still low starts the download stub. An empty capture then reads as "the output was discarded" when it means "the application never ran".

A genuine power cycle cannot be captured this way at all: the USB device is inside the chip, so cutting power destroys the port the listener is holding. An RTS reset is a chip reset (rst:0x15 USB_UART_CHIP_RESET) and the handle survives it. Only a separate USB-UART adapter on the UART socket could see a post-power-cycle boot, since its own connection to the host is independent of the target's power — and that socket is wanted for the receiver.

Background in #181.

A healthy boot looks like this

TrackSessionTimer GPS bootstrap
imu: ready
sd-card: mounted
sd-card name=SN32G capacity=30424 MB fs-total=30409 MB free=30408 MB
settings: source=0 current-format=1, mode track-day
track catalog: built (26 discovered, 26 loaded, 0 rejected, cap 32)
gnss: transport attached over i2c
ready screen presented on the 600x450 panel

settings: source=0 means the stored configuration was read in its current format; a non-zero value means it was migrated from an older one on that boot. The track counts depend on what is on the card, and rejected or a truncation warning means definitions did not make it into the catalog.

What is verified, and what is not

State
Display, touch, gesture UI working on hardware
microSD, settings persistence, session lifecycle working, survives power cycle
QMI8658 IMU, gyro-tracked attitude, G recording working
Boot log capture over USB working
GNSS receiver never connected. The transport attaches and reports itself at boot, but no part of the chain has met a real receiver
RTC present, not driven
Vehicle power path, enclosure, antenna mounting not built

The receiver and antenna are in hand. The Qwiic cables are not, and that single missing cable is what stands between the device and a lap time.

When the cables arrive

In order, because the first step is the irreversible one:

  1. Read the I2C socket's label and confirm its pin order before connecting anything (#9).
  2. Connect the receiver over Qwiic and check GPS Only mode: a satellite count that climbs, then a fix, outdoors.
  3. Confirm bus faults and a silent receiver are distinguishable on the Diagnostics GNSS page (#178).
  4. Starve the poll deliberately and watch the gap counter trip on the device. The host tests prove the logic; they do not prove the wiring (#18).
  5. Select Donington Park and confirm the timing engine arms (#139).
  6. Only then the UART stage, which needs soldering and the console moved first (ADR-005).

Clone this wiki locally