Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions PCB/v2.x/GPIO_table.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@ Pins are chosen by position, not carried over from V1, so V2 needs its own firmw

## Module rules
- **Variant:** use the -N8.
- **Straps:** IO0 is pulled up by R_BOOT_PU1. IO3, IO45 and IO46 are unconnected, so nothing can disturb the boot mode or flash voltage.
- **Straps:** IO0 is pulled up by R_BOOT_PU1. IO45 and IO46 are unconnected, so nothing can disturb the boot mode or flash voltage. IO3 carries PWR_HOLD: its strap only selects the JTAG source, which is ignored at default eFuses (Table 3-5).
- **ADC:** analog inputs stay on ADC1 (IO1-IO10), since ADC2 is not usable while the Wi-Fi radio runs, which ESP-NOW needs.
- **PWR_HOLD is on IO42,** which has no reset pull (ESP32-S3 datasheet Table 2-1). RXD0's pull-up could re-latch the board as the rail collapses at power-off. C_LATCH_G1 (1 µF) carries the latch through a reset of about 300 ms at worst (derated capacitor, flat cell).
- **Firmware must latch PWR_HOLD with `gpio_hold_en(IO42)` once it has driven it high,** so it survives resets and USB uploads, and must call `gpio_hold_dis(IO42)` before driving it low to power off. Espressif documents hold across chip and system resets; confirm it through ROM download mode on hardware.
- **PWR_HOLD is on IO3,** which has no reset pull (ESP32-S3 datasheet Table 2-1). RXD0's pull-up could re-latch the board as the rail collapses at power-off. It has to be an RTC pad (IO0-IO21): on V1 hardware IO3's hold lasted through a full USB upload, while IO42, a digital pad, lost its hold on the USB reset that ends an upload. C_LATCH_G1 (1 µF) carries the latch through a reset of about 300 ms at worst (derated capacitor, flat cell).
- **Firmware must latch PWR_HOLD with `gpio_hold_en(IO3)` once it has driven it high,** so it survives resets and USB uploads, and must call `gpio_hold_dis(IO3)` before driving it low to power off. Light sleep must not release it. The V1 test ran IO3 with no load, so confirm with a real upload at V2 bring-up.
- **Power button sense stays on an RTC-domain pin.** With the board off, VSYS lifts this node through R_INV_BASE1 and D_INV_ISO1. V1 verified that on a VDD3P3_RTC pin; IO33-IO37 are in the VDD_SPI domain (Table 2-1).
- **TXD0 (IO43)** prints the boot log, so it stays free for debugging. RGB data has moved off it, so the LED no longer sees that traffic.
- **Boot glitches:** the low-level power-up glitches on IO1-IO17 (Table 2-2, where IO15 and IO16 appear as XTAL_32K) are 60 µs. Those pins carry only inputs, except IO1 (SLEEP_RQ, where low means awake). XBee RESET is on RXD0, which has no listed glitch.
- **Boot glitches:** the low-level power-up glitches on IO1-IO17 (Table 2-2, where IO15 and IO16 appear as XTAL_32K) are 60 µs. Those pins carry only inputs, except IO1 (SLEEP_RQ, where low means awake) and IO3 (PWR_HOLD, where D_OR2 blocks a low). XBee RESET is on RXD0, which has no listed glitch.
- **IO26 stays unconnected.** It is the PSRAM chip-select, and Arduino runs a PSRAM probe at every boot, so it is driven before `setup()`. The probe fails on the -N8, which has no PSRAM, and boot continues.
- **Do not flash V1 firmware on V2.** It would leave PWR_HOLD unheld on IO42, since V1 holds IO44, and treat other V2 pins as SPI. V2 needs its own pin map.
- **Do not flash V1 firmware on V2.** It would leave PWR_HOLD unheld on IO3, since V1 holds IO44, and treat other V2 pins as SPI. V2 needs its own pin map.
- **EN:** 10 kΩ and 1 µF, as Espressif advises (Peripheral Schematics), so EN crosses 0.75 × VDD about 14 ms after the rail is up. V1 has no capacitor.
- **Pad 61:** the thermal pad is 9 segments, soldered to GND.

Expand Down Expand Up @@ -64,7 +64,7 @@ Pins are chosen by position, not carried over from V1, so V2 needs its own firmw
| XBee RTS | IO39 | 35 | Carrier pad 4 |
| Vol down | IO40 | 36 | Direct, active low, internal pull-up |
| XBee ON/SLEEP | IO41 | 37 | Carrier pad 5 |
| Power hold (PWR_HOLD) | IO42 | 38 | Must go high early in boot, then latched with `gpio_hold_en` |
| Power hold (PWR_HOLD) | IO3 | 7 | Must go high early in boot, then latched with `gpio_hold_en` |
| XBee RESET | RXD0 (IO44) | 40 | Carrier pad 6. Drive open drain only: low or released. The pull-up is on the carrier, and RXD0's own pull-up is harmless here |
| Reset | EN | 45 | R_EN_PU1 and C_EN1 only, no button |

Expand Down Expand Up @@ -92,4 +92,4 @@ Every button wires straight to a module GPIO and to GND, using the internal pull
OLED 0x3C, ICM-42670-P 0x68. R_I2C_SDA1 and R_I2C_SCL1 stay on the board so the IMU works with no OLED attached.

## Free pins
TXD0 (IO43) is free. Leave IO26 unconnected (see Module rules). IO3, IO45 and IO46 are straps, so leave them unconnected.
TXD0 (IO43) and IO42 are free. Leave IO26 unconnected (see Module rules). IO45 and IO46 are straps, so leave them unconnected.
8 changes: 4 additions & 4 deletions PCB/v2.x/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ through `snips_controller.stackup_sch`; sync it with the Stackup KiCad plugin. R
- **Reflow:** the bottom side goes first and the top side last, because Espressif requires a single reflow for the module (datasheet section 12.3). The XBee reflows on its own carrier and never goes upside down, which is what stopped the two modules from sharing this board before.
- **Radio link:** Amidala's droids use XBee Zigbee 3.0, so a Zigbee controller needs the carrier. The firmware still has to move from the XBee's SPI interface to UART, and needs a V2 pin map.
- **Programming:** USB, through the module's native USB. To force download mode, hold SW_BOOT1 while pressing the power button. A blank board has no firmware to latch PWR_HOLD, so hold the power button through its first flash. After that, uploads need no button.
- **No reset button:** the power button can only turn the board on. PWR_HOLD is on IO42, which has no reset pull, and C_LATCH_G1 (1 µF) carries the latch through a reset of about 300 ms at worst (derated capacitor, flat cell). Firmware latches the pin with `gpio_hold_en`, so a longer reset or a USB upload holds power with no button, and releases it with `gpio_hold_dis` to power off. Keep a watchdog running to recover from a hang.
- **No reset button:** the power button can only turn the board on. PWR_HOLD is on IO3, an RTC pad with no reset pull, and C_LATCH_G1 (1 µF) carries the latch through a reset of about 300 ms at worst (derated capacitor, flat cell). Firmware latches the pin with `gpio_hold_en`, so a longer reset or a USB upload holds power with no button, and releases it with `gpio_hold_dis` to power off. Keep a watchdog running to recover from a hang.
- **Buttons:** all 13 connect directly to module GPIOs. See `GPIO_table.md`.
- **Power blocks:** the buck, charger and power latch follow V1's relative placement, all on the bottom. Two charger GND vias were moved 0.1-0.75 mm in V1's layout to clear earlier button positions. C_BUCK_IN1 turned 90 degrees with its VSYS pad straight above the VIN pin. BUCK_FB runs around the input cap and hops to F.Cu past the inductor, so both planes shield it; keep it out from under L_BUCK1. The latch is too long to fit whole, so it is split at `PWR_BTN_TRIGGER`: the switch and `PWR_LATCH_G` node sit under the stick, turned 270 degrees, and the inverter sits beside the stick's lower pins. See `diagrams/` for why they are arranged this way.
- **OLED cable:** U_OLED1's opening faces USB, and a Qwiic plug is 7.0 mm wide with its protrusions, so keep top-side parts out of x 0.9-7.9 mm from the mouth at y 52.7 mm down to the MH3 post. Check that a real cable goes in before committing the shell.
Expand Down Expand Up @@ -108,7 +108,7 @@ The droids use XBee Zigbee 3.0, so the controller joins that network through the
Recommended bringup order:
1. Current-limited supply on VSYS, with no carrier fitted: check 3V3 and idle current with the power button held.
2. Flash over USB, holding the power button, with SW_BOOT1 held if the chip does not enumerate. Confirm firmware drives `PWR_HOLD` high.
3. Time the shortest press that stays on, at 3.3 V and 4.2 V. The simulation gives 0.1-0.2 s on a normal cell and up to about 0.4 s on a flat one, set by when firmware drives `PWR_HOLD`, so plan on a half-second press and keep that drive early in boot. Run a USB upload with no button held, then confirm a watchdog reset reboots the board and a graceful shutdown stays off.
3. Time the shortest press that stays on, at 3.3 V and 4.2 V. The simulation gives 0.1-0.2 s on a normal cell and up to about 0.4 s on a flat one, set by when firmware drives `PWR_HOLD`, so plan on a half-second press and keep that drive early in boot. Run a USB upload with no button held, which is the first test of IO3's hold under D_OR2's load, then confirm a watchdog reset reboots the board and a graceful shutdown stays off.
4. OLED and ICM-42670-P on I2C.
5. SK6812 RGB LED.
6. Buttons, using the pin table.
Expand All @@ -122,8 +122,8 @@ Recommended bringup order:

- Battery → bq25185 (charger and power path, 4.2V, 500mA) → TLV62569 (3.3V buck) → 3V3 for everything, including the carrier.
- Power switch: soft latch (AO3401A PFET + AO3400A NFET) controls the TLV62569 EN pin. USB does not hold it on.
- Boot: press the power button → latch enables 3V3 → module boots → IO42 goes high through D_OR2 to hold the latch, then firmware latches it with `gpio_hold_en`.
- Shutdown: firmware sees a hold on IO21 → graceful shutdown → `gpio_hold_dis`, IO42 low → the gate decays through R_LATCH_G1 in about 0.6-1.7 s → power cut.
- Boot: press the power button → latch enables 3V3 → module boots → IO3 goes high through D_OR2 to hold the latch, then firmware latches it with `gpio_hold_en`.
- Shutdown: firmware sees a hold on IO21 → graceful shutdown → `gpio_hold_dis`, IO3 low → the gate decays through R_LATCH_G1 in about 0.6-1.7 s → power cut.
- bq25185 STAT1 and STAT2 go to IO15 and IO14 (open drain, 10kΩ pull-ups to 3V3).
- Battery voltage: VSYS through R_VSYS1/R_VSYS2 to IO7, reading VSYS/3.
- Below about 3.3 V on the battery the buck passes it through, and the DRV5055's minimum is 3.0 V, so the firmware cutoff should sit above that.
Expand Down
2 changes: 1 addition & 1 deletion PCB/v2.x/diagrams/power_control_layout.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ Top copper layer. The VSYS→PWR_EN path runs straight across the top as one thi
| **B** | **SW_PWR1's position isn't yours to optimize electrically.** It has to land under a hole in whatever enclosure this board sits in — mechanical placement drives this part, and the rest of the latch logic has to route to wherever that ends up, not the other way around. |
| **C** | **C_DBNC1 sits right on the switch's own pins.** Same principle as the charger's pushbutton guidance in its own datasheet: the debounce cap does its job by being close to the contact bounce it's filtering, not by being close to anything else. |
| **D** | **The diode-OR and its pull-down are one compact decision node — and R_LATCH_G1's value is load-bearing, not incidental.** D_OR1, D_OR2, R_LATCH_G1, and Q_LATCH1's gate all meet at PWR_LATCH_G. A press only drives this node through `R_INV_PU1` (100kΩ) and a diode drop — with the original 10kΩ pull-down, that divider left barely 0.3V at Q_LATCH1's gate, nowhere near enough to turn it on. Raised to 1MΩ, the same divider delivers ~2.4V at the bottom of the battery range (VSYS≈3.0V, modelling a real ~0.35V diode drop at µA currents) up to ~3.5V at full charge, without adding any idle current (that's set by `R_INV_PU1`, untouched). A 2N7002's worst-case threshold is 2.5V, which left no margin at the bottom, so `Q_LATCH1` is an AO3400A (Vgs(th) ≤1.45V): at least ~1V of overdrive everywhere. Keeping this cluster tight matters less for noise than for just keeping the logic legible on the board. |
| **E** | **PWR_HOLD arriving late is a hard failure, not a glitch.** Per this project's own GPIO notes, IO42 (PWR_HOLD) must go high as the MCU's very first instruction — release the button before that happens and R_LATCH_G1's pull-down turns everything back off. Not a layout fix, but it's the reason D_OR2's path exists at all. |
| **E** | **PWR_HOLD arriving late is a hard failure, not a glitch.** Per this project's own GPIO notes, IO3 (PWR_HOLD) must go high as the MCU's very first instruction — release the button before that happens and R_LATCH_G1's pull-down turns everything back off. Not a layout fix, but it's the reason D_OR2's path exists at all. |
| **F** | **D_OR1 can't read `PWR_BTN_SENSE` directly — found the hard way, on real hardware.** The original design had `R_BTN_PU1` (to VSYS) and a since-removed `R_PWR_SENSE1` (to GND) both on that node, dividing it to roughly half VSYS at idle — comfortably above a diode's ~0.6V forward threshold, so `D_OR1` was forward-biased (and the board powered on) the instant a battery was plugged in, button or no button. The inverter fixes this properly instead of just retuning the divider: idle, `Q_INV1` is on and holds `PWR_BTN_TRIGGER` at GND; pressed, it turns off and `R_INV_PU1` pulls `PWR_BTN_TRIGGER` to VSYS. Only a real press can forward-bias `D_OR1` now. Keep this block close to `D_OR1` — it's parts serving one signal, not worth spreading out.<br><br>**Second-order bug, found the same way:** the inverter's own base resistor originally tapped `PWR_BTN_SENSE` directly, in series with `R_BTN_PU1` — stealing enough idle base current to sag the MCU's own button-read node to ~1V instead of near-VSYS. `R_INV_BASE1` is now its own dedicated 100kΩ pull-up straight to VSYS (not sharing current with `R_BTN_PU1`), and `D_INV_ISO1` (a Schottky, chosen specifically for its lower forward drop than Q_INV1's own base-emitter junction) lets a press still pull the base low without ever loading `PWR_BTN_SENSE`. |
| **G** | **`PWR_EN` had no defined rest state — TI's own datasheet says not to do this.** `Q_PWR1`'s drain feeds the buck's EN pin directly, with nothing else on that node. TLV62569's EN pin is a bare comparator input with no internal pull ("Do not leave floating" per its datasheet) — with `Q_PWR1` off, EN was held by MOSFET/input leakage alone, no guaranteed logic level. `R_PWR_EN_PD1` (100kΩ to GND) fixes it: negligible loading when `Q_PWR1`'s tens-of-mΩ RDS(on) is driving EN high, solid GND the instant it's off. |
| **H** | **`PWR_BTN_SENSE` must never sit above the MCU's rail.** It lands directly on IO21 (module pin 25), whose ceiling is VDD + 0.3V (~3.6V), but `R_BTN_PU1` used to pull it to VSYS — up to ~4.5V on USB, and it back-fed the unpowered 3V3 rail through the pin's clamp diode whenever the MCU was off. Since `R_INV_BASE1` gave the inverter its own VSYS pull-up (callout F), `R_BTN_PU1` no longer has to supply the inverter, so it now pulls to 3V3: idle reads 3.3V, and with the MCU off the node is simply unpowered instead of leaking into the rail. |
Expand Down
54 changes: 27 additions & 27 deletions PCB/v2.x/production/gerbers/snips_controller-B_Cu.gbr
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
%TF.GenerationSoftware,KiCad,Pcbnew,10.0.5*%
%TF.CreationDate,2026-11-01T10:03:19-08:00*%
%TF.CreationDate,2026-11-01T11:07:59-08:00*%
%TF.ProjectId,snips_controller,736e6970-735f-4636-9f6e-74726f6c6c65,rev?*%
%TF.SameCoordinates,Original*%
%TF.FileFunction,Copper,L4,Bot*%
%TF.FilePolarity,Positive*%
%FSLAX46Y46*%
G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)*
G04 Created by KiCad (PCBNEW 10.0.5) date 2026-11-01 10:03:19*
G04 Created by KiCad (PCBNEW 10.0.5) date 2026-11-01 11:07:59*
%MOMM*%
%LPD*%
G01*
Expand Down Expand Up @@ -798,6 +798,10 @@ X122700000Y-114100000D03*
X121100000Y-124880210D03*
%TO.N,/XBee/XBEE_SLEEP_RQ*%
X138900000Y-106200000D03*
%TO.N,/MCU_Core/PWR_HOLD*%
X140950000Y-90700000D03*
X138485000Y-72539999D03*
X142000000Y-107300000D03*
%TO.N,/Buttons_Direct/BTN_MACRO5*%
X133300000Y-100800000D03*
X133100000Y-107000000D03*
Expand Down Expand Up @@ -842,10 +846,6 @@ X147700000Y-98600000D03*
X134700000Y-117700000D03*
%TO.N,/XBee/XBEE_ON_SLEEP*%
X135600000Y-118300000D03*
%TO.N,/MCU_Core/PWR_HOLD*%
X140950000Y-90700000D03*
X138485000Y-72539999D03*
X142000000Y-107300000D03*
%TO.N,/XBee/XBEE_RESET*%
X138200000Y-120550000D03*
%TO.N,VBUS*%
Expand Down Expand Up @@ -1309,6 +1309,27 @@ X139150000Y-105950000D02*
X139150000Y-103600000D01*
X138900000Y-106200000D02*
X139150000Y-105950000D01*
%TO.N,/MCU_Core/PWR_HOLD*%
X142700000Y-97600000D02*
X142700000Y-93500000D01*
X143200000Y-98100000D02*
X142700000Y-97600000D01*
X142000000Y-107300000D02*
X143200000Y-106100000D01*
D48*
X138385000Y-72440000D02*
X138485000Y-72539999D01*
D49*
X143200000Y-106100000D02*
X143200000Y-98100000D01*
D48*
X136935000Y-72440000D02*
X138385000Y-72440000D01*
D49*
X140950000Y-91750000D02*
X140950000Y-90700000D01*
X142700000Y-93500000D02*
X140950000Y-91750000D01*
%TO.N,/Buttons_Direct/BTN_MACRO5*%
X135460000Y-102100000D02*
X135460000Y-101840000D01*
Expand Down Expand Up @@ -1516,27 +1537,6 @@ X135600000Y-118300000D02*
X136610000Y-119310000D01*
X136610000Y-119310000D02*
X136610000Y-121300000D01*
%TO.N,/MCU_Core/PWR_HOLD*%
X142700000Y-97600000D02*
X142700000Y-93500000D01*
X143200000Y-98100000D02*
X142700000Y-97600000D01*
X142000000Y-107300000D02*
X143200000Y-106100000D01*
D48*
X138385000Y-72440000D02*
X138485000Y-72539999D01*
D49*
X143200000Y-106100000D02*
X143200000Y-98100000D01*
D48*
X136935000Y-72440000D02*
X138385000Y-72440000D01*
D49*
X140950000Y-91750000D02*
X140950000Y-90700000D01*
X142700000Y-93500000D02*
X140950000Y-91750000D01*
%TO.N,/XBee/XBEE_RESET*%
X138200000Y-120550000D02*
X139150000Y-120550000D01*
Expand Down
4 changes: 2 additions & 2 deletions PCB/v2.x/production/gerbers/snips_controller-B_Mask.gbr
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
%TF.GenerationSoftware,KiCad,Pcbnew,10.0.5*%
%TF.CreationDate,2026-11-01T10:03:19-08:00*%
%TF.CreationDate,2026-11-01T11:07:59-08:00*%
%TF.ProjectId,snips_controller,736e6970-735f-4636-9f6e-74726f6c6c65,rev?*%
%TF.SameCoordinates,Original*%
%TF.FileFunction,Soldermask,Bot*%
%TF.FilePolarity,Negative*%
%FSLAX46Y46*%
G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)*
G04 Created by KiCad (PCBNEW 10.0.5) date 2026-11-01 10:03:19*
G04 Created by KiCad (PCBNEW 10.0.5) date 2026-11-01 11:07:59*
%MOMM*%
%LPD*%
G01*
Expand Down
4 changes: 2 additions & 2 deletions PCB/v2.x/production/gerbers/snips_controller-B_Paste.gbr
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
%TF.GenerationSoftware,KiCad,Pcbnew,10.0.5*%
%TF.CreationDate,2026-11-01T10:03:19-08:00*%
%TF.CreationDate,2026-11-01T11:07:59-08:00*%
%TF.ProjectId,snips_controller,736e6970-735f-4636-9f6e-74726f6c6c65,rev?*%
%TF.SameCoordinates,Original*%
%TF.FileFunction,Paste,Bot*%
%TF.FilePolarity,Positive*%
%FSLAX46Y46*%
G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)*
G04 Created by KiCad (PCBNEW 10.0.5) date 2026-11-01 10:03:19*
G04 Created by KiCad (PCBNEW 10.0.5) date 2026-11-01 11:07:59*
%MOMM*%
%LPD*%
G01*
Expand Down
4 changes: 2 additions & 2 deletions PCB/v2.x/production/gerbers/snips_controller-B_Silkscreen.gbr
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
%TF.GenerationSoftware,KiCad,Pcbnew,10.0.5*%
%TF.CreationDate,2026-11-01T10:03:19-08:00*%
%TF.CreationDate,2026-11-01T11:07:59-08:00*%
%TF.ProjectId,snips_controller,736e6970-735f-4636-9f6e-74726f6c6c65,rev?*%
%TF.SameCoordinates,Original*%
%TF.FileFunction,Legend,Bot*%
%TF.FilePolarity,Positive*%
%FSLAX46Y46*%
G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)*
G04 Created by KiCad (PCBNEW 10.0.5) date 2026-11-01 10:03:19*
G04 Created by KiCad (PCBNEW 10.0.5) date 2026-11-01 11:07:59*
%MOMM*%
%LPD*%
G01*
Expand Down
4 changes: 2 additions & 2 deletions PCB/v2.x/production/gerbers/snips_controller-Edge_Cuts.gbr
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
%TF.GenerationSoftware,KiCad,Pcbnew,10.0.5*%
%TF.CreationDate,2026-11-01T10:03:19-08:00*%
%TF.CreationDate,2026-11-01T11:07:59-08:00*%
%TF.ProjectId,snips_controller,736e6970-735f-4636-9f6e-74726f6c6c65,rev?*%
%TF.SameCoordinates,Original*%
%TF.FileFunction,Profile,NP*%
%FSLAX46Y46*%
G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)*
G04 Created by KiCad (PCBNEW 10.0.5) date 2026-11-01 10:03:19*
G04 Created by KiCad (PCBNEW 10.0.5) date 2026-11-01 11:07:59*
%MOMM*%
%LPD*%
G01*
Expand Down
Loading
Loading