http://www.youtube.com/watch?v=bDFuHII-ahM
I have amended the default software to control the 3 LED indicator.
This indicator works as follows: on boot shows that it is alive (working)
then if radiation dose rate exceeds 0.5uSv/h then yellow LED is lit. If dose rate exceeds 1uSv/h yellow and orange LEDs are lit and if dose rate is over 1.2uSv/h then all three LEDs yellow, orange and red are lit. As you can see this change works together with LCD and serial connection. So if you just need to have a rather simple Geiger counter (without LCD) you can do it with three leds: yellow - just warning of some radiative anomaly, orange - informing that a person should not be in that place more than several hours and red - advising to hide or leave instantly.
the connection of LEDs which is supposed by software is in the picture below
The software sketch for SMB-20 and is below:
/* Geiger Counter Kit - Default Sketch (v5b.1) bHogan 7/29/11
* Ammended with 3 LED threshold indicator by impexeris on ebay.com on 08/22/11
*
* if you use si29BG (СИ29БГ) I would suggest the ratio of 102 or 101.5
Counts events/min. and outputs CPM & ~uSv/hr to LCD display and also to serial port.
* Sample period shortens with increase in counts. Bar graph on LCD
* One of 2 conversion ratios can be selected via jumper. ACCURACY NOT VERIFIED
* v4 adds check for voltage at the uC to indicate a low battery voltage
* v5 add seperate counterfor serial - set at 1 min to give CPM without rounding
*
* SETUP: (Any 2x16 Hitachi HD44780 compatible LCD)
* +5V LCD Vdd pin 2 >>> Gnd LCD Vss pin 1, and R/W pin 5
* LCD RS pin 4 to D3 >>> LCD Enable pin 6 to D4
* LCD D4 pin 11 to D5 >>> LCD D5 pin 12 to D6
* LCD D6 pin 13 to D7 >>> LCD D7 pin 14 to D8
* LCD LEDA pin 15 to ~1K to +5 >>> LCD LEDK pin 16 to GND
* 10K pot: - ends to +5V and Gnd, wiper to LCD VO pin (pin 3)
* *INT from Geiger circuit is connected to PIN 2 and triggered as FALLING.
* PIN 9 - jumper to GND if secondary conversion ratio is desired
*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2.1 of the License, or any later version.
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* THIS PROGRAM AND IT'S MEASUREMENTS IS NOT INTENDED TO GUIDE ACTIONS TO TAKE, OR NOT
* TO TAKE REGARDING EXPOSURE TO RADIATION. ALWAYS FOLLOW THE INSTRUCTIONS GIVEN BY
* THE CIVIL AUTHORITIES. DO NOT RELY ON THIS PROGRAM!
*/
#include <LiquidCrystal.h> // HD44780 compatible LCDs work with this lib
#define LED_PIN 13 // for debug only - flashes 5X at startup
#define LED_PIN_Y 12 // yellow led - over 0.5 mSv/H
#define LED_PIN_O 11 // orange led - over 1 mSv/H
#define LED_PIN_R 10 // orange led - over 1.2 mSv/H
#define TUBE_SEL 9 // jumper to select alt conversion to uSv
// HOW MANY CPM =1 uSV? Two commonly used sets of ratios for the SBM-20 & LND712 are defined:
// libelium now uses: 175.43 for SBM-20 and 100.00 for LND712
// www.utsunomia.com/y.utsunomia/Kansan.html use: 150.51 for SBM-20 and 123.14 for LND712
#define PRI_RATIO 175.43 // no TUBE_SEL jumper - SET FOR SBM-20
#define SEC_RATIO 100.00 // TUBE_SEL jumper to GND - SET FOR LND712
// use 102 or 101.5 for si29BG (СИ29БГ)
// mS between writes to serial - counts also accumulate seperately for this period
#define LOGGING_PEROID 60000 // best set for at least 1 min (60000 mS) unless testing
// mS between writes to display - counts/period are averaged to CPM < 60000 less accurate
// LOGGING_PERIOD shuld be a multiple of these periods
#define LONG_PERIOD 10000 // mS sample & display below 100 CPM
#define SHORT_PERIOD 5000 // mS sample & display above 100 CPM
#define FULL_SCALE 1000 // max CPM for 8 bars & overflow warning
#define LOW_VCC 4200 //mV // if Vcc < LOW_VCC give low voltage warning
// instantiate the library and pass pins for (RS, Enable, D4, D5, D6, D7)
LiquidCrystal lcd(3, 4, 5, 6, 7, 8); // default layout for the Geiger board
volatile boolean newEvent = false; // true when new event was caught by ISR
boolean lowVcc = false; // true when Vcc < LOW_VCC
unsigned long dispPeriodStart, dispPeriod, dispCnt, dispCPM; // counters for the display period
unsigned long logPeriodStart, logCnt, logCPM; // counters for the logging period
unsigned long checkVccTime;
float uSv = 0.0; // display CPM converted to "unofficial" uSv
float uSvLogged = 0.0; // logging CPM converted to "unofficial" uSv
float uSvRate; // holds the rate selected by jumper
int Vcc_mV; // mV of Vcc from last check
//Custom characters used for bar graph
byte bar_0[8] = {0x00, 0x00, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00}; //blank
byte bar_1[8] = {0x10, 0x10, 0x18, 0x18, 0x18, 0x10, 0x10, 0x00}; //1 bar
byte bar_2[8] = {0x18, 0x18, 0x1c, 0x1c, 0x1c, 0x18, 0x18, 0x00}; //2 bars
byte bar_3[8] = {0x1C, 0x1C, 0x1e, 0x1e, 0x1e, 0x1C, 0x1C, 0x00}; //3 bars
byte bar_4[8] = {0x1E, 0x1E, 0x1f, 0x1f, 0x1f, 0x1E, 0x1E, 0x00}; //4 bars
byte bar_5[8] = {0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x00}; //5 bars
byte bar_6[8] = {0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x00}; //6 bars (same)
void setup(){
Serial.begin(9600); // comspec 96,N,8,1
attachInterrupt(0,GetEvent,FALLING); // Geiger event on pin 2 triggers interrupt
pinMode(LED_PIN,OUTPUT); // setup LED pin 13
pinMode(LED_PIN_Y,OUTPUT); // setup LED pin 12
pinMode(LED_PIN_O,OUTPUT); // setup LED pin 11
pinMode(LED_PIN_R,OUTPUT); // setup LED pin 10
pinMode(TUBE_SEL,INPUT); // setup tube select jumper pin
digitalWrite(TUBE_SEL, HIGH); // set 20K pullup on jumper pin(low active)
dispPeriod = LONG_PERIOD; // start with long display period
Blink(LED_PIN,5); // show it's alive
Blink_fast(LED_PIN_R,1); // show it's alive
Blink_fast(LED_PIN_O,1); // show it's alive
Blink_fast(LED_PIN_Y,1); // show it's alive
Blink_fast(LED_PIN_O,1); // show it's alive
Blink_fast(LED_PIN_R,1); // show it's alive
Blink_fast(LED_PIN_O,1); // show it's alive
Blink_fast(LED_PIN_Y,1); // show it's alive
lcd.begin(16,2); // cols, rows of display (8x2, 16x2, etc.)
lcd.createChar(0, bar_0); // load 7 custom characters in the LCD
lcd.createChar(1, bar_1);
lcd.createChar(2, bar_2);
lcd.createChar(3, bar_3);
lcd.createChar(4, bar_4);
lcd.createChar(5, bar_5);
lcd.createChar(6, bar_6);
lcd.setCursor(0,0);
lcd.clear(); // clear the screen
lcd.print(" GEIGER COUNTER "); // display a simple banner
delay (2000); // leave the banner up 2 sec.
lcd.clear(); // clear the screen
if(digitalRead(TUBE_SEL)){ // read jumper to select conversion ratio
uSvRate = PRI_RATIO; // use the primary ratio defined
}
else{ // jumper is set to GND . . .
uSvRate = SEC_RATIO; // use the secondary ratio defined
}
lcd.print(uSvRate,0); // display conversion ratio in use
lcd.print(" CPM to uSv");
lcd.setCursor(0,1); // set cursor on line 2
Vcc_mV = readVcc(); // read Vcc voltage
lcd.print("Running at "); // display it
lcd.print(Vcc_mV/1000. ,2); // display as volts with 2 dec. places
lcd.print("V");
delay (5000); // leave info up for 5 sec.
lcd.clear(); // clear the screen
lcd.print("CPM "); // display static "CPM"
lcd.setCursor(0,1);
lcd.print("uSv/hr "); // display static "uSv/hr"
Serial.println("CPM \t uSv \t Vcc"); // print header for log
dispPeriodStart = millis(); // start timing display CPM
logPeriodStart = dispPeriodStart; // start logging timer
checkVccTime = dispPeriodStart; // start Vcc timer
}
void loop(){
if (millis() >= checkVccTime + 2000){ // timer for check battery
checkVccTime = millis(); // reset timer
Vcc_mV = readVcc();
if (Vcc_mV <= LOW_VCC) lowVcc = true; // check if Vcc is low
else lowVcc = false;
}
if (millis() >= dispPeriodStart + dispPeriod) ProcCounts(); // period is over
if (newEvent){ // add to current count
dispCnt++;
logCnt++;
newEvent = false;
}
logCount(); // check if it's time to log & if so log
//uncomment 2 lines below for self check (1/166ms X 60 = 360 CPM
//newEvent = true; // simulate an interrupt
//delay(167); // 167 mS ~= 6 Hz
}
void ProcCounts(){ // aggregate totals and reset counters for each period
// These are calcs for display - less accurate
dispCPM = (dispCnt * 60) /(dispPeriod / 1000); // calc CPM for the period
uSv = dispCPM / uSvRate; // make uSV conversion
if (dispCPM > 99)dispPeriod = SHORT_PERIOD; // shorten sample period if CPM > 100
else dispPeriod = LONG_PERIOD;
LEDIndicate(uSv);
displayCount();
dispCnt = 0; // reset display event counter
dispPeriodStart = millis(); // reset display time
}
void LEDIndicate(float uSv){
if (uSv > 0.5)
digitalWrite(LED_PIN_Y,HIGH);
else digitalWrite(LED_PIN_Y,LOW);
if (uSv > 1)
digitalWrite(LED_PIN_O,HIGH);
else digitalWrite(LED_PIN_O,LOW);
if (uSv > 1.2)
digitalWrite(LED_PIN_R,HIGH);
else digitalWrite(LED_PIN_R,LOW);
}
void displayCount(){
lcd.setCursor(4, 0); // set cursor after "CPM "
lcd.print(" "); // clear area
lcd.setCursor(4, 0); // set cursor after "CPM" again
lcd.print(dispCPM); // display CPM on line 1
lcd.setCursor(7, 1); // as above for line 2
lcd.print(" ");
lcd.setCursor(7, 1);
lcd.print(uSv,4); // display uSv/hr on line 2
lcd.setCursor(9,0); // move cursor to 9th col, 1st line for lcd bar
if (dispCPM <= FULL_SCALE) lcdBar(dispCPM); // display bargraph on line 2
else { // put warning on line 2 if > full scale
lcd.setCursor(10,0); // move cursor to 10th col, 1st line for lcd bar
lcd.print(" ");
lcd.setCursor(10,0);
lcd.print(">"); // show what full scale on bars is set at
lcd.print(FULL_SCALE);
}
if (lowVcc) { // overwrite display with battery voltage if low
lcd.print(" ");
lcd.setCursor(9,0);
lcd.print("Vcc=");
lcd.print(Vcc_mV/1000. ,2); // display as volts with 2 dec. places
}
}
void lcdBar(int counts){ // displays CPM as bargraph on 2nd line
// Adapted from DeFex http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1264215873/0
int scaler = FULL_SCALE / 47; // 8 char=47 "bars", scaler = counts/bar
int cntPerBar = (counts / scaler); // amount of bars needed to display the count
int fullBlock = (cntPerBar / 6); // divide for full "blocks" of 6 bars
int prtlBlock = (cntPerBar % 6 ); // calc the remainder of bars
for (int i=0; i<fullBlock; i++){
lcd.print(5,BYTE); // print full blocks
}
lcd.print(prtlBlock,BYTE); // print remaining bars with custom char
for (int i=(fullBlock + 1); i<8; i++){
lcd.print(" "); // blank spaces to clean up leftover
}
}
void logCount(){ // unlike logging sketch, just outputs to serial
if (millis() < logPeriodStart + LOGGING_PEROID) return; // period not over
logCPM = float(logCnt) / (float(LOGGING_PEROID) / 60000);
uSvLogged = logCPM / uSvRate; // make uSV conversion
// Print to serial in a format that might be used by Excel
Serial.print(" ");
Serial.print(logCPM,DEC);
Serial.print(",\t");
Serial.print(uSvLogged,4);
Serial.print(",\t"); // comma delimited
Serial.print(Vcc_mV/1000. ,2); // print as volts with 2 dec. places
Serial.print("V");
Serial.println(",");
Blink(LED_PIN,2); // show it logged
logCnt = 0; // reset log event counter
logPeriodStart = millis(); // reset log time
dispCnt = 0; // reset display event counter
dispPeriodStart = millis(); // reset display time
}
void Blink(byte led, byte times){ // just to flash the LED
for (byte i=0; i< times; i++){
digitalWrite(led,HIGH);
delay (150);
digitalWrite(led,LOW);
delay (100);
}
}
void Blink_fast(byte led, byte times){ // just to flash the LED
for (byte i=0; i< times; i++){
digitalWrite(led,HIGH);
delay (60);
digitalWrite(led,LOW);
delay (50);
}
}
long readVcc() { // SecretVoltmeter from TinkerIt
long result;
// Read 1.1V reference against AVcc
ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
delay(2); // Wait for Vref to settle
ADCSRA |= _BV(ADSC); // Convert
while (bit_is_set(ADCSRA,ADSC));
result = ADCL;
result |= ADCH<<8;
result = 1126400L / result; // Back-calculate AVcc in mV
return result;
}
void GetEvent(){ // ISR triggered for each new event (count)
newEvent = true;
}