GMSR STUDIO← Limonchito docs

Limonchito working document, published as-is. Prices and lead times are estimates from September 2026 and change often.

Lemon: programming/test fixture and factory test sequence (rev B)

Scope: the board-level fixture (station 2 in ASSEMBLY.md), used after JLCPCB assembly and hand-soldering of the coil, LRA, dome carriers and SIM, before the board goes into the core. Also a short final test on the sealed lemon. The command set matches firmware/src/factory.c. Every command prints one line <TEST> KEY=VALUE ... RESULT=PASS|FAIL, which the host script parses. Pads and nets: NETLIST.md, production/ELECTRONICS_REVB.md.

1. What the fixture contacts

All probing is from the bottom side except the RF probe and the LED sensor, which come from the top.

Fixture contact Board pad / net Use
J1.1 VBAT+ voltage sense (after Q1)
J1.2 / J1.3 / J1.4 SWDIO / SWDCLK / nRESET programming (J-Link)
J1.5, TP15 GND ground
J1.6 / J1.7 UART_TX (out of U1) / UART_RX factory shell, 115200 8N1, 3.3 V TTL
TP8 BAT_CONN (J2 pin 1 side, before Q1) power in, reverse-polarity test
TP9 3V3 (3.4 V rail) rail check
TP10 / TP11 QI_5V / RECT Qi receiver check
TP12 ACC_INT1 accelerometer interrupt continuity
TP13 MCP_STAT charger state
TP14 LED_A LED anode voltage (open/short check)
RF pogo (top) ANT1 feed pad (x = 15…16) + GND pad fixture antenna for the modem attach test
Photodiode (top) above D1 (x 10.0, y 11.0) LED on/off

J1 is a 2×4 grid at 2.54 mm (position 8 empty). TPs are Ø1.0 mm pads. Use P75-series (Ø1.02 mm) pogo pins with a 4-point crown or spear tip, 1.5 mm pre-travel, about 1.5 N each. Mount them in a 3 mm FR4 or CNC acetal plate drilled from the placement CSV. Pogo positions come straight from drawings/pcb_placement.csv (J1 centre −18.8, 0 bottom, TP8…TP15 rows). Mirror X for a bottom-up bed.

2. Fixture hardware (per fixture, about €650–900)

Item Part / suggestion Est. €
Nest + clamp 3D-printed nest locating on the PCB outline and the dome carriers; a toggle clamp (e.g. Kipp K0073 class) pressing a printed lid with soft foam pads on the parts. Leave the LRA free (foam cut-out), otherwise the LRA test reads low 60
Pogo bed 8 × J1/TP pins + receptacles, 1 RF coax pogo (Ingun HFS or equivalent SMA-terminated spring probe) 90
Programmer SEGGER J-Link BASE (commercial licence), or the nRF9151 DK's on-board J-Link used in "external target" mode through its Debug-out header. Not J-Link EDU (licence forbids production) 0–450
Power + current Nordic PPK2 in source-meter mode (0.8–5 V, measures nA to 1 A) feeding TP8 100
Relays / IO RP2040 or Arduino-class board + 8-channel relay/MOSFET board: PPK2 ↔ TP8 path, polarity-reversal relay (DPDT), Qi puck USB power, dome actuators 40
Dome actuators 2 × 5 V push solenoids (≈10 mm stroke) with Ø6 mm silicone tips over SWA/SWB, or operator presses 25
LED sensor BPW34 photodiode + transimpedance op-amp into the RP2040 ADC, in a black tube over D1 10
Qi transmitter the same certified Qi puck used in the cradle, under the nest with a 3.3 mm acetal spacer (matches the lemon's coil-to-pad distance) 20
Fixture antenna small wideband LTE stick antenna (698–2690 MHz) on the RF pogo's SMA 15
USB-UART FTDI TTL-232R-3V3 or CP2102 (3.3 V) 15
Reverse-polarity source 4.20 V from the relay board via 1.00 kΩ sense resistor + DPDT relay (measure the drop with the RP2040 ADC) 5
Reference cell (Qi step) a known-good 602025 cell on a JST GH lead, switched onto TP8 during the charge test (the PPK2 cannot sink charge current) 5
Host any laptop, Python 3.10+, pyserial, nrfutil (device + 91 commands), label printer –
Enclosure the Qi puck and the modem attach work in the open. No RF shield box is needed for a pass/fail attach on a live network –

3. Factory test sequence (per board)

The host script (firmware/tools/fixture.py, owned by the firmware stream) runs these steps in order, stops at the first FAIL, and writes one CSV row per unit: serial, IMEI, ICCID, modem fw, app fw, every measured value, PASS/FAIL, timestamp, fixture ID, operator. The limits below are starting values; tighten them after 20 golden boards.

# Step How Pass limits Time
0 Load Insert punched nano-SIM, close hinged lid, place board, clamp host sees clamp switch 15 s
1 Power-off checks (unprogrammed) (a) Reverse polarity: relay applies −4.20 V to TP8 through 1 k for 1 s. (b) Relay back to normal: PPK2 at 3.90 V on TP8, 50 mA limit (a) I
2 Program nrfutil device recover (clean state), program the modem firmware if the SiP ships older than required (nrfutil 91 modem-firmware-upgrade, about 60–90 s), then the merged image (NSIB b0 + MCUboot + app with factory shell) and the b0 key-hash provisioning hex into UICR OTP. Verify, reset nrfutil exit 0; boot banner on UART within 5 s 30 s (+90 s modem fw)
3 Identity factory info RESULT=PASS (IMEI 15 digits), ICCID present (SIM seated), MFW = required version, FW = release, LOCKED=0 5 s
4 Battery sense factory vbat with PPK2 at 3.90 V RESULT=PASS and MV = 3900 ± 60 (divider plus ADC tolerance); CHG=0 2 s
5 Accelerometer factory accel (board flat in the nest) RESULT=PASS, DEV = lis2dw12, G_MG 900–1100, TRIG=irq (host rejects poll) 2 s
5b Accel INT1 drive check Right after step 5 (sensor disarmed, INT1 idles low, push-pull): the RP2040 applies a 100 kΩ pull-up to TP12 and reads it. TP12 must sit on the trace at the U1 end (layout note) TP12 < 0.3 V with the pull-up, which proves U8 drives the net as far as the pad. A full interrupt proof needs factory accel irq (§5) 1 s
6 LED factory led on, read photodiode and TP14; factory led off, read again on: photodiode ≥ threshold (set from golden, about 5× dark), TP14 1.70–2.05 V, PPK2 current rises by 1.2–1.9 mA. off: photodiode < 1.5× dark, TP14 < 0.2 V 2 s
7 Domes factory domes 8000, host fires solenoid A then B (or the operator presses) DOMES A=1 B=1 RESULT=PASS; an initial STUCK_* line = FAIL 3 s
8 Haptics factory lra (auto-calibrate, store, play heartbeat, measure on the accelerometer) CAL=OK, VIB_MG ≥ CONFIG_LEMON_FACTORY_VIB_MIN_MG (firmware Kconfig), RESULT=PASS; the host also logs COMP/BEMF/GAIN for drift tracking 3 s
9 Qi charge Relay swaps TP8 from PPK2 to the reference cell (kept at 3.6–3.9 V, so the charger is in constant-current mode). Host powers the Qi puck, waits 6 s, then factory vbat and reads TP10/TP11/TP13. Puck off, wait 3 s, factory vbat on: CHG=1, TP10 QI_5V 4.90–5.10 V, TP11 RECT 5.0–8.0 V, TP13 STAT low (charging). off: CHG=0. Swap back to PPK2 12 s
10 Modem factory modem 90 with the RF pogo on the fixture antenna ATTACH=OK, RSRP_DBM ≥ −105 (set from golden units at the workshop bench), IP present, T_S ≤ 60 20–60 s
11 Provision Host creates a serial and a 128-bit random key (Python secrets), stores both in the production DB (encrypted at rest), then factory provision <serial> <key32hex> <server> <port>. It writes the pairing/IMEI/ICCID row for the backend KEY=1 RESULT=PASS; the key is never logged in the CSV 2 s
12 Key check factory seal 4026531840 1 0; host decrypts/verifies with its copy of the key (AES-128-CCM, nonce per lemon.h) tag verifies, plaintext type = 1 1 s
13 Lock + sleep current factory lock (unit reboots, shell and UART gone). Leave on PPK2 at 3.90 V. Wait 45 s for attach + HELLO, then average current over 41 s (two eDRX cycles) reboot seen (UART goes silent); I_avg ≤ 200 µA in the fixture's coverage (floor is 60–120 µA); no current > 5 mA for more than 3 s after the first 45 s 90 s
14 APPROTECT nrfutil device protection-set SecureRegions then All (or nrfjprog --rbp ALL). Power-cycle. Then nrfutil device protection-get reports All; a memory read attempt fails 10 s
15 Label + unload print serial/IMEI label (inside the battery bay / on the core); CSV row closed – 10 s

Cycle time: about 4.5 min with the modem firmware already current, or about 6 min with a modem firmware update. Steps 10 and 13 dominate. For 500 boards in about 5 working days, run 2 fixtures in parallel with one operator (≈20 boards/h). Or run steps 10 + 13 on a 4-slot "soak rack" that has just power, PPK2/INA219 current sense and a fixture antenna, which frees the pogo fixture after step 9.

Retest rule: a FAIL at steps 1, 3, 5, 6, 8 or 9 goes to rework (visual/X-ray of the part named in the failing test). A FAIL at step 10 is retried once with the antenna probe reseated. Locked units (after step 13) that fail 13/14 can only be reused after nrfutil device recover (full erase) and a restart from step 2.

4. Final test on the sealed lemon (station "final functional test")

The unit is locked, so there is no shell. The test is black-box, through the backend:

# Step Pass
F1 Insert a charged cell into the battery bay, close it, place on the cradle cradle LED shows charging; the backend sees HELLO and later a STATUS with FLAG_CHARGING
F2 The backend admin CLI temporarily pairs the unit with the workshop's golden lemon pairing row written
F3 Squeeze the unit (both domes) golden lemon buzzes within one eDRX cycle (≤25 s); unit gets the delivered tick
F4 Squeeze the golden lemon; leave the unit untouched unit buzzes; after the firmware's notice time the LED breathes through the skin window, visible under workshop light
F5 Pick the unit up LED stops (accelerometer wake works end to end)
F6 Unpair from golden, assign the real partner serial, pack the pair backend pairing confirmed

Log the result against the serial in the same CSV/DB.

5. Notes and requests to the other streams