Skip to the content
2 layers53 × 63 mmGeiger entropy, the printed circuit board in 3D

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

2

README

README.md

Entropy32 Plus

Assembled PCB

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

  1. A Geiger counter detects decay events from a radioactive source (or background) and outputs a pulse via a 3.5mm audio jack. GMC-320S Geiger Pulse
  2. 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. GMC-320S Geiger Pulse
  3. 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.
  4. The conditioned entropy is mapped to words from the standard BIP39 English wordlist.
  5. 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:

text
minutes ≈ 1024 / CPM

The 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:

SourceTypical CPMTime 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

ATmega328P GMC-320S Geiger counter

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.

Basic Case

Repository contents

FilePurpose
entropy32_plus.inoMain firmware — state machine, entropy capture, and UI logic
sha256.h / sha256.cppSHA-256 implementation used to condition raw entropy
bip39_wordlist.hBIP39 English wordlist, compiled into firmware
button.hButton input handling
logo_bitmap.hBoot splash bitmap, compiled into firmware
tools/english.txtSource BIP39 wordlist
tools/generate_wordlist.pyScript to regenerate bip39_wordlist.h from english.txt
tools/generate_logo_bitmap.pyScript 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

Entropy32 schematic

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.

  1. Wire up an ISP programmer. Either a second Arduino running the ArduinoISP example 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.
  2. 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.
  3. Flash the firmware. With the bootloader in place and the board running at 16MHz external clock, open entropy32_plus.ino and 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:

text
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:

SymbolBytesWhat it is
BIP39_WORDLIST_BLOB13,117The official 2048-word BIP39 English wordlist
main (loop + inlined UI code)3,062State machine, screen drawing, menu logic
SHA256::transform2,278The SHA-256 compression function
vfprintf948Pulled in by snprintf, used for CPM/ETA/progress formatting
u8x8_font_5x7_r764Built-in OLED font glyph table
U8x8 / TwoWire internals~1,100I2C + SSD1306 driver plumbing
LOGO_BITMAP384Boot splash bitmap
K (SHA-256 round constants)256Fixed 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):

EstimatorMin-entropy (bits per bit)
Most Common Value0.996
Collision0.885
Markov0.996
Compression0.828
t-Tuple0.895
LRS0.984
MultiMCW prediction0.996
Lag prediction0.998
MultiMMC prediction0.983
LZ78Y prediction0.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_iid prints a warning about it, and the recorder's verify_evidence.py therefore 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

Parts

37
QtyValueFootprintReferences
510kResistor_SMD:R_0805_2012MetricR1, R3, R6, R7, R9
3100nFCapacitor_SMD:C_0805_2012MetricC2, C9, C10
31kResistor_SMD:R_0805_2012MetricR2, R4, R8
210pFCapacitor_SMD:C_0805_2012MetricC7, C8
21uFCapacitor_SMD:C_0805_2012MetricC3, C6
25K1Resistor_SMD:R_0805_2012MetricR11, R12
10.1uFCapacitor_SMD:C_0805_2012MetricC1
1100kResistor_SMD:R_0805_2012MetricR5
1100pFCapacitor_SMD:C_0805_2012MetricC4
116MHzCrystal:Crystal_SMD_2520-4Pin_2.5x2.0mmY1
12.2kResistor_SMD:R_0805_2012MetricR10
14.7uFCapacitor_SMD:CP_Elec_5x3C5
1ATmega328P-APackage_QFP:TQFP-32_7x7mm_P0.8mmU3
1AudioJack2Connector_Audio:Jack_3.5mm_PJ320E_HorizontalJ1
1ER_OLEDM0.91_1x-I2CDisplay:ER_OLEDM0.91_1x-I2CU2
1FWDButton_Switch_THT:SW_PUSH_6mmSW2
1GreenLED_SMD:LED_0805_2012MetricPWR1
1ICSPConnector_PinHeader_2.54mm:PinHeader_2x03_P2.54mm_VerticalJ3
1LM393Package_SO:SOIC-8_3.9x4.9mm_P1.27mmU1
1LOGOEntropy32:wave_packet_logoG***
1RESETButton_Switch_THT:SW_PUSH_6mmSW3
1REVButton_Switch_THT:SW_PUSH_6mmSW1
1USB_C_Receptacle_PowerOnly_6PConnector_USB:USB_C_Receptacle_GCT_USB4125-xx-x_6P_TopMnt_HorizontalJ2
1YellowLED_SMD:LED_0805_2012MetricL1

The 35 most common of 37 parts.

Similar boards

  • 2 layers68.9 × 23 mmRPM, the printed circuit board in 3D

    RPM

    midasgossye/RPM-counter

    Open-source DIY RPM counter using IR reflectance

    64 parts3
  • 2 layers120 × 120 mmMagLev, the printed circuit board in 3D

    MagLev

    KDueck/MagLev

    A design for a reverse engineered magnetic levitation PCB.

    211 parts
  • 4 layers247.9 × 172.6 mmBDSM G144, the printed circuit board in 3D

    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 layers174.8 × 84 mmDDS A2 rev1, the printed circuit board in 3D

    DDS A2 rev1

    apxco/Kicad

    Zum Testen von einigen Transistorschaltungen für Verstärker. Noch nicht fertig.

    69 parts
  • 2 layers97.2 × 50.5 mmAtmel328pBoard, the printed circuit board in 3D

    Atmel328pBoard

    ieb/N2KEngine

    N2K Single board engine monitor using a 328p

    72 parts
  • 2 layers231.1 × 106.7 mmSnark barker, the printed circuit board in 3D

    Snark barker

    schlae/snark-barker

    A 100% compatible replica of the famed SB 1.0 sound card

    199 parts687
  • 2 layers130 × 80 mmSmu main 1, the printed circuit board in 3D

    Smu main 1

    jaromir-sukuba/J-SMU

    DIY source-measure unit

    210 parts244

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).