2011 m. rugpjūčio 22 d., pirmadienis

Added 3LED indicator function (software ammended)



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;
}






Arduino Geiger counter with LCD and si29bg GM tube