< async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"">

Sunday, December 8, 2024

Generating True Random Numbers with a Geiger Counter and an ATtiny13 to serial port

ADVERTISEMENT

For most cases of random number generation, systems use a pseudo-random number generator (PRNG), that is an algorithm that generates numbers that "look" random. But for certain applications, where true randomness is required — as in cryptography or simulations — there’s a different approach: draw on a physical process that, by its nature, is unpredictable. Radioactive decay is one such process that can be exploited to produce random numbers. For this project we are going to show you how to create a simple random number generator with the help of a Geiger counter, Geiger tube, and an ATtiny13.

Random bytes


Particle Detection: 

The Geiger counter detects ionizing particles (like beta particles or gamma rays) passing through the Geiger tube. Each time a particle is detected, a pulse is generated.

Signal Capture: 

This pulse is sent to the ATtiny13, which is set up to measure the time between two consecutive pulses. The ATtiny uses this timing data to assess the randomness of the intervals.

Assigning a Bit: 

The ATtiny13 then compares the duration between consecutive pulses. If the time difference is shorter than a certain threshold, it assigns a bit value of "1". If the time difference is longer, it assigns a bit value of "0". This method relies on the natural randomness of radioactive decay to generate unpredictable bit sequences.

The result is then transmitted to a serial port/ can easily be analysed on a computer. I used background radiation and the random number generation was very slow. Alternatively you could use the output from this setup as a seed for a PRNG.



Wiring/connections

I hooked up the PB4 on the attiny13 to the particle count output ( speaker) on an inexpensive counter (BR-6). The PB1 is used as the serial output (TX) using software serial (library from Ćukasz Podkalicki). 

Source Code for attiny13

1:  #include <avr/io.h>  
2:  #include <avr/interrupt.h>  
3:  #include <stdint.h>  
4:  // Pin definitions  
5:  #define GEIGER_PIN PB4  
6:  // Global variables  
7:  volatile uint32_t timer_overflows = 0;  
8:  volatile uint32_t last_timer_value = 0;  
9:  volatile uint32_t last_interval = 0;  
10:  volatile uint8_t random_byte = 0;  
11:  volatile uint8_t bit_count = 0;  
12:  volatile uint8_t edge_pending = 0;  
13:  volatile uint32_t pending_timer_value = 0;  
14:  // Timer overflow interrupt  
15:  ISR(TIM0_OVF_vect) {  
16:    timer_overflows++;  
17:  }  
18:  // Pin Change Interrupt (PCINT) for detecting edges on PB4  
19:  ISR(PCINT0_vect) {  
20:    static uint8_t last_state = 1;  
21:    uint8_t current_state = (PINB & (1 << GEIGER_PIN)) ? 1 : 0;  
22:    if (last_state == 1 && current_state == 0) { // Negative edge detected  
23:      uint32_t current_timer_value = ((uint32_t)timer_overflows << 8) | TCNT0;  
24:      if (edge_pending == 0) {  
25:        pending_timer_value = current_timer_value;  
26:        edge_pending = 1;  
27:      }  
28:    }  
29:    last_state = current_state;  
30:  }  
31:  // Safely process pending edges  
32:  void process_pending_edge() {  
33:    if (edge_pending) {  
34:      edge_pending = 0;  
35:      uint32_t interval = pending_timer_value - last_timer_value;  
36:      last_timer_value = pending_timer_value;  
37:      // Process interval  
38:      if (interval > 10 ) { // Valid range  
39:        if (interval != last_interval) { // Avoid noise  
40:          uint8_t random_bit = (interval > last_interval) ? 1 : 0;  
41:          last_interval = interval;  
42:          // Add bit to random byte  
43:          random_byte = (random_byte << 1) | random_bit;  
44:          bit_count++;  
45:          // If we have 8 bits, send the random byte  
46:          if (bit_count == 8) {  
47:            cli(); // Disable interrupts during serial print  
48:            uart_putu(random_byte);  
49:            uart_puts(",");  
50:            sei(); // Re-enable interrupts  
51:            bit_count = 0;  
52:            random_byte = 0;  
53:          }  
54:        }  
55:      }  
56:    }  
57:  }  
58:  // Initialize timer  
59:  void timer_initx() {  
60:    TCCR0B = (1 << CS00); // No prescaler  
61:    TIMSK0 = (1 << TOIE0); // Enable overflow interrupt  
62:  }  
63:  // Initialize Pin Change Interrupt for PB4  
64:  void pcint_init() {  
65:    GIMSK = (1 << PCIE);  // Enable pin change interrupt  
66:    PCMSK = (1 << PCINT4); // Enable interrupt on PB4  
67:    sei();         // Enable global interrupts  
68:  }  
69:  int main() {  
70:    // Initialize peripherals  
71:    DDRB &= ~(1 << GEIGER_PIN); // Set GEIGER_PIN as input  
72:      // Set PB1 (TX) as output for bit-banging UART  
73:    DDRB |= (1 << PB1);  
74:    PORTB |= (1 << PB1); // Start with high state (idle state for UART)  
75:    DDRB &= ~(1 << PB4);   
76:    PORTB |= (1 << PB4);   
77:    timer_initx();  
78:    pcint_init();  
79:    while (1) {  
80:      process_pending_edge(); // Handle pending edges in the main loop  
81:    }  
82:    return 0;  
83:  }  
If you want to compile use avr-gcc and following uart library (https://github.com/lpodkalicki/attiny13-software-uart-library). Hook a usb to serial connector to the pc and the rx pin to the PB1 on the attiny13. Include uart.h & uart.c from library and use following makefile
 MCU=attiny13  
 FUSE_L=0x6A  
 FUSE_H=0xFF  
 F_CPU=1200000  
 CC=avr-gcc  
 LD=avr-ld  
 OBJCOPY=avr-objcopy  
 SIZE=avr-size  
 AVRDUDE=avrdude  
 CFLAGS=-std=c99 -Wall -g -Os -mmcu=${MCU} -DF_CPU=${F_CPU} -I. -I.. -DUART_RX_ENABLED -DUART_TX_ENABLED -DUART_BAUDRATE=19200  
 TARGET=main  
 SRCS=main.c uart.c  
 all:  
      ${CC} ${CFLAGS} -o ${TARGET}.o ${SRCS}  
      ${LD} -o ${TARGET}.elf ${TARGET}.o  
      ${OBJCOPY} -j .text -j .data -O ihex ${TARGET}.o ${TARGET}.hex  
      ${SIZE} -C --mcu=${MCU} ${TARGET}.elf  
 flash:  
      ${AVRDUDE} -p ${MCU} -c usbasp -U flash:w:${TARGET}.hex:i -F -P usb  
 fuse:  
      $(AVRDUDE) -p ${MCU} -c usbasp -U hfuse:w:${FUSE_H}:m -U lfuse:w:${FUSE_L}:m  
 clean:  
      rm -f *.c~ *.o *.elf *.hex  

No comments:

Post a Comment