I plugged my Geiger counter’s audio cable into my oscilloscope just for kicks the other day and saw ~9V pulses coming out when it occurred to me that I could easily read those into an Arduino or Raspberry Pi or ESP8266 microcontroller and respond to them. As a demo, I made a hardware random number generator (HRNG) out of a esp8266.
While we all know radioactive decay is random, converting random clicks into random numbers may not be immediately obvious. One great way of doing it is to use the timings between pulses as a random variable and compare the length of time between two subsequent pairs of pulses. If the length of the first gap is bigger than the length of the second, you emit a 1. Otherwise, you emit a 0. When you have enough bits to build an integer (or whatever kind of number you’re seeking), you emit the fully-assembled bitstream. Hotbits has a good explanation here.
First step as usual was to step the pulses from the Gieger counter down to around 3.3V for the ESP8266’s GPIO digital input pins. For this, as usual, I just made a little voltage divider out of 2 resistors. I grabbed them out of the grab bag until I had the ratio right, ending up with 22 kOhm and a 10 kOhm one. This brought pulses from around 7V peak down to an appropriate range.
For coding, I used ESP8266 GPIO interrupts. I made an interrupt that fires when the GPIO pin is RISING from LOW to HIGH (e.g. when a pulse is coming in). Here’s how it looks:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
#define GEIGER_INPUT D6 unsigned long timePrev; unsigned long duration1; unsigned long duration2; unsigned int pulseCount = 0; unsigned int bitCount = 0; bool flag = false; byte bits; int numBits = 8;// sizeof(bits); void setup() { Serial.begin(9600); Serial.println("Welcome To Radbits 8000"); pinMode(GEIGER_INPUT, INPUT); attachInterrupt(digitalPinToInterrupt(GEIGER_INPUT), handle_pulse, RISING); } ICACHE_RAM_ATTR void handle_pulse(void) { flag = true; switch(pulseCount) { case 0: case 2: timePrev = micros(); break; case 1: duration1 = micros()-timePrev; break; case 3: duration2 = micros()-timePrev; break; } } |
I’m running a pulseCount that increments from 0 to 3 and then loops around each time a pulse comes by (triggered by flag
). The 1st and 3rd pulse reset the timePrev while the 2nd and 4th pulses compute the two durations. Once 4 pulses have come through, I have 2 durations that are ready for comparison.
You only ever want to do relatively simple things in interrupts, so that’s all that does. In the main loop, there’s a function that monitors for the flag being set by the interrupt and it does the rest of the work. It waits until all 4 pulses have been received and then it compares the two durations and sets the next bit in our bits variable. Once we have enough bits, it emits the final fully assembled number and starts over.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
void processPulses() { if (!flag) { return; } flag = false; if(pulseCount == 3) { // durations are prepared. process and reset pulseCount = 0; if (duration1 == duration2) { // durations equal. Reject and wait for next return; } if (duration1 > duration2) { // Set this particular bit to 1 bits |= (1<<bitCount); } else { // set this bit to 0 bits &= ~(1<<bitCount); } bitCount++; if(bitCount == numBits) { bitCount = 0; emit(); } } else { pulseCount++; } } void emit() { Serial.println(bits); } void loop() { processPulses(); } |
That’s it. Fun little project for sure.
Here’s a histogram of the results after running for a few minutes.
A file full of numbers from it is available here:
I will probably use this Geiger counter interface for more stuff later.
Looks good. Have you tried testing it with a raster scan?