The attached code is some heavy modifications that I made to the Frequency Counter Kit (sku: KIT-10140) Arduino
Sketch to make use of it on the Geiger Counter (sku: SEN-09848). This could be used with any Geiger counter which
has a pulse output however. This code attempts to display count per minute and approximate energy. It uses the three
unused digital inputs to select units and mode of operation. Note that, while it works well, it could use cleanup and
reorganization… Enjoy it and I hope some one else can get some benefit from it…
/*
Geiger Counter Arduino Sketch
by: Barry Harding
Feb 12 2011
This started from the following project:
Frequency Counter Arduino Sketch
by: Jim Lindblom
SparkFun Electronics
License: Beerware
This is basically a modified (heavily so)version of the SparkFun
Electronics "Frequency Counter Arduino Sketch". This has been
used on the "Frequency Counter Kit sku: KIT-10140" connected to
the "Geiger Counter sku: SEN-09848". But any Geiger counter
circuit with digital out (+5v TTL) should work.
It has two modes of operation, "Survey" and "Background average"
mode. It can also display measured energy in standard or metric
units. For "Background average" mode there are three sub-modes
1 minute, 5 minute, or continous. There are three unused digital
inputs that can easily be used for all these choise user inputs
(others could be retasked but three is enough). To select the
mode/submode two inputs are used as shown:
Reset button
| o \ Survey
/ |--o \ 5min Average
GRND ------ .------| o \o---------- D12 1min Average
| |--o Continuous
|
| | o \
| | o \
|---|--o \o--------- D11
|--o
Note that this uses a 2P4T switch and that you may want to add a 10k
ohm resister as pull up on D11 and D12 (but should not be needed since
328p has them). Also note that there is a trick to get the reset
functionality for the three average modes. When the mode switches the
counters are reset to zero. By pressing the reset button the mode is
tempararily switched to Survey more. For standard/metric mode a
simple 1P1T switch goes between ground and D13.
The circuit: Powered at 5V (5V LCD), Arduino running at 16MHz
D2 - RS (LCD)
D3 - R/W (LCD)
D4 - E (LCD)
D5 - Frequency input
D6 - DB4 (LCD)
D7 - DB5 (LCD)
D8 - DB6 (LCD)
D9 - DB7 (LCD)
D10 - Gate of NPN transistor (Collector tied to 5V, emitter tied to LCD backlight pin)
(could be D10 - RESET COUNT input)
D11 - CPM 5 minute input 11 CPS, 01 CPM1, 10 CPM5, 00 count
D12 - CPM 1 minute input " " " "
D13 - metric input 1 = standard, 0 = metric
For the LiquidCrystal library, much thanks to:
David A. Mellis
Limor Fried (http://www.ladyada.net)
Tom Igoe
*/
#include <LiquidCrystal.h>
// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(2, 3, 4, 6, 7, 8, 9);
unsigned int tovf1 = 0;
unsigned long frequency = 0;
unsigned long temp = 0;
unsigned long prevmilli = 0;
unsigned long subprevmilli = 0;
unsigned long secs = 0;
unsigned long interval = 1000;
boolean ncpm5 = true;
boolean ncpm1 = true;
boolean nmetric = true;
boolean nmicro = false; // In micro instead of milli scale for auto scaling
int cpmcps = 0; // "
unsigned long whole; // Used when simulating decimals in with integer math
unsigned long frac; // "
unsigned long fact;
// Timer 1 is our counter
// 16-bit counter overflows after 65536 counts
// tovfl will keep track of how many times we overflow
ISR(TIMER1_OVF_vect)
{
tovf1++;
}
void setup() {
pinMode(5, INPUT); // This is the frequency input
pinMode(10, OUTPUT); // Reset count WAS Backlight control pin
pinMode(11, INPUT); // CPM 5 minute
pinMode(12, INPUT); // CPM 1 minute
pinMode(13, INPUT); // standard/metric switch
digitalWrite(10, HIGH); // Turn backlight on
// Timer 1 will be setup as a counter
// Maximum frequency is Fclk_io/2
// (recommended to be < Fclk_io/2.5)
// Fclk_io is 16MHz
TCCR1A = 0;
// External clock source on D5, trigger on rising edge:
TCCR1B = (1<<CS12) | (1<<CS11) | (1<<CS10);
// Enable overflow interrupt
// Will jump into ISR(TIMER1_OVF_vect) when overflowed:
TIMSK1 = (1<<TOIE1);
// Reset all counter variables to start at zero
TCNT1H = 0;
TCNT1L = 0;
// set up the LCD's number of rows and columns:
lcd.begin(16, 2);
// Print a splash screen to the LCD.
lcd.print("GeigerCounter ");
lcd.setCursor(0, 1);
lcd.print(" v1.3 ");
delay(2000);
}
void loop() {
boolean tmpncpm1;
boolean tmpncpm5;
unsigned long curmilli = millis();
nmetric = digitalRead(13); // Get current energy display mode
// If we are in Survey mode do it
if(ncpm1 == true && ncpm5 == true) {
if(curmilli - prevmilli > interval) { // Time for an update (every second)?
frequency = (TCNT1H<<8)|TCNT1L;
frequency = (TCNT1H<<8)|TCNT1L;
// Correct weird counter bug
// A small range of frequencies (~30k-50k) are getting
// 42949 appended to the front of them
// Will look into this but this works for now
if (frequency > 40000000)
frequency -= 4294900000;
// 65536 (2^16) is maximum of counter
// We'll multiply that by how many times we overflowed
temp = 65536*(unsigned long)tovf1;
frequency += temp;
lcd.clear();
lcd.print("Count/Sec ");
lcd.print(frequency);
lcd.setCursor(0,1);
// We avoid using floating point here since it is too slow
// and big for a smaller imbedded processor. Instead we adjust
// all math by shifting decimal place to the left.
temp = frequency * 5;
if(nmetric) {
fact = 10000; // Four decimal places
whole = temp/fact;
frac = temp % fact;
lcd.print("mSv/hr ");
lcd.print(whole);
lcd.print(".");
if(frac < 10)
lcd.print("000");
else if(frac < 100)
lcd.print("00");
else if(frac < 1000)
lcd.print("0");
lcd.print(frac);
}else{
fact = 100; // Two decomal places
whole = temp/fact;
frac = temp % fact;
lcd.print("mR/hr ");
lcd.print(whole);
lcd.print(".");
if(frac < 10)
lcd.print("0");
lcd.print(frac);
}
// Reset all counter variables and start over for every sample
TCNT1H = 0;
TCNT1L = 0;
tovf1 = 0;
prevmilli = millis();
}
// Else we are in background average mode
}else{
// We only reset the collected data at the end of average period
if(interval && curmilli - prevmilli > interval) {
// Reset all counter variables and start over
TCNT1H = 0;
TCNT1L = 0;
tovf1 = 0;
frequency = 0;
secs = 0;
prevmilli = millis();
}
// But we update the display and collected count every second
if(curmilli - subprevmilli > 1000) {
temp = (TCNT1H<<8)|TCNT1L;
temp = (TCNT1H<<8)|TCNT1L;
// Correct weird counter bug
// A small range of frequencies (~30k-50k) are getting
// 42949 appended to the front of them
// Will look into this but this works for now
if (temp > 40000000)
temp -= 4294900000;
frequency += temp;
// 65536 (2^16) is maximum of counter
// We'll multiply that by how many times we overflowed
temp = 65536*(unsigned long)tovf1;
frequency += temp;
secs++; // Total number of seconds for this collection cycle
lcd.clear();
// If in contous mode we switch between displaying raw totals
// and Count per minute (or second) every three seconds.
if(ncpm1 == false && ncpm5 == false && cpmcps > 2) {
// we are doing count per, mode
// Adjust number of seconds in counter per mode
if(cpmcps == 5)
cpmcps = 0;
else
cpmcps++;
// Display the data
temp = frequency/secs;
if(temp){
lcd.print("Count/Sec ");
lcd.print(temp);
}else{
if(secs > 60)
temp = secs/60;
else
temp = 1;
temp = frequency/temp;
lcd.print("Count/Min ");
lcd.print(temp);
}
}else{
// We are doing totals mode
// Adjust number of seconds in raw mode
if(ncpm1 == false && ncpm5 == false)
cpmcps++;
else
cpmcps = 0;
// Display the data
if(secs < 1000) // Hack to make large numbers fit
lcd.print("Sec ");
else if(secs < 10000)
lcd.print("Sc ");
else if(secs < 100000)
lcd.print("S ");
else
lcd.print("s");
lcd.print(secs);
if(frequency < 10000) // Hack to make large numbers fit
lcd.print(" Cnt ");
else if(frequency < 100000)
lcd.print(" Ct ");
else if(frequency < 1000000)
lcd.print(" C ");
else if(frequency < 10000000)
lcd.print(" c");
else
lcd.print(" ");
lcd.print(frequency);
}
lcd.setCursor(0,1);
temp = (frequency/secs) * 5;
if(temp == 0) // frequency too small sa adjust units to micro
nmicro = true;
else
nmicro = false;
if(nmetric) {
if(nmicro) {
// uSv mode xxx.x
temp = (frequency * 50000)/secs;
fact = 100;
whole = temp/fact;
frac = temp % fact;
lcd.print("uSv/hr ");
lcd.print(whole);
lcd.print(".");
if(frac < 10)
lcd.print("0");
lcd.print(frac);
}else{
// mSv mode xx.xxx
fact = 10000;
whole = temp/fact;
frac = temp % fact;
lcd.print("mSv/hr ");
lcd.print(whole);
lcd.print(".");
if(frac < 10)
lcd.print("000");
else if(frac < 100)
lcd.print("00");
else if(frac < 1000)
lcd.print("0");
lcd.print(frac);
}
}else{
if(nmicro) {
// uR mode xxxx.
whole = (frequency * 50000)/secs;
lcd.print("uR/hr ");
lcd.print(whole);
}else{
// mR mode xxx.x
fact = 100;
whole = temp/fact;
frac = temp % fact;
lcd.print("mR/hr ");
lcd.print(whole);
lcd.print(".");
if(frac < 10)
lcd.print("0");
lcd.print(frac);
}
}
// Reset all counter variables and start over since we collected
// data from the hardware.
TCNT1H = 0;
TCNT1L = 0;
tovf1 = 0;
subprevmilli = millis();
}
}
// Now that we displayed anything needed update mode switch settings state.
tmpncpm1 = digitalRead(11);
tmpncpm5 = digitalRead(12);
// Any mode changes from last time here?
if(tmpncpm1 != ncpm1 || tmpncpm5 != ncpm5) {
// YES
// How long before we reset data collection
if(tmpncpm1 == true && tmpncpm5 == true) // Survey mode every second
interval = 1000 ;
else if(tmpncpm1 == false && tmpncpm5 == true) // 1 minute averge 60 seconds
interval = 60000;
else if(tmpncpm1 == true && tmpncpm5 == false) // 5 minute averge 300 seconds
interval = 300000;
else{
interval = 0; // continous - no auto data resets
}
// Reset all counter variables and start over.
// We need to do this since units may have changed.
// Also this helps with trick to make reset button not need
// its own input pin.
TCNT1H = 0;
TCNT1L = 0;
tovf1 = 0;
frequency = 0;
secs = 0;
ncpm1 = tmpncpm1;
ncpm5 = tmpncpm5;
prevmilli = millis();
subprevmilli = prevmilli;
}
}