Pip-Boy 3000 / sensor systems
End-to-end radiation sensing
and $GM telemetry
A live engineering guide to the Pip-Boy 3000 Geiger/GNSS mod board, covering every stage of the instrument, from gas discharge physics to the text on the Pip-Boy screen.
System Overview and Operating Principle
An SBM-21 Geiger–Müller tube sits at ≈400 V,
a bias the board manufactures from its own 3.3 V rail with an ESP32-driven boost stage and a
one-stage voltage peak rectifier, closed-loop regulated through a 30 MΩ:Rfb divider into the
ESP32-S3's ADC. When an ionizing particle crosses the tube, a Townsend avalanche, propagated along
the whole anode wire by UV photons, dumps roughly a nanocoulomb through the tube; the cathode
pickoff resistor turns that current into a positive 2–3 V pulse, which is AC-differentiated,
clamped, and compared against 0.51 V by a
TLV3201 to make one clean 3.3 V logic blip per event. The ESP32-S3 counts those blips in the PCNT
hardware counter without interrupts or missed bursts, while a u-blox GNSS receiver supplies
navigation data over I2C on XIAO D4/GPIO5 (SDA) and D5/GPIO6 (SCL). Once per second the firmware fuses count rate, an
estimated dose rate, HV health, and the GNSS fix into one checksummed
$GM sentence over USB-C, TTL UART, or BLE. The Pip-Boy 3000 Mk V (Espruino on an
STM32) parses that sentence in a fixed buffer and draws a rad readout. This end result also explains
why this document keeps repeating that a 21 mm tube with a
≈3 CPM background is a
toy that teaches real physics, not a radiation-safety instrument.
Functional Architecture and Signal Path
Nine domain changes between a β particle and a line of ASCII. Click any block to enter its chapter; the dots move at the master field rate.
FIG 0.1 Live signal chain
SBM-21 Detector Physics
The SBM-21 is a coaxial cylindrical counter: a thin anode wire of radius a ≈ 50 µm on the axis of a cathode of inner radius b ≈ 2.5 mm, filled with a noble gas plus a halogen quench fraction (Br₂/Cl₂) at ~100 torr. The field of that geometry is the whole story:
A charged particle crossing the gas leaves a track of primary ion pairs, on the order of tens to a few hundred pairs, essentially anywhere in the volume, almost all of it in the weak-field region. The electrons drift inward. Nothing multiplies until an electron falls inside the critical radius where the field exceeds the multiplication threshold E_c ≈ 10⁶ V/m:
Inside that skin, impact ionization runs away. The first Townsend coefficient, ionizations per electron per unit path, follows the semi-empirical form α/p = A exp(−Bp/E), so the multiplication per avalanche is M = exp(∫ α dr) over the last ~50 µm of fall. In a proportional counter the avalanche stays local and M is finite, so pulse height tracks primary ionization. The Geiger regime is different in kind, not degree: the avalanche is so dense that de-exciting gas atoms emit UV photons energetic enough to photoionize elsewhere. Those photons are not confined, they travel along the tube and seed new avalanches at other points on the wire, which seed more photons, until the entire anode length is sheathed in discharge. That is why a GM pulse's amplitude is independent of whether the particle made 5 primary pairs or 500: the tube is a trigger, not a spectrometer, and all energy information is destroyed by design.
The discharge terminates itself. Electrons are collected in nanoseconds, but the positive ions, ~1000× slower, remain as a space-charge sheath around the wire. That sheath screens the field below the multiplication threshold, and the avalanche starves. The tube is now blind: until the ion sheath drifts out to the cathode and the field recovers, a new particle produces no pulse (dead time), then undersized pulses (recovery time, the distinction between the two is whether the comparator threshold catches the runt pulse, which is why resolving time depends on your electronics, not just the tube). The transit sets the physics floor on the dead time:
The counter follows the non-paralyzable dead-time model. The measured rate can be corrected with Ntrue = Nobs/(1 − Nobsτd); at τd = 100 µs the correction reaches 1% at 6,000 CPM and 10% at 60,000 CPM. At background it is negligible. Sandbox 2 keeps the ion-clearing floor and the electronic RaC recovery separate while plotting the loss.
Quenching: without help, ions arriving at the cathode would liberate secondary electrons and re-trigger the tube forever. Halogen molecules charge-exchange with the noble-gas ions (their ionization potential is lower), and the halogen ion dissipates its neutralization energy by dissociating, Br₂ → 2Br, rather than ejecting an electron. The atoms later recombine, so the quench agent is not consumed. Organic-quenched tubes (ethanol, etc.) crack their quencher permanently and die after ~10⁹ counts; halogen tubes self-heal, which is why a 1970s SBM-21 still works and why its plateau tolerates being run a little hot.
FIG 1.1 Inside one avalanche, cross-section and along the wire (signature visualization, scrub it)
The SBM-21 was chosen for one reason, its provisional 21 mm envelope targets the available space, and it pays for that in sensitive volume. Computing it (all dimensions estimates for surplus tubes):
γ detection in any GM tube is mostly indirect: the photon interacts in the cathode wall and the ejected electron ionizes the gas. So sensitivity scales with cathode area and wall Z, and the tube's energy response is far from flat, over-reading soft γ by 2–5× relative to ¹³⁷Cs unless energy-compensated with a filter sleeve. A bare SBM-21 therefore measures exposure poorly: the ≈5 CPM/µSv·h⁻¹ factor is an order-of-magnitude anchor for ¹³⁷Cs geometry, and every µSv/h this document displays inherits that estimate. This is the first of the places where "hobby instrument, not a dosimeter" is a technical statement, not a disclaimer.
Design sensitivity and failure modesSANDBOX 1 Bias point vs the plateau
Mid-plateau (390–440 V here) the count rate changes by the plateau slope, a few percent per 100 V, so a supply that wanders ±10 V costs well under 1% in rate. Below the starting voltage (≈360 V) avalanches fail to become Geiger discharges and efficiency collapses; above the plateau knee, spontaneous avalanches from field emission and imperfect quenching make the tube count itself, and past ≈470 V it latches into continuous discharge, which shortens tube life in seconds-to-minutes, not hours. This is why the regulation loop in chapter 3 exists at all.
SANDBOX 9 Tube swap, what changes everywhere else
Surplus-tube dimensions, plateau limits, and sensitivity vary by suffix, lot, and source. The swap model therefore treats cross-tube values as comparative estimates; use the datasheet for the exact part in hand before changing the enclosure, bias, or calibration.
Oscilloscope verificationTP1 (anode, 1:100 HV probe only): a flat 400 V rail that dips ~50–100 V at each event and recovers with τ = Ra·C ≈ 25 µs. The dip amplitude is set by the discharge charge against the node capacitance (ΔV = Q/C ≈ 0.4 nC/5.3 pF ≈ 75 V, a lumped-C simplification that holds until you add probe capacitance, which is itself ~15 pF and visibly slows the recovery: the act of probing TP1 changes TP1). Open the scope dock and select TP1 while the master field is set hot.
High-Voltage Power Conversion
The boost stage is an energy shuttle. While Q1 conducts, the inductor integrates current from the 3.3 V rail (v = L di/dt); when Q1 opens, that current has nowhere to go and the collapsing field drives the switch node as high as the load network allows, the flyback. Per cycle:
The C2/D1/D2 network is an AC-coupled peak rectifier. A switch waveform from 0 to 200 V produces approximately 199 V in the topology check; it does not produce 400 V. Q1 must withstand the full output voltage plus overshoot. The revised board uses a 600 V transistor. The converter still needs transistor switching, thermal, leakage and regulation measurements before assembly release.
FIG 2.1 Energy flow, boost, flyback, charge pump
Q1: Nexperia PBHV8560Z, 600 V, SOT223. Pin 1 is base, pin 2 and the tab are collector, and pin 3 is emitter. Replace the purchased MMBTA42; its 300 V rating is insufficient. RB1 is now 100 Ω, with a new 100 kΩ base-emitter pull-down RBE1. Nominal base drive is about 24 mA during a pulse; GPIO drive strength and turn-off storage require measurement. The ideal-switch feasibility model uses 5 kHz and a maximum 70 µs pulse, with cycle skipping near 400 V. Those are model inputs, not validated firmware settings.
D1/D2: purchased Vishay US1M-E3/61T, 1 kV. Reverse recovery, junction capacitance and hot leakage materially affect this low-current converter. The 1 mH SRR1260-102K remains fitted. At 3.3 V and 70 µs, the ideal ramp is 231 mA; the feasibility model including winding resistance reaches about 215 mA. Verify peak current, saturation margin and Q1 switching loss on the prototype.
Design sensitivity and failure modesSANDBOX 6 The HV loop's operating envelope
Ripple at this load is not the classic sawtooth: between counts the rail droops only through the 30 MΩ divider (τ = 141 ms), and each Geiger discharge removes ΔV = Q/Chv = 0.4 nC/4.7 nF ≈ 0.09 V. The supply is a trickle charger topping up a bucket, and the loop's real job is rejecting temperature and battery-voltage drift, not load transients.
TP5 (switch node, 1:100 only): flat near 0 V during ton, then the flyback edge, a peak above 400 V with L·Cpar ringing at a few MHz and decaying over ~1 µs, repeating at fpwm. TP8: 400 V DC with the load-dependent sawtooth of the burst regulator. If TP5's peak flattens and the input current jumps, you are watching L1 saturate.
High-Voltage Regulation and Control
The divider is 30 MΩ over 68 kΩ: 400 V produces 0.905 V and draws 13.3 µA (5.32 mW). C4 is now placed beside the XIAO ADC pad. Its 10 nF value gives about 0.68 ms filtering with the divider source resistance. Use calibrated ADC attenuation that can also measure an overvoltage, rather than saturating near the normal operating point. The control loop and sampling timing require hardware validation.
Design rationale and component selectionFirmware-controlled high voltage needs explicit fault handling: start with PWM disabled; use bounded, finite pulse bursts; stop on stale or implausible ADC data, excessive voltage, missing output rise, or watchdog reset. A maximum duty cycle alone is not overvoltage protection. This PCB has no independent hardware overvoltage shutdown. The animations below are qualitative teaching models and must not be copied as control firmware or used to justify a component rating.
Design sensitivity and failure modesSANDBOX 7 Feedback divider, ADC codes, and the stall test
At 400 V, the 30 MΩ : 68 kΩ divider produces 0.905 V ; select calibrated ADC attenuation with headroom for overvoltage detection. A 10 nF reservoir at the ADC pin supplies the SAR sampling capacitor without loading the divider.
TP6: ≈0.905 V DC with small ripple. Without the 10 nF reservoir, each ADC acquisition charge-shares with the SAR sampling capacitor and yanks the high-impedance node down before it recovers through ~68 kΩ, a comb of droop spikes at the loop tick rate. With the reservoir fitted, the acquisition transients stay local and the ADC measures the divider instead of its own history.
Pulse Pickoff and Signal Conditioning
During the discharge, the same current flows through the anode load Ra (from HV+) and the cathode resistor Rk (to ground). Picking off at the cathode is the trick that makes a single-supply readout clean: the node idles at exactly 0 V, and the event appears as a positive-going pulse of V = i(t)·Rk, Rk is a transimpedance, ~2–3 V peak for the ~0.4 nC discharge. Anode pickoff (Rev A) gives the same information riding on 400 V with awkward polarity, workable, but every downstream choice fights it.
Cc(Rk+Rs) is a high-pass, equivalently, a differentiator for signals slower than its corner:
Only the fast leading edge crosses; the slow tail is differentiated away and Rs restores the baseline to 0 V. Because the coupled waveform must integrate to zero, every pulse is followed by an equal-area undershoot, small at 9.4 µs, but grow τ and the undershoot of one pulse starts eating the amplitude of the next (pile-up); shrink τ and you shave the pulse amplitude itself. The parasitic capacitance on the sense node (trace + clamp + comparator input, ~10 pF) is the real limit: it forms a capacitive divider with Cc, which is why Cc can't just be made tiny "for speed."
The BAT54S clamp earns its place at the extremes: the first edge of a large event can punch past the rails, and the TLV3201's absolute-maximum input is Vcc+0.3 V. A Schottky's ~0.3 V forward drop clamps inside that window where a silicon diode's 0.7 V would not, the forward voltage is the specification here, not an incidental property.
Design rationale and component selectionRa = 4.5 MΩ (three CRCW12061M50FKEA 1206 resistors in series, about 133 V each at 400 V versus a 200 V working rating) limits the avalanche current so the space-charge quench can win: too small and the supply refeeds the discharge into continuous glow; too large and the Ra·C recovery stretches past the ion-clearing time and becomes the dead time. Rk = 100 kΩ is a compromise between pulse amplitude and the recovery it adds on the cathode side. τhp = 9.4 µs remains below the 25 µs anode recovery, long enough to preserve the leading edge and short enough that consecutive resolvable pulses don't stack.
Design sensitivity and failure modesSANDBOX 2 Ra, node capacitance, dead time, and the counts you lose
SANDBOX 3 Cc × Rs, the shaping network
TP2 (1:10): the raw positive cathode pulse, 2–3 V, decaying over tens of µs. TP3 (1:10): the same event after Cc, a sharp ~2 V spike collapsing with τ = 9.4 µs and a shallow undershoot. If TP3 shows a comb of narrow spikes with no tube events, that is the switcher coupling into the sense node, chapter 5's threshold and chapter 7's layout are the two defenses.
Comparator Thresholding and Pulse Qualification
The comparator is the analog/digital firewall: on one side, a continuous voltage carrying tube physics, switcher spikes, and thermal noise; on the other, edges that either exist or don't. The threshold divider sets the decision boundary, and the hysteresis network makes it two boundaries:
When the output snaps high, Rh lifts the sense node's baseline by 0.30 V, so the comparator holds until the shaped pulse has decayed well below where it tripped. Two consequences: the output cannot chatter on the pulse's noisy falling edge (one particle, one edge, the PCNT requirement), and the output width is stretched to ~20 µs, comfortably above any glitch-filter setting. The width is not a datasheet number, it falls out of τhp and the hysteresis, which is why sandboxes 3 and 5 both move it.
Design rationale and component selectionThe TLV3201's push-pull rail-to-rail output drives the PCNT pin directly, no pull-up, and 40 ns propagation instead of the LM393's µs-class, open-drain rise through 10 k. At GM count rates the speed is irrelevant; the edge quality is not, because PCNT counts edges, and a slow RC rise through a noisy region can double-count without hysteresis. Vth at 0.51 V sits ~4× above the shaped switcher residue and ~4× below the pulse peak, the point of maximum separation between the two distributions, which the ROC below makes visible.
Design sensitivity and failure modesSANDBOX 4 Threshold vs noise, the operating characteristic
SANDBOX 5 Hysteresis, and what zero looks like
TP4 (1:10): idle low, one clean 3.3 V pulse of ~10 µs per event. Trigger on TP3 and overlay TP4 to see the trip point and the hysteresis-held release directly, that overlay is the waveform river below, automated.
FIG 5.1 One particle, end to end, TP1 → TP4
Event Counting and Rate Estimation
The PCNT peripheral is a hardware edge counter clocked from the APB bus: the comparator's rising edge increments a 16-bit register with zero CPU involvement and zero per-event latency. The alternative, a GPIO interrupt, costs ~2–4 µs of ISR entry/exit per event and, worse, serializes events through the interrupt controller: two pulses inside the ISR window merge into one. At background rates either works; in a burst (bringing a check source close, or the tube's own double-pulsing when mis-biased) the interrupt path undercounts precisely when the reading matters. The PCNT glitch filter rejects edges shorter than N APB cycles (up to 1023 × 12.5 ns ≈ 12.8 µs), set it to ~1 µs: an order below the 10 µs comparator pulse, an order above coupled switcher spikes.
Pin and peripheral allocationThe count line lands on D3/GPIO4 because D2/GPIO3 is a strapping pin, sampled at reset to select boot behavior; a comparator that happens to hold it during a brownout reboot bricks the boot in a way that looks exactly like a firmware bug. And HV feedback must use an ADC1 channel (A1/GPIO2): ADC2 is arbitrated against the Wi-Fi radio's internal use and returns garbage or errors while the radio is active, a design that works on the bench and fails the moment telemetry is enabled, which is the most expensive kind of bug to find.
Design sensitivity and failure modesMove the count line to an interrupt: nothing visibly changes until the day it does, a burst undercounts, silently, with no error path. Skip the glitch filter: every switcher spike that survives layout becomes a count, and your "background" tracks duty cycle. Put feedback on ADC2: HV regulation dies when Wi-Fi transmits, the duty clamp catches it, and the rail sags, visible as hv_ok dropping in the $GM stream exactly during uploads.
Oscilloscope verificationNothing, that is the point. After TP4 the signal exists as register state. The observable is the $GM sentence's CPM field, and the bring-up check is statistical (chapter 10), not oscillographic.
GNSS Integration and RF Compatibility
The GNSS receiver does all the satellite work and exposes its navigation stream through the MAX-M10S I2C-compatible DDC interface at 7-bit address 0x42. XIAO D4/GPIO5 is SDA and D5/GPIO6 is SCL; D6/GPIO43 and D7/GPIO44 remain available for the external host UART. Firmware reads the GNSS UTC and fix data over I2C and timestamps count bins from the ESP32's local timer. The threat runs the other way: the HV switcher is a deliberate generator of fast edges, and a GNSS front-end is a −130 dBm receiver.
Near-field coupling, not far-field radiation, is the mechanism at these sizes. The switch node swings ~400 V in an illustrative 100 ns edge, dV/dt ≈ 4 kV/µs, and couples capacitively (E-field): every pF between the node and a victim trace injects i = C·dV/dt ≈ 4 mA per pF. The input-current loop (C8/C9→L1→Q1→ground) carries a pulsed current of approximately 215 mA in the feasibility model and couples inductively (H-field) as a magnetic dipole whose near field falls as 1/r³, the loop's enclosed area is the knob you control in layout. The harmonics of a 5 kHz square edge extend into the tens of MHz, and while none of that reaches 1.5 GHz directly, it desensitizes the receiver through its LNA bias and the antenna's ground reference. Distance beats shielding at these geometries: the far corner of a 66 mm board buys ~(4×)³ ≈ 60× less H-field than adjacent placement, and the external u.FL antenna moves the aperture off the board entirely.
FIG 7.1 Near-field overlay on the Rev D floorplan, drag the GNSS section
Compact, reversible tube mounting: PocketMagic specifies a 16 × 6 mm can, 5 × 2 mm pin and 21 mm overall length. The Littelfuse 01000058Z body clip is 7.11 mm long and 5.84 mm tall. The Keystone 6049 pin socket is 5.283 mm long and 5.156 mm tall. MC1 and MC2 have identical footprints: four plated holes in a cross, 5.08 mm between opposite holes, 1.4 mm finished drill and 2.2 mm solder lands. The clip uses the transverse pair; the socket uses the axial pair. Populate one contact per end.
To reverse the tube: swap the clip and socket between the two ends, rotate each 180°, and change the two JP1 links on the underside. Always use the numbered pads; the back view is mirrored. The cathode can connects to the pulse pickoff, not ground.
| Assembly | MC1 · left | MC2 · right | JP1 links |
|---|---|---|---|
| Default · shown | Body clip · cathode − | Pin socket · anode + | 1–3 and 2–4 |
| Reversed | Pin socket · anode + | Body clip · cathode − | 1–2 and 3–4 |
Disconnect power and discharge before changing parts or links. Remove both old links first; fit exactly one configuration and check electrode continuity before powering up. Sample fit is still required: the clip is specified for 6.3/6.35 mm fuses; the socket for a nominal 2.03 mm pin. Check grip on the 6 mm can, actual pin diameter, engagement, seating height and assembled insulation in both orientations. Dimensions and assembly notes · Contact BOM · Mounting data.
FIG 7.2 GC1 Rev D authoritative placement, 66 × 44 mm
Drag to orbit, scroll to zoom, and select a package to open its floating inspector. Explosion mode moves every package away so the bare board can be inspected; trace X-ray shows the current generated KiCad tracks and vias. Hover — or tap, on touch — any track or via to read its net, the pins it runs between, and the voltage and current that node is expected to carry; none of it is measured. Top and back views are explicit—the back is the physically expected left/right reversal. The XIAO geometry is triangulated from Seeed Studio’s official STEP assembly; the tube follows published nominal dimensions; MC1 is the solder-in Littelfuse 01000058Z body clip and MC2 is the red Keystone 6049 pin socket. Their solder holes follow the PCB; jaw curvature, socket engagement and seating height remain sample-fit assumptions.
Measuring assembled geometry…
Rev D sourcing BOM · purchased parts and review changes
Distributor data read from digikey.com (US catalogue, USD, cut-tape tier) on 2 August 2026, plus one surplus seller for the tube. Stock and price move daily — this is an as-checked snapshot, not a quote. Some lines cannot be bought as specified; each one carries a substitute below.
| Ref · qty | Function | Order this | Availability | Unit | Ext |
|---|
Copy DigiKey list puts part-number,quantity lines on the clipboard for DigiKey's bulk-add box; both Keystone contacts are included; the surplus tube and items without a DigiKey part number are excluded. A substitution changes nothing about the blocking gates — the hot-leakage measurement on D1/D2, the SOA and storage-time test on Q1, and the residual-clamp test on ESD1 all still apply, and D1/D2 in particular now need re-running against a different manufacturer's die.
Swap the GNSS to the HV corner and fixes drop every time hv_ok is true. Route the sense node past the switcher and sandbox 4's false-count axis stops being hypothetical. Pour ground under the flyback node and the added ~pF slows the edge (good) while coupling it straight into the plane (bad), the reason the pour is pulled back there.
Oscilloscope verificationTP9 SDA and TP10 SCL (both 1:10): idle high; SDA falls while SCL is high for START, the 0x42 address shifts MSB-first, and the receiver holds SDA low for ACK on the ninth clock. At 400 kHz SCL is 2.5 µs per cycle. Confirm clean 3.3 V logic levels and measure the rising edges; RPU1/RPU2 now provide 4.7 kΩ pull-ups to 3.3 V. Their rise time with the assembled bus capacitance must meet the selected I2C timing mode. The RF problem is not directly visible on an ordinary probe trace; quantify it by comparing MAX-M10S C/N₀, time-to-first-fix, and fix retention with the HV converter disabled and enabled.
Host Telemetry Interface
UART framing is voltage-level time-division: idle high; a start bit (low) arms the receiver's
mid-bit sampler; eight data bits LSB-first; a stop bit (high) guarantees a re-armable edge. At
115200 baud one bit is 1/115200 = 8.68 µs, and both ends'
oscillators must agree within ~2% over ten bits, trivial for crystals, the classic failure for
RC-oscillator MCUs. The same bytes over USB-CDC ride a 12 Mbit/s packet protocol where framing
is the host controller's problem; over BLE they ride notifications with ~30–100 ms latency.
The sentence is NMEA-shaped on purpose: off-the-shelf GPS software will pass $GM
through unharmed, and the raw $GNGGA can be forwarded alongside so the host sees a
normal receiver plus one extra sentence type.
| J2 pin | GC1 net | Harness connection |
|---|---|---|
| 1 | GND | Common ground to the verified Pip-Boy ground. |
| 2 | HOST_UART_TX | XIAO D6/GPIO43 output through R5 100 Ω to the verified Pip-Boy RX. |
| 3 | HOST_UART_RX | Verified Pip-Boy TX through R6 100 Ω to HOST_UART_RX_RAW and XIAO D7/GPIO44/MOD1.8. |
SANDBOX 10 Sentence builder, checksum computed digit by digit
FIG 8.1 TP7, the bits on the wire
One fused line per second, produced by the ESP32 as the hub, the tube on a counter pin, the GNSS on I2C, one merged stream out over the dedicated host UART. The XOR checksum is weak as error detection (single-bit errors only, no burst protection) but it is free, it is what NMEA parsers already speak, and on a 15 cm board-to-board wire the error model is "connector fell off," not bit rot. The µSv/h field carries the calibration estimate with it, any consumer of this stream inherits the ±order-of-magnitude uncertainty of chapter 1, which is why hv_ok and raw CPM are transmitted too: CPM is a measurement, µSv/h is an interpretation.
Design sensitivity and failure modesRaise the report rate to 10 Hz and each frame carries 0.005 counts at background, pure
Poisson noise. Drop the checksum and the Pip-Boy renders garbage the first time the connector
half-seats. Use String.split(',') in the parser and the Espruino heap fragments.
Chapter 9 is built around avoiding that failure.
TP7 (1:10): idle high, 8N1 frames at 8.68 µs/bit in once-per-second bursts ~5 ms long. FIG 8.1 is that trace, decoded.
Pip-Boy Display Integration
Target: The Wand Company Pip-Boy 3000, Espruino on STM32F4. Everything below respects the Espruino subset, no ES modules, no async/await, no template literals, and treats JsVar memory as the binding constraint.
| A · Hardware UART | B · USB CDC | C · BLE bridge | |
|---|---|---|---|
| Path | Board UART TX → a spare STM32 USART RX and board UART RX ← the paired STM32 USART TX inside the case. 3.3 V both sides, no level shifter. Common ground is required; each signal has a 100 Ω series resistor. The RX path is electrical only until matching GC1 firmware exists. | ESP32-S3 is a USB device. The Pip-Boy's port is also a device port, there is no USB host stack on either end, so this path does not work Pip-Boy-to-board. USB-C remains the bench/service link to a PC. | ESP32 advertises a Nordic-UART-style service. This needs the Pip-Boy firmware to expose a BLE central API to apps, not established; treat as feature-detect-first. |
| Latency | µs, bit-true | Not applicable | 30–100 ms + connection drops |
| Power | ~nothing | Not applicable | +~10 mA average on the board side |
| Verified? | Pin choice is NOT verified. Firmware aliases may already belong to stock peripherals, not a broken-out user UART. Find a free USART pair on the exact unit with continuity, the REPL, and a scope before wiring anything. | Limitation verified by architecture (device↔device). | Unverified, check whether your firmware build exposes NRF/Bluetooth objects at the REPL:
typeof NRF. |
$GM,… line arrives at the interpreter and
is evaluated as JavaScript, a stream of syntax errors at 1 Hz, and a security hole
besides. Confirm the active port with E.getConsole() before trusting it. The app below
never moves or disables the console: it refuses to claim Serial1 when the console is
there, and renders SERIAL1 CONSOLE BUSY instead.// $GM emitter, call once per second from the fuse/report task.
// PIPBOY_MODE trims the frame for a memory-poor parser: integer cpm,
// dose in nSv/h (integer), single flags field. Same checksum scheme.
bool PIPBOY_MODE = false;
static uint8_t nmea_ck(const char *s) { // XOR between '$' and '*'
uint8_t ck = 0;
while (*s) ck ^= (uint8_t)*s++;
return ck;
}
void emit_reading(uint32_t cpm, float usvph, bool hv_ok,
double lat, double lon, int alt_m,
int fix, int sats, uint32_t utc_hhmmss) {
char body[96];
if (PIPBOY_MODE) {
// $GP,<cpm>,<nsvph>,<flags> flags bit0=hv_ok bit1=fix
snprintf(body, sizeof body, "GP,%lu,%lu,%d",
(unsigned long)cpm, (unsigned long)(usvph * 1000.0f + 0.5f),
(hv_ok ? 1 : 0) | (fix ? 2 : 0));
} else if (fix) {
snprintf(body, sizeof body, "GM,%lu,%.2f,%d,%.4f,%.4f,%d,%d,%d,%06lu",
(unsigned long)cpm, usvph, hv_ok, lat, lon,
alt_m, fix, sats, (unsigned long)utc_hhmmss);
} else { // empty position fields, per spec
snprintf(body, sizeof body, "GM,%lu,%.2f,%d,,,,0,0,%06lu",
(unsigned long)cpm, usvph, hv_ok, (unsigned long)utc_hhmmss);
}
char line[112];
snprintf(line, sizeof line, "$%s*%02X\r\n", body, nmea_ck(body));
Serial.print(line); // USB-C CDC (bench)
Serial0.print(line); // UART0 TTL, 115200 8N1 → Pip-Boy
}
Design decisions, because they are the memory discipline: the NMEA parser is a fixed
Uint8Array(96) filled in place, no split(','), no substring garbage; a
number parser walks bytes and accumulates arithmetic instead of allocating strings; history is a
pre-sized Float32Array ring; every listener and interval handle is kept so teardown
actually detaches. Pins and calibration live in USER/PIPGEIG.JSON; an absent or invalid
RX pin boots to an explicit setup screen rather than pretending to know the hardware.
Loading readable device source…
{
"id": "pipgeig",
"name": "RobCo Field Dosimeter",
"version": "1.00",
"files": "APPS/PIPGEIG.JS,APPINFO/PIPGEIG.info",
"src": "APPS/PIPGEIG.JS",
"icon": "APPINFO/HOLO.IMG"
}
| Field | Meaning | Example | Notes |
|---|---|---|---|
| $GM | sentence type | $GM | constant |
| cpm | counts per minute | 42 | integer · the measurement |
| usvph | dose estimate, µSv/h | 0.31 | estimate, cpm ÷ ~5, uncalibrated |
| hv_ok | HV in regulation | 1 | |V−V_t| < tolerance |
| lat, lon | degrees, WGS-84 | 43.0481,-76.1474 | empty when fix = 0 |
| alt_m | altitude, m | 152 | empty when fix = 0 |
| fix, sats | fix flag, satellites | 1, 9 | 0/1 · count used |
| utc | hhmmss from GNSS | 174532 | UTC, not local |
| *ck | XOR of chars between $ and * | *15 | two uppercase hex digits. Worked: XOR over “GM,42,0.31,1,43.0481,-76.1474,152,1,9,174532” = 0x15; verify with the builder in chapter 8. |
DEMO 9.1 RobCo field dosimeter — parser, controls, and calibration
Feed a valid or corrupted
$GM frame, then use both simulated knobs. K2 changes RADS / TREND / NAV; K1
selects and opens the calibration flow. Bad checksums retain the last-good instrument state.
Connect the detector to the Pip-Boy
The app never guesses a pin and never steals the Espruino console. Back up the SD card, then validate the physical interface on your exact unit before applying power.
- With both devices powered off, verify a free Pip-Boy USART RX pin and common ground by
continuity and against the firmware. At the REPL, confirm
E.getConsole()is notSerial1. The example A10/A9 pair is not a wiring recommendation. - Connect Geiger-board TX → verified Pip-Boy RX, RX ← verified Pip-Boy TX, and GND → GND. Both sides must be 3.3 V logic. Rev D provides the reverse electrical path, but it is not a command channel unless corresponding GC1 firmware is implemented.
- Create
USER/PIPGEIG.JSONon the Pip-Boy SD card with the verified pin names, for example{"rx":"A10","tx":""}. Copy the app through the mounted card or a USB-C Web Serial file installer; do not flash firmware or use “Send to Espruino.” - Reboot, open the app under ITEMS › MISC, and confirm LINK 115200, changing CPM, HV status, and checksum acceptance. Open CAL with Knob 1 to capture local background, enter the measured tube factor, set the CPM alert, and save.
Download the deployable app · Readable source · Wiring, controls, and configuration notes · Official Pip-Boy 3000 app guide · Official Pip API
Commissioning, Calibration, and Safety
- Disconnect USB and every other power source. Wait for the high-voltage rail to discharge, then use an HV-rated meter to verify TP8 is below 5 V before touching or installing the board.
- Check MC1/MC2 contact seating and JP1 polarity links: default 1–3 / 2–4; reversed 1–2 / 3–4. The can must reach GM_CATHODE and the isolated pin GM_ANODE. Remove both old links before changing orientation; verify there is no anode-to-cathode short.
- Insulate the complete PCB from the enclosure and all metalwork. Secure all three 2.2 mm M2 holes with suitable insulating hardware while preserving the shown washer and copper keepout envelopes.
- Build J2 with pin 1 GND, pin 2 GC1 TX to verified host RX, and pin 3 host TX to GC1 RX. Cross TX/RX, provide common ground, and use 3.3 V logic only; never apply 5 V.
- Route the cable clear of the approximately 400 V section, tube contacts, RF antenna and u.FL cable, the lower-edge J2 opening, and all mounting washers. Do not touch HV copper, tube contacts, or components while energized.
- Treat Pip-Boy pin assignments, enclosure fit, calibration, and RX command support as unverified. RX requires matching GC1 firmware. This is engineering hardware, not a certified dosimeter.
- Use a current-limited bench supply for first power-up. Confirm the ESP32 answers
?IDwith PWM held at zero duty and no HV generated. - 3V3 present at the comparator and GNSS. Still no HV.
- TP5 with the 1:100 probe: switching present, flyback peak < 260 V, ringing decays in ~1 µs. This is where Q1's 300 V margin is verified or the snubber gets added (chapter 2).
- TP8, 1:100: rail regulates at the target ±2%. The right half of the board is now live whenever powered.
- TP6, 1:1: 0.905 V with the 68 kΩ feedback resistor,
hv_ok= 1. - TP2 → TP3 → TP4 (1:10): background gives a pulse every ~20 s, patience or a check source (thoriated lantern mantle, uranium glass) speeds this up.
- CPM rises with the source near, falls when removed; PCNT total matches a hand count of TP4 edges over a minute.
- TP9 SDA and TP10 SCL with sky view: address
0x42is acknowledged, logic levels and rise time meet the selected I2C mode, and?FIXeventually reports. - One valid
$GMper second at TP7/USB, checksum verified by an independent parser, the demo above counts.
SANDBOX 8 Poisson counting, how long until you can trust a number
Calibration procedure, in the order that respects the statistics: (1) log background for ≥1 hour; at 3 CPM that is ~180 counts, ±7.5%; (2) with a reference source or a borrowed calibrated meter, log ≥300 counts at a fixed geometry and solve CPM_PER_USVPH = (CPM−CPMbg)/rate; (3) quote dose only from the smoothed average, and only for the γ energy you calibrated at, the SBM-21's response is not flat and uncompensated (chapter 1). Without step 2 the µSv/h field is a relative indicator. This is the second place "not a dosimeter" is engineering, not liability language.
Electrical and radiation safetyDesign notes, visual system, interaction, physics, open questions
Visual system. The page follows the theme-aware Ferox Works product language and the
catalog's persisted af-theme preference. Dark mode uses Paper #0E1013 · Surface
#15191E · Ink #F0F3F7 · Cobalt #78A2FF; light mode uses Paper #F5F3EE · Surface #FBFAF7 · Ink
#1B1F25 · Teal #176F74. Manrope carries interface and editorial text; IBM Plex Mono is reserved
for measurements, controls, and protocol data. Core derivations use semantic MathML and live
results use typographic mathematical HTML, both with a dedicated mathematics font stack, so
fractions, roots, subscripts, and superscripts read like mathematics rather than console output.
Cobalt/teal marks
selection and derived structure, amber marks estimates or cautions, phosphor is reserved for
live signals and valid readouts, and hazard red is reserved for unsafe operating states.
Interaction and legibility. Every simulation now exposes a visible reading guide that names its encodings before the user touches a control. Canvas labels receive a dark outline so traces, heat maps, and animated particles cannot wash them out. Below 720 px the plots retain a readable native drawing width inside a horizontal inspection rail instead of shrinking labels into illegibility. The PCB's TP1–TP17 pads are real pointer and keyboard targets; selecting one opens the shared scope, chooses the node, and automatically selects a 1:100 probe for HV points.
Architecture. One state object; set() patches it, recomputes all derived
quantities in a single pure derive(), and notifies subscribed widgets by dependency
key. One rAF loop drives two clocks: a wall clock for the 1 Hz stream/counters and a scaled
"micro" clock (÷ time-scale) for waveform physics; each canvas renders only while on-screen
(IntersectionObserver) and every animation owns a scrub. Waveforms at TP1–TP17 are closed-form
models parameterized by the live state, the scope, the river, and the sandboxes all draw from
the same functions, so a slider moved in chapter 4 changes chapter 5's traces because they are
literally the same equations.
Physics fidelity. Animation time is model time, not decorative velocity. Electron and ion motion in the cylindrical tube advance linearly in r² under the 1/r field; the UV propagation window is explicitly sub-microsecond; the HV startup plot clamps at the regulated setpoint; rail droop returns to, rather than past, the setpoint; the switch-node flyback interval follows the inductor-current collapse; and the UART renders exactly one start, eight data, and one stop bit. Global pause freezes both waveform and Poisson clocks. Scrubbing changes observation time only, never the governing equations.
Self-critique applied. Cut: a WebGL field shader (Canvas gradients carry the same information at a fraction of the budget); decorative particle trails in ch0; a 3D board mesh (CSS tilt + true-scale SVG says everything the geometry knows). Known simplifications, named in place: lumped tube capacitance, ideal diodes in the peak rectifier animation, Gaussian pulse-height model in the ROC. The counters use expected-rate statistics rather than micro-clock Poisson when the time scale is deep in slow motion, physically honest (at 10⁵× slow, a second lasts a day) but stated here so it isn't mistaken for a bug.
Open questions (each blocks a number from being promoted from estimate to specified): SBM-21 anode wire radius and fill pressure, resolve with a datasheet scan or a destructive teardown of a dead tube; datasheet dead time, measure via two-source method after build; ESP32-S3 effective C_sample, measure droop vs added reservoir C; Pip-Boy console port, free USART pins, BLE availability, APPINFO schema, resolve at the device REPL and against your app repos.
Queued next. Deeper α(E/p) with a plotted Townsend curve; per-pixel field rendering in the cutaway; full ROC with measured noise; Pip-Boy 3000 app hardware validation notes.