

Geiger entropy
A dedicated Bitcoin seed phrase generator that uses radioactive decay as an entropy source. Completely open source and designed to be simple, auditable, and built by anyone with readily available components.
- Size
- 53 × 63 mm
- Copper layers
- 2
- Thickness
- 1.6 mm
- Parts
- 37
- Nets
- 36
- Pads
- 131
- Tracks, vias
- 165, 138
- Pours
- 1
- Routed
- Yes
- KiCad
- KiCad 10.0 (file 20260206)
- Created
- 3 Sep 2026
- Updated
- 3 Nov 2026
Overview
Geiger entropy is an open-source printed circuit board by captainchapster, from the captainchapster/Entropy32-Plus repository on GitHub, published under the MIT licence. It is a 2-layer board of 53 × 63 mm with 37 parts on 36 nets, routed with 165 tracks and 138 vias.
It is built around the ATMEGA328P-AU and LM393. Its parts include 12 resistors, 10 capacitors, 3 ICs, 3 connectors and 3 switches. It belongs with the sensors and measurement boards and the AVR (ATmega, ATtiny) boards of this library.
Main components
2ATMEGA328P-AU
TQFP-32(7x7)-40℃~+85℃ 1.8V~5.5V 10bit 1KB 20MHz 23 2KB 32KB 8 Bit AVR Built-in FLASH TQFP-32(7x7) Microcontrollers (MCU/MPU/SOC) ROHSMicrochip TechnologyMicrocontrollers (MCU/MPU/SOC)LM393
SOP-8-16V~16V 0℃~+70℃ 2 2.5mA 250nA 2V~36V 36V 5mV 5nA Fail-safe function Open-drain SOP-8 Comparators ROHSMSKSEMIComparators
README
README.mdEntropy32 Plus

Entropy32 (aka The Universe Bifurcator) is a dedicated Bitcoin seed phrase generator that uses radioactive decay as its entropy source. It's completely open source and designed to be simple, auditable, and buildable by anyone with readily available components.
Why radioactive decay?
Pseudo-random number generators are deterministic — given the same seed, they always produce the same output. True randomness for something as consequential as a Bitcoin wallet's seed phrase should come from a physical process nobody can predict or manipulate. Radioactive decay is quantum-mechanically random: the timing of individual decay events is fundamentally unpredictable, making it an ideal entropy source for generating cryptographic secrets.
How it works
- A Geiger counter detects decay events from a radioactive source (or background) and outputs a pulse via a 3.5mm audio jack.

- An LM393 comparator IC then takes the 0-1.5V pulse and compares it to a bias of ~0.5V via a voltage divider such that if V>0.5V we get HIGH else LOW.

- The timing between events is captured and compared in non-overlapping pairs (each inter-arrival time feeds exactly one comparison, so adjacent output bits never share an input interval). Each resulting bit passes two continuous NIST SP 800-90B health tests (Repetition Count Test and Adaptive Proportion Test) before it's fed through SHA-256 conditioning to whiten the raw entropy and remove any bias — if either test trips, the device halts rather than generating a seed from a degraded source.
- The conditioned entropy is mapped to words from the standard BIP39 English wordlist.
- A simple button-driven, state-machine UI walks you through generating and displaying your seed phrase — entirely offline, with no wireless connectivity, no persistent storage of the seed, and no software dependencies beyond the device itself.
How long does it take to generate a seed?
The firmware always fills a 512-bit raw pool before conditioning — that's true whether you pick a 12-word or 24-word phrase, since the seed length only changes how many of the whitened bits get used afterward, not how much raw entropy gets collected. Because of the non-overlapping interval pairing described above, each output bit costs two Geiger pulses, so at a steady count rate the pool fills in roughly:
minutes ≈ 1024 / CPMThe device's collection screen shows live CPM and a running ETA using this same math, so you don't need to do it by hand. Actual CPM depends heavily on your specific tube, source activity, distance, and shielding — treat the table below as a rough guide, not a spec, and follow appropriate safety/legal practices for any check source you use:
| Source | Typical CPM | Time to fill the pool |
|---|---|---|
| Ambient background | ~15–25 CPM | ~40–70 min |
| Uranium glass ("vaseline glass") | ~50–100 CPM | ~10–20 min |
| Thoriated tungsten welding rod (2% ThO₂) | ~100–200 CPM | ~5–10 min |
| Am-241 foil (smoke detector ionization chamber) | ~300–800 CPM | ~1.3–3.5 min |
| Low-activity calibration/check source | ~1,000–3,000 CPM | ~20 sec–1 min |
| High-activity check source (close contact) | ~5,000–10,000+ CPM | ~6–12 sec |
Hardware
- Custom PCB built around an ATmega328P microcontroller, designed in KiCad
- Geiger counter module as the entropy source (currently designed to take the 3.5mm output of the GQ Electronics GMC-320S)
- LM393 comparator IC
- Push-button interface for on-device operation
- Serveral common passives i.e. resistors, capacitors, and LEDs
- 16MHZ crystal oscillator
- 0.91" OLED display with I2C adaptor
- PJ320E 3.5mm audio jack
- USB-C receptacle 6 pin
- header pins
3D printed case
I have included a custom designed basic case which you can 3d print to protect your device. The design works by allowing the board to slide on tracks into the case, once inserted all the way, a small prong prevents the board from sliding back out. If you plan to use this case be sure to hold the board down while unplugging the USB and/or the 3.5mm audio cable as this would otherwise put excessive strain on the prong to the point it may break off and fail.

Repository contents
| File | Purpose |
|---|---|
entropy32_plus.ino | Main firmware — state machine, entropy capture, and UI logic |
sha256.h / sha256.cpp | SHA-256 implementation used to condition raw entropy |
bip39_wordlist.h | BIP39 English wordlist, compiled into firmware |
button.h | Button input handling |
logo_bitmap.h | Boot splash bitmap, compiled into firmware |
tools/english.txt | Source BIP39 wordlist |
tools/generate_wordlist.py | Script to regenerate bip39_wordlist.h from english.txt |
tools/generate_logo_bitmap.py | Script to regenerate logo_bitmap.h from a rendered image |
KiCad/... | Schematics, PCB and other files |
KiCad/production/... | Fabrication data: bom, zipped fab files, and positions etc |
enclosures/... | 3D print files for cases & enclosures |
Building it yourself
The Arduino sketch (entropy32_plus.ino) and its accompanying .h/.cpp files must remain in the same top-level folder for the Arduino IDE to compile correctly — it doesn't recurse into subfolders for sketch code. Open entropy32_plus.ino in the Arduino IDE, verify your board settings for the ATmega328P, and flash as normal.
Use the KiCad files to tailor the board to your liking before fabrication or use the premade production files to order your board and/or pick and place from your preferred fabrication plant i.e. JLCPCB or PCB Way etc.
Programming a bare ATmega328P
A freshly fabricated board has a blank ATmega328P — there's no bootloader on it and no USB-to-serial chip to flash it through, so the usual "select a port and hit upload" Arduino flow doesn't apply until it's been programmed once via the ICSP header exposed on the board.
- Wire up an ISP programmer. Either a second Arduino running the
ArduinoISPexample sketch, or a dedicated programmer such as a USBasp, connected to the ICSP header. If using a USBasp, make sure it's set to 5V logic to match this board. - Burn the bootloader. In the Arduino IDE, select Arduino Uno or Arduino Nano as the board type (either is a stand-in for a bare ATmega328P at this stage), select your ISP programmer under Tools → Programmer, and run Tools → Burn Bootloader. This also sets the fuses for running off the board's 16MHz external crystal rather than the internal oscillator.
- Flash the firmware. With the bootloader in place and the board running at 16MHz external clock, open
entropy32_plus.inoand upload it via Sketch → Upload Using Programmer (still through the same ISP connection — the board has no onboard USB-serial chip for a normal serial upload).
Flash footprint
Geiger pulse capture, the SP 800-90B runtime health tests, SHA-256 conditioning, the full BIP39 wordlist, the OLED driver, and the entire menu/boot UI all fit on the ATmega328P's 32KB of flash — with 486 bytes to spare:
Sketch uses 30234 bytes (98%) of program storage space. Maximum is 30720 bytes.
Global variables use 1061 bytes (51%) of dynamic memory, leaving 987 bytes for local variables. Maximum is 2048 bytes.Breakdown of where it all goes, pulled from the compiled .elf with avr-size/avr-nm:
| Symbol | Bytes | What it is |
|---|---|---|
BIP39_WORDLIST_BLOB | 13,117 | The official 2048-word BIP39 English wordlist |
main (loop + inlined UI code) | 3,062 | State machine, screen drawing, menu logic |
SHA256::transform | 2,278 | The SHA-256 compression function |
vfprintf | 948 | Pulled in by snprintf, used for CPM/ETA/progress formatting |
u8x8_font_5x7_r | 764 | Built-in OLED font glyph table |
| U8x8 / TwoWire internals | ~1,100 | I2C + SSD1306 driver plumbing |
LOGO_BITMAP | 384 | Boot splash bitmap |
K (SHA-256 round constants) | 256 | Fixed SHA-256 round constants |
The wordlist alone is 43% of the chip. Adafruit_GFX + Adafruit_SSD1306 don't fit alongside it — see the note in entropy32_plus.ino — which is why the firmware talks to the display through U8x8's text-only mode instead.
Validation
The firmware runs the two minimal continuous health tests NIST SP 800-90B requires of a noise source at runtime — a Repetition Count Test and an Adaptive Proportion Test (see runHealthChecks() in entropy32_plus.ino) — which will halt the device rather than generate a seed if the raw source looks stuck or degraded. Runtime health tests are not the same as validating the source's entropy.
Full-length entropy assessment (2,000,000 edges)
A 23-day capture (2026-10-03 to 2026-10-26) was logged with Entropy32 Recorder. The recorder is a separate board that timestamps the same post-LM393 D2 rising edges the firmware reads. It captured 2,000,000 edges (~61 CPM average) with zero buffer overruns or transport drops. The source was a GMC-320S Geiger counter beside a glass jar of cut-up 2% thoriated tungsten welding electrodes, on top of background radiation.
The intervals were put through the firmware's exact bit derivation from this repository at 2b9893a: 200 µs minimum interval, non-overlapping pairs and ties discarded. No intervals fell under the 200 µs cutoff (the detector's dead time is ~750 µs) and there were no ties. That produced 999,999 comparison bits (500,205 zeros, 499,794 ones, within 0.4 standard deviations of a fair coin). Those bits were assessed with NIST's ea_non_iid (v1.1.8):
| Estimator | Min-entropy (bits per bit) |
|---|---|
| Most Common Value | 0.996 |
| Collision | 0.885 |
| Markov | 0.996 |
| Compression | 0.828 |
| t-Tuple | 0.895 |
| LRS | 0.984 |
| MultiMCW prediction | 0.996 |
| Lag prediction | 0.998 |
| MultiMMC prediction | 0.983 |
| LZ78Y prediction | 0.983 |
The assessed min-entropy is the lowest of these: H ≈ 0.828 bits per comparison bit, set by the compression estimator. At that rate, the 512-bit raw pool holds about 424 bits of min-entropy before SHA-256 conditioning. That is well above the 256 bits a 24-word phrase needs.
What this does and doesn't show:
- The dataset is one bit short of the 1,000,000-sample size. 2,000,000 edges give 1,999,999 intervals and so 999,999 non-overlapping pairs.
ea_non_iidprints a warning about it, and the recorder'sverify_evidence.pytherefore labels the package a pilot. The missing sample makes no practical difference to the estimates, but the recorder now stops captures on the comparison-bit count so the next run will clear the threshold. - Only the sequential-dataset non-IID estimators were run. The SP 800-90B restart tests were not.
- It covers one device, one Geiger tube, one source and one environment. The enriched source ran at ~61 CPM; a background-only setup is slower, and this run says nothing about other builds or sources.
- It is an entropy assessment, not NIST certification or formal validation.
The raw edges, derived bits, full tool output, manifest and SHA-256 checksums are all published in the evidence directory. Running python tools/verify_evidence.py entropy32_sp80090b_2026-10-03a in that repository re-derives the bits from the raw edges and checks every count and hash.
Earlier pilot (10,000 edges)
Before the full-length run, a 10-hour pilot captured 10,000 edges (~17 CPM) and was derived with this repository at 311c859. That produced 4,999 comparison bits (2,543 zeros, 2,456 ones). ea_non_iid assessed them at H ≈ 0.738 bits per comparison bit (collision estimator; compression was not run at that size). That is lower than the full-length result because the estimators' 99% confidence bounds are wide with so few samples. The source and setup details for that run were not recorded. Its evidence directory is kept for reference.
Please verify the entropy source you intend to use; otherwise, understand that you are using the device at your own risk.
Disclaimer
This is a hobbyist/educational project. If you use it to generate a real Bitcoin seed phrase, understand the risks: verify the entropy quality yourself, review the code, and never trust a seed-generating device (this one included) with significant funds without independent auditing.
Licenses
- For firmware/software, see
MIT LICENSE. - For hardware, see
CERN-OHL-S v2 LICENSE.
Parts
37| Qty | Value | Footprint | References |
|---|---|---|---|
| 5 | 10k | Resistor_SMD:R_0805_2012Metric | R1, R3, R6, R7, R9 |
| 3 | 100nF | Capacitor_SMD:C_0805_2012Metric | C2, C9, C10 |
| 3 | 1k | Resistor_SMD:R_0805_2012Metric | R2, R4, R8 |
| 2 | 10pF | Capacitor_SMD:C_0805_2012Metric | C7, C8 |
| 2 | 1uF | Capacitor_SMD:C_0805_2012Metric | C3, C6 |
| 2 | 5K1 | Resistor_SMD:R_0805_2012Metric | R11, R12 |
| 1 | 0.1uF | Capacitor_SMD:C_0805_2012Metric | C1 |
| 1 | 100k | Resistor_SMD:R_0805_2012Metric | R5 |
| 1 | 100pF | Capacitor_SMD:C_0805_2012Metric | C4 |
| 1 | 16MHz | Crystal:Crystal_SMD_2520-4Pin_2.5x2.0mm | Y1 |
| 1 | 2.2k | Resistor_SMD:R_0805_2012Metric | R10 |
| 1 | 4.7uF | Capacitor_SMD:CP_Elec_5x3 | C5 |
| 1 | ATmega328P-A | Package_QFP:TQFP-32_7x7mm_P0.8mm | U3 |
| 1 | AudioJack2 | Connector_Audio:Jack_3.5mm_PJ320E_Horizontal | J1 |
| 1 | ER_OLEDM0.91_1x-I2C | Display:ER_OLEDM0.91_1x-I2C | U2 |
| 1 | FWD | Button_Switch_THT:SW_PUSH_6mm | SW2 |
| 1 | Green | LED_SMD:LED_0805_2012Metric | PWR1 |
| 1 | ICSP | Connector_PinHeader_2.54mm:PinHeader_2x03_P2.54mm_Vertical | J3 |
| 1 | LM393 | Package_SO:SOIC-8_3.9x4.9mm_P1.27mm | U1 |
| 1 | LOGO | Entropy32:wave_packet_logo | G*** |
| 1 | RESET | Button_Switch_THT:SW_PUSH_6mm | SW3 |
| 1 | REV | Button_Switch_THT:SW_PUSH_6mm | SW1 |
| 1 | USB_C_Receptacle_PowerOnly_6P | Connector_USB:USB_C_Receptacle_GCT_USB4125-xx-x_6P_TopMnt_Horizontal | J2 |
| 1 | Yellow | LED_SMD:LED_0805_2012Metric | L1 |
The 35 most common of 37 parts.
Similar boards
- 2 layers68.9 × 23 mm

- 2 layers120 × 120 mm

- 4 layers247.9 × 172.6 mm

BDSM G144
matatma23-gento/embedded-portfolio
This repository collects KiCad hardware design projects. Project specifications, validation results, and individual contributions are being documented; the presence of design files does not imply production readiness or…
350 parts - 2 layers150 × 100 mm

- 2 layers174.8 × 84 mm

DDS A2 rev1
apxco/Kicad
Zum Testen von einigen Transistorschaltungen für Verstärker. Noch nicht fertig.
69 parts - 2 layers97.2 × 50.5 mm

- 2 layers231.1 × 106.7 mm

- 2 layers130 × 80 mm

Questions
How big is the Geiger entropy PCB?
It measures 53 × 63 mm and has 2 copper layers, 1.6 mm thick.
What parts does the Geiger entropy use?
37 parts, built around the ATMEGA328P-AU and LM393. The full list of its parts is on this page, with links to each part's symbol and footprint where PCBKit's library has them.
Where can I download the Geiger entropy KiCad files?
From the captainchapster/Entropy32-Plus repository on GitHub, at the commit this page was made from: the board is KiCad/geiger_entropy.kicad_pcb and its schematic KiCad/geiger_entropy.kicad_sch. PCBKit opens the board in the browser, with nothing to install.
Can I use the Geiger entropy design in my own project?
Its repository publishes it under the MIT licence: you may reuse it on that licence's terms (keep the author's notice, and for a copyleft licence share your changes alike).