Limonchito working document, published as-is. Prices and lead times are estimates from September 2026 and change often.
Lemon totem - design as built (rev B mechanical, 2026-09-17)
Two phone-free squeeze-to-buzz lemons on LTE-M. The mechanical design is generated by build_lemon.py and
build_cradle.py (Blender) and make_drawings.py. All three take their parameters from lemon_geom.py:
edit a number there and rerun them. Coordinates: X = length (+X blossom tip / antenna end), Y = width (squeeze axis),
Z = thickness (+Z hatch, -Z Qi rest face), origin at the body centre, millimetres.
Current state per stream: mechanical rev B = this file + production/MECHANICAL_REVB.md; electronics rev B =
production/ELECTRONICS_REVB.md, NETLIST.md, drawings/pcb_placement.csv; firmware/backend = production/FIRMWARE_BACKEND.md;
production process = ASSEMBLY.md. Sections 1, 1b, 1c and 5-9 below are the rev A record: the electronics
details there are superseded by ELECTRONICS_REVB.md wherever they differ (for example XTAL1 removed, J2 connector, D1, U8).
Rev A files are kept in rev_a_backup/.
0. Rev B mechanical, in one table
| Rev B | |
|---|---|
| Shape | Lemon, 63.9 x 44.0 x 27.0 mm incl. blossom nipple (4.2), stem nipple (2.0) + stub, green leaf relief (part of the casting). Flat Qi rest face about 24 x 24 at z = -13. |
| Skin | Cast PU Shore 70A, 1.5 mm (0.8 under the Qi flat and over the LED rod), peel dimples 0.2 deep, smooth squeeze pads |
| Core | 2 SLA halves bonded permanently; captive plunger + PET film in each d9.6 window; 4 PCB seat ribs; coil pad |
| Battery | User-replaceable (EU 2023/1542 Art. 11): cell (envelope 26 x 20.5 x 6.2) on J2 JST GH top-entry, under a top hatch with 2 x M2x5 Torx T6 + ruthex M2x4 inserts and a hook tongue; no glue. Sealed variant: LEMON_BATTERY_ACCESS=sealed |
| LED path | Clear-resin rod d2.5 integral to the hatch plate, 0.3 mm above D1 (10.0, 11.0), 0.8 mm skin window d4 |
| Accelerometer | U8 LIS2DW12 (-9.0, -8.5) top side, under the cell |
| Qi | coil bottom 2.0 mm from the rest face (rev A 3.3) |
| Mass | 32.1 g |
| Fit check | fit_report.txt: PASS (all parts in the cavity with 0.25 margin, no mesh intersections, 27 mechanical checks OK) |
| Cradle | passive leaf shell 134 x 78 x 16 around an unmodified certified Qi puck (default Samsung EP-P2900 d55.5 x 4.4); coil gap 3.0 mm + puck cover |
| Production | 4-cavity platinum-silicone body moulds from printed masters, bottom-fill, pressure-cured PT Flex 70, 2 h 20 min cycle; 8-cavity hatch-skin moulds; station process in ASSEMBLY.md |
1. Rev A record: what changed from the plan, and why
| Spec | Built | Why |
|---|---|---|
| 45 × 32 × 22 mm ellipsoid | 54 × 43 × 22 mm superellipsoid (pebble) | A 20 mm-wide pouch cell, a 10.5 mm SiP and an LTE antenna do not fit in the spec envelope. At 45 × 32 the corners of a 502035 cell sit ~30 % outside the cavity. The pebble outline (exponent 2.4) keeps the corners fuller than an ellipse so the body stays small. |
| 502035 LiPo 350 mAh | 602025 LiPo 250 mAh with PCM | Fits above the SiP with 0.7 mm foam; 5–6 weeks per charge at the chosen eDRX profile. |
| TPS62840 3.3 V buck feeds everything | nRF9151 VDD straight from the cell; TPS62840 only for the 3.3 V logic rail (VDD_GPIO, DRV2605L) | The SiP's own PMIC accepts 3.0–5.5 V (verify in the nRF9151 product spec). Modem TX bursts of ~250 mA should not go through a 60 nA-IQ buck. |
| Domes "on the core" | Two 12.2 mm domes on 12.5 × 12.5 mm vertical FR4 carriers soldered edge-on to the main PCB at ±Y; Ø8 mm rubber nubs cast in place through Ø9.5 mm windows in the core wall | Gives the dome a rigid backing, a 0.15 mm preload gap, and a mechanical lateral locator for the core inside the skin. |
| Ignion NN01-104 chip antenna | Flex-PCB IFA on the +X tip inner wall (≈30 × 6 mm) | NN01-104 wants ~60 × 40 mm of ground plane for 700–960 MHz. Using the tip volume is the only way to get usable sub-GHz efficiency in a 54 mm body. Expect −6 to −10 dB; LTE-M's repetition gain absorbs it. |
| PSM on with zeroed timers | PSM off, eDRX 20.48 s, PTW 1.28 s set with %XPTW |
PSM would make the lemon unreachable between TAUs. With PSM off the PDN stays up and the network pages the device every eDRX cycle. |
| MQTT over TLS to a broker | 16-byte UDP datagrams over 1NCE's OpenVPN to a €4 VPS | TCP/TLS through carrier NAT needs keepalives that would dominate the battery. 1NCE gives each SIM a static private IP reachable through their OpenVPN; the server can push straight to the partner lemon. |
| — | SWD/UART pogo pads on the PCB underside | The body is sealed permanently; program and test before overmolding. |
1b. What changed in the parts-sourcing pass (this session)
Everything above was placeholder part numbers ("e.g.", "verify") priced by guess. This pass replaced every part with a real, currently-orderable SKU (see BOM.md for prices/links) and fixed what that turned up:
| Spec | Was | Now | Why |
|---|---|---|---|
| U1 footprint | LGA 10.5×10.5mm | 113-LFLGA 12.1×11.1mm, moved (7.0,0.5)→(5.5,0.5) | Real Nordic package is bigger than assumed; old position collided with the antenna pi-match cluster once corrected |
| U1 clock | (none — unquestioned) | 32.768kHz LFXO required, XTAL1+CXL1+CXL2 added | Nordic Product Spec: the modem has no internal-clock fallback. This was a real missing component, not a refinement |
| U1 DEC0 | (not modelled) | CDEC0, exact 4.7µF, added | Nordic-verified this pin fails at 10µF/47µF; wasn't on the board at all before |
| U4 | "BQ51003B" | BQ51013BRHLR | BQ51003B isn't a real TI part number |
| L2 coil | "760308101104 (verify)" | Würth 760308101214, 26µH | 101104 is a transmitter coil, wrong part; the resonant caps C7/C8 need recomputing for 26µH, not the old 6–8µH guess |
| U5 | "DFN-8 2×3" | SOT-23-5 | Package was simply wrong |
| U6 | TPS62840 + guessed VSET | TPS62849DLCR, fixed 3.4V | TPS62840 has no fixed-3.3V SKU; its pin-compatible sibling is fixed 3.4V (in spec for everything downstream) and needs no VSET resistor |
| U7 (DW01A+FS8205) | populated | removed | The sourced 602025 cell ships with an integrated PCM; U7 was redundant |
| AIN0 switch (P0.15, DMG3415, 2N7002) | populated | removed, divider now always-on | Saved ~1.65µA (1-3% of the sleep floor) at the cost of 2 unsourced transistors and a guessed pinout — not worth it |
| J1 | "Tag-Connect TC2030-NL" | generic 7-pad pogo jig, 2.54mm pitch | A real TC2030 cable only exposes one spare UART pin (SWO), not full RX+TX duplex — the lemon shell needs both for interactive bring-up |
| C1/C2 | (1,6.5) / (1,-5.5) | (1,7.0) / (1,-6.0) | Clearance under U1's larger footprint |
An actual KiCad schematic now exists (lemon_totem.kicad_sch) generated from NETLIST.md,
and a real priced BOM exists (BOM.md). build_lemon.py and make_drawings.py were
updated to match; build_lemon.py has since been run through Blender in this session too
(see the changelog entry below) — fit_report.txt is current as of that run.
1c. eSIM form factor — resolved
U2 was a placeholder solder-down MFF2 chip (DFN-8 6×5mm). 1NCE only sells MFF2 in reels of 100+, not practical for 2 units. Fix: Same Sky NSIM-2-C, a hinged-lid nano-SIM (4FF) socket ($0.71, DigiKey, in stock) — 1NCE's normal 3-in-1 card punches out to exactly that size (confirmed in 1NCE's own docs). Real footprint is much bigger than the chip it replaces (~13×14mm vs 6×5mm), so U2 moved from the top layer to the bottom layer, at (1.5, 0.0), where there was room; CDEC0 moved out of its way to (9.5, 5.5). Insert the punched-out card into the socket by hand before the clamshell is glued (same point in the sequence as soldering the battery leads) — the hinged lid mechanically latches the card in, which matters more than usual here since nothing is ever opened again after sealing. Card-detect (S/W) pins on the socket are left unpopulated; VPP (C6) is unused by modern SIMs.
2. Envelope and Z stack-up (rev B, z = 0 is the widest section)
| Layer | z range (mm) | Notes |
|---|---|---|
| Hatch skin / body skin top | ... +14.0 | PU 70A 1.5 mm, peel texture |
| Hatch plate (clear resin 1.2) on a 0.8 core ledge | about +7 ... +12 | removable, 2 x M2x5 T6 at the stem end |
| Press ribs + PORON 1.5 (compressed to 1.0) | +3.8 ... +4.8 | holds the cell without glue |
| Cell envelope 26 x 20.5 x 6.2 at (-0.5, -2.2) | -2.4 ... +3.8 | 602025 with PCM, JST GHR-02V plug |
| Foam 0.4 on the U1 shield | -2.8 ... -2.4 | adhered to U1, not to the cell |
| Top-side parts | -4.0 ... +0.25 | U1 1.2 under the cell; J2 4.25 and M1 3.2 beside it |
| Main PCB 44 x 26 x 1.0 | -5.0 ... -4.0 | lowered from -3.0 so the 44 mm board fits the lemon taper (fit 0.982) |
| Bottom-side parts | -6.4 ... -5.0 | <= 1.4 mm |
| Coil leads space | 3.7 mm | |
| Ferrite + coil (Wurth 760308101214) | -11.0 ... -10.1 | on the core coil pad |
| Core floor >= 0.55 + skin 0.8 | -13.0 | Qi rest face |
Squeeze axis at z = -0.6: dome carriers centred there (z -6.85 ... +5.65), 1.6 mm under the hatch ledge.
3. Component placement
Source of truth: drawings/pcb_placement.csv (electronics stream; build_lemon.py reads it on every run and
adopts D1, J2 and U8 automatically). Mechanical positions: M1 LRA at (-18.5, 0) on flying leads (overrides the CSV),
dome carriers at y = +/-13.0, Qi coil centred at (0, 0) on the core pad, cell at (-0.5, -2.2). Keep-outs and interface
numbers: drawings sheet 4 and MECHANICAL_REVB.md §7. placements.json lists the modelled centres and sizes.
4. Squeeze path
Grip across the width -> skin (1.5) + 3M 850 PET film deflect -> captive SLA plunger (d9.0, tip d3.0) slides in the d9.6 core window -> crosses the 0.15 mm preload gap -> Snaptron F12450N dome snaps (~0.35 mm, 4.4 N) -> SQZ low on the carrier -> GPIO wakes the SiP. Both domes within 60 ms (AND). Expected trip 12-20 N of grip (to be measured on R1). Rev A cast a PU nub through a window that was also sealed with film. Both could not be true; the plunger removes the conflict.
5. Electrical
Qi pad ─► L2 coil ─► U4 BQ51013B (5 V) ─► U5 MCP73831 @100 mA ─► BT1 (3.0–4.2 V, PCM inside) ─┬─► U1 nRF9151 VDD (direct)
└─► U6 TPS62849 ─► 3.4 V ─► U1 VDD_GPIO, U3 DRV2605L
U1 GPIO: P0.10 SQZ_A, P0.11 SQZ_B (1 MΩ pull-up, sense-low wake) I²C: P0.30 SDA, P0.31 SCL
P0.12 HAPT_EN, P0.14 CHG_STAT (from U5), AIN0 VBAT/2 via always-on divider (P0.15 spare)
U1 DEC0 ─► 4.7 µF EXACT U1 XL1/XL2 ─► 32.768 kHz LFXO + load caps (required, was missing)
U3 OUT+/OUT− ─► M1 LRA (closed-loop auto-resonance)
Real schematic capture: lemon_totem.kicad_sch + lemon_totem.kicad_pro — every net above
as an importable KiCad file (see NETLIST.md's header for what's authoritative vs. narrative,
and ROUTING.md for how to take it from here to a routed, fabbable board).
6. Firmware (Zephyr, nRF Connect SDK)
- Modem once at boot:
AT%XSYSTEMMODE=1,0,0,0·AT+CEDRXS=2,4,"0010"(20.48 s) ·AT%XPTW=4,"0000"(PTW 1.28 s) ·AT+CPSMS=0·AT%RAI=1·AT+CEREG=5. For a faster feel use"0001"(10.24 s) at ~1.6× sleep current. - Outbound: SENSE wake → debounce 30 ms, both domes within 60 ms → DRV2605L "strong click" locally → 16 B UDP (seq, type, AES-CCM tag) to the server → wait ACK 2 s, retry ×2 → short "delivered" tick → RAI release → sleep.
- Inbound: modem paged inside the eDRX cycle → datagram on the bound UDP socket → verify → heartbeat (two 60 ms LRA pulses 90 ms apart, twice) → sleep. Latency ≤ one eDRX cycle.
- Housekeeping: battery ADC each wake; low-battery pattern under 3.4 V; charge-start tick from CHG_STAT; watchdog.
7. Backend
VPS (Fly.io / Hetzner, €4) running the 1NCE OpenVPN client and ~60 lines of Python: receive from lemon A's static IP, forward to lemon B's, ack both. No broker, no keepalives, no NAT traversal. Bytes per pulse ≈ 150 including headers; 500 MB lasts the 10-year SIM.
8. Power budget
| Item | Per day |
|---|---|
| eDRX 20.48 s floor, good coverage (60–120 µA) | 1.4–2.9 mAh |
| Other ICs asleep (<10 µA) | 0.2 mAh |
| 30 pulses × ~0.1 mAh (≈2 s at ~180 mA) | 3.0 mAh |
| Total | 4.6–6.1 mAh → 35–47 days on 250 mAh × 0.85 usable |
Poor coverage can triple the floor. Charge from empty at 100 mA ≈ 2.7 h.
9. Bill of materials
See BOM.md — real SKUs, real prices, distributor links, checked against actual
distributor/manufacturer pages this session. Short version: ≈€340-390 for 2 units,
electronics + fab + casting resin all in. The casting resin (originally a €340 shock —
Smooth-On PMC-770's only small EU-shippable kit) is resolved: Polytek PT Flex 70, same
Shore A70 hardness, ≈€14-17 for a proper-sized kit — see BOM.md. The old €286 estimate
here was built from placeholder prices and is superseded.
10. Build order
Production: ASSEMBLY.md (stations S0-S11, cycle times, workshop layout, prototype appendix). Mould making and casting parameters: MECHANICAL_REVB.md §10 and drawings sheet 6. Order of the first prototype round: board (JLCPCB) -> S1/S2 -> print cores, hatch, plungers and 1 master -> 1-cavity silicone mould -> dry-fit cell + hatch -> S4 -> cast -> hatch skin -> S8/S9 -> measure trip force, Qi on 3 pucks, LED visibility, drop, cell swap time.
11. Files
- Scripts:
lemon_geom.py(parameters),build_lemon.py,build_cradle.py,make_drawings.py. - Models:
lemon_totem.blend(exploded view frames 1-48, silicone mould set in a hidden collection),lemon_cradle.blend,lemon_assembly.glb. - Checks:
fit_report.txt,mech_results.json,placements.json. - Print:
stl_core_top_half,stl_core_bottom_half,stl_hatch_plate,stl_squeeze_plunger,stl_cradle_leaf,stl_cradle_cover. - Mould tooling:
stl_master_lemon_skin,stl_mould_parting_board,stl_mould_box_frame,stl_mould_hatch_plug. - Reference:
stl_skin_reference,stl_hatch_skin_reference,stl_pcb_outline. - Renders:
render_assembled,render_peel_closeup,render_exploded,render_cutaway,render_bottom_qi_face,render_battery_swap,render_mold,render_cradle*. - Drawings:
drawings/lemon_drawings.pdf(10 sheets + PNGs), DXF outlines (body, PCB, carrier, hatch outline, hatch opening, cradle leaf). - Sealed variant:
variant_sealed/. - Electronics / other streams:
BOM.md,NETLIST.md,ROUTING.md,lemon_totem.kicad_sch,component_photos/,production/*.md.