IOT Geiger Counter (InfluxDB)

Of course every household needs a Geiger Counter and I bought this kit to do all the fancy 400/500V voltage work along with an SBM-20 Geiger-Muller Tube on eBay for the actual radiation detecting. Typically it seems people hook this up to a tablet etc. and run an app but my plan was to log to InfluxDB. It can also operate stand-alone which is why I added a handy display (Nokia 5110).

Finished IOT Geiger Counter

Notes:
This type of detector is designed to detect Beta & Gamma rays. (it cannot detect Alpha rays but this sensor can be added easily if wanted.

What it does:
– Listens & counts pulses for 60 seconds
– After 60 seconds writes this value to InfluxDB
– Updates display with current metrics. (last 60 second reading, average reading, max reading, estimated dosage & the current IP address)

Connecting it up:

LCD PinESP8266 Labelled PinESP8266 GPIO PinGeiger Detector Board
1 – RSTD0GPIO 16
2 – CED1GPIO 5
3 – DCD2GPIO 4
4 – DIND3GPIO 0
5 – CLKD4GPIO 2
6 – Vcc3.3V3.3V
7 – Backlight3.3V (on)3.3V
8 – GroundGroundGroundGround
D5GPIO 14Int (interrupt)
VU/ 5VVU / 5V5V

Code:
See latest code at my GitHub (or below):
– Requires Adafruit libraries. Link 1, Link 2
– Requires Running Average Library
– Requires InfluxDb library

#include <ESP8266WiFi.h>
#include <InfluxDb.h>
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_PCD8544.h>
#include "RunningAverage.h"

#define INFLUXDB_HOST "192.168.1.XXX"
#define WIFI_SSID "XXXXXXXXXXXX"
#define WIFI_PASS "XXXXXXXXXXXX"
#define DATABASE "XXXXXXXXXXXX"
#define MEASUREMENT "XXXXXXXXXXXX"
#define DEVICE "XXXXXXXXXXXX"
#define ID "Geiger_Counter"
#define LOG_PERIOD 60000

Influxdb influx(INFLUXDB_HOST);
Adafruit_PCD8544 display = Adafruit_PCD8544(2, 0, 4, 5, 16); //LCD 1.5 Inch (Nokia 5110)84×48 (x,y) pixels
RunningAverage raMinute(60);

//################
int debug = 1; //#
//################

int loopCount = 0;
int cpm = 0;
int cpm_max = 0;
int cpm_1hr_avg = 0;
int cpm_ravg = 0;
int counts = 0;
int cal_factor = 1;
int wifiStatus;

unsigned long currentMillis;
unsigned long previousMillis; //variable for time measurement

void setup(){                                               
  Serial.begin(9600);      // start serial monitor
  delay(1000);
  Serial.println("");
  Serial.println("");
  Serial.println("Setup Routine of ESP8266 Geiger Counter");

  display.begin();
  display.setContrast(55);
  display.display();        // show adafruid splashscreen
  delay(2000);
  display.clearDisplay();   // clears the screen and buffer

  pinMode(LED_BUILTIN, OUTPUT); //D4
  digitalWrite(LED_BUILTIN, HIGH); //Turns it off

  pinMode(14, INPUT_PULLUP);                  // set pin INT0 input for capturing GM Tube events / GPIO5 = D1
  attachInterrupt(14, tube_pulse, FALLING); //defines interrupts

  raMinute.clear();

  influx.setDb(DATABASE);

  display.setTextSize(1);
  display.setTextColor(BLACK);
  display.setCursor(0,0); display.setTextSize(1);display.print("Up Hrs: ");display.setCursor(48,0);display.print("0");
  display.setCursor(0,9); display.setTextSize(1);display.print("CPMi 1m:");
  display.setCursor(0,17);display.setTextSize(1);display.print("CPM avg:");
  display.setCursor(0,25);display.setTextSize(1);display.print("CPM Max:");
  display.setCursor(0,33);display.setTextSize(1);display.print("uSv/hr: ");
  display.setCursor(0,41);display.setTextSize(1);display.print("Not Connected");
  display.display();

    wifiStatus = WiFi.status();
    if (wifiStatus != WL_CONNECTED) {   
      new_connection();
    }
    else {
        display.fillRect(0,41,84,48, WHITE);
        display.setCursor(0,41);
        display.print(WiFi.localIP());
        display.display();
    }
    

  if (debug == 1) {Serial.println("Setup Complete.");}
}

void loop(){                                              
  currentMillis = millis();
  if(currentMillis - previousMillis > LOG_PERIOD){
    cpm = counts * cal_factor;                        

    raMinute.addValue(cpm);
    cpm_ravg = raMinute.getAverage();

    if (cpm > cpm_max){
      cpm_max = cpm;
    }
    
    Serial.print("CPM: ");                         
    Serial.println(cpm);                          

    display.fillRect(48,0,40,40, WHITE);
    display.display();
    display.setCursor(48,0);display.print(loopCount*.0166);
    display.setCursor(48,9);display.print(cpm);
    display.setCursor(48,17);display.print(cpm_ravg);
    display.setCursor(48,25);display.print(cpm_max);
    display.setCursor(48,33);display.print(cpm_ravg*0.0057);
    display.display();

    Serial.println("Attempting to write to DB");   
    counts = 0;

    InfluxData row(MEASUREMENT);
    row.addTag("Device", DEVICE);
    row.addTag("ID", ID);
    row.addValue("CPM", cpm);  
    row.addValue("LoopCount", loopCount);
    row.addValue("RandomValue", random(0, 100));
  
    wifiStatus = WiFi.status();
    while ( wifiStatus != WL_CONNECTED )
        {
          new_connection();
        }
  
    influx.write(row);
    if (debug == 1) {Serial.println("Wrote Data.");}
  
    //WiFi.mode(WIFI_OFF); // Probably turn off Wifi if want to save battery
    //WiFi.forceSleepBegin();
    //delay( 1 );
  
    status_blink();
    previousMillis = currentMillis;
    loopCount++;
  }
}

ICACHE_RAM_ATTR        //Needed to fix ISR not in IRAM boot error
void tube_pulse(){     //procedure for capturing events from interrupt
  counts++;
}

void new_connection() {
  
    wifiStatus = WiFi.status();
    
    if (wifiStatus != WL_CONNECTED) {   
       
        WiFi.mode(WIFI_STA);
        WiFi.begin(WIFI_SSID, WIFI_PASS);
        int loops = 0;
        int retries = 0;
        display.fillRect(0,41,84,48, WHITE);
        display.setCursor(0,41);
        display.print("Not Connected");
        display.display();
       
        while (wifiStatus != WL_CONNECTED)
        {
          retries++;
          if( retries == 300 )
          {
              if (debug == 1) {Serial.println( "No connection after 300 steps, powercycling the WiFi radio. I have seen this work when the connection is unstable" );}
              WiFi.disconnect();
              delay( 10 );
              WiFi.forceSleepBegin();
              delay( 10 );
              WiFi.forceSleepWake();
              delay( 10 );
              WiFi.begin( WIFI_SSID, WIFI_PASS );
          }
          if ( retries == 600 )
          {
              if (debug == 1) {Serial.println( "No connection after 600 steps. WiFi connection failed, disabled WiFi and waiting for a minute" );}
              WiFi.disconnect( true );
              delay( 1 );
              WiFi.mode( WIFI_OFF );
              WiFi.forceSleepBegin();
              delay( 10 );
              retries = 0;
              
              if( loops == 3 )
              {
                  if (debug == 1) {Serial.println( "That was 3 loops, still no connection so let's go to deep sleep for 2 minutes" );}
                  Serial.flush();
                  ESP.deepSleep( 120000000, WAKE_RF_DISABLED );
              }     
          }
          delay(50);
          wifiStatus = WiFi.status();
        }
        
        wifiStatus = WiFi.status();
        Serial.print("WiFi connected, IP address: ");Serial.println(WiFi.localIP());
        display.fillRect(0,41,84,48, WHITE);
        display.setCursor(0,41);
        display.print(WiFi.localIP());
        display.display();
    }
}

void status_blink() {
  digitalWrite(LED_BUILTIN, LOW);   // Turn the LED on (Note that LOW is the voltage level   
  delay(100);
  digitalWrite(LED_BUILTIN, HIGH);   // Turn the LED on (Note that LOW is the voltage level
  delay(100);
  digitalWrite(LED_BUILTIN, LOW);   // Turn the LED on (Note that LOW is the voltage level   
  delay(100);
  digitalWrite(LED_BUILTIN, HIGH);   // Turn the LED on (Note that LOW is the voltage level
}

ToDo:
– Comment code
– Add in check at start of code to see if tube functioning.
– I would like to update the running average to 60min average but not enough time to currently do these 10 lines of code.
– Add in control (on/off ) for the LCD backlight, buzzer & Wifi for battery consumption.
– Perhaps would be nice to log to SD card also, not sure if I still have enough I/O for that.
– The ‘case’ is a very rough and not worthy of sharing, a nicer more bespoke would be ideal.
– Add radiation symbol on splash screen.

Resources I used:
https://mightyohm.com/blog/2014/11/a-spotters-guide-to-the-sbm-20-geiger-counter-tube/

That’s it!

ESP8266 & Stepper Motors

Nothing outrageously complicated but when I used the default arduino stepper library to control my 28BYJ-48 stepper motors on my ESP8266 they only turned one direction. With modifications to the library it was possible to get one stepper motor working correctly but the ESP crashed if I tried to control 2 stepper motors, solution sketch below:

What did work:
– Sketch below. (Note: No fancy acceleration/deceleration but I didnt need it)

int pos_rot = 0;  
int pos_elev = 0; 

int rot_counter = 0;
int elev_counter = 0;

int next_rot = -1;
int next_elev = 1;

int step_delay = 10; //Delay between steps in ms

const int motor_pin_1 = 16; // ESP D0
const int motor_pin_2 = 5;  // ESP D1
const int motor_pin_3 = 4;  // ESP D2
const int motor_pin_4 = 0;  // ESP D3
const int motor_pin_5 = 2;  // ESP D4
const int motor_pin_6 = 14; // ESP D5
const int motor_pin_7 = 12; // ESP D6
const int motor_pin_8 = 13; // ESP D7

void setup() {
  pinMode(motor_pin_1, OUTPUT); // Blue
  pinMode(motor_pin_2, OUTPUT); // Pink
  pinMode(motor_pin_3, OUTPUT); // Yellow
  pinMode(motor_pin_4, OUTPUT); // Orange
  pinMode(motor_pin_5, OUTPUT);
  pinMode(motor_pin_6, OUTPUT);
  pinMode(motor_pin_7, OUTPUT);
  pinMode(motor_pin_8, OUTPUT);
  
  // Begin Serial communication at a baud rate of 9600:
  Serial.begin(115200);
  delay(100);Serial.println("Leaving Setup");delay(100);
}

void loop() {
    //test_sweep();
    rot(2038);delay(2000);rot(-2038);   // 2038 steps clockwise followed by the same counterclockwise
    elev(1019);delay(2000);elev(-1019); // 1019 steps clockwise followed by the same counterclockwise
}

void test_sweep(){
      Serial.print("Rotating CCW, pos_rot = ");Serial.println(pos_rot);

    while (pos_rot < 2038) {
    rot(1);
    pos_rot = pos_rot + 1;
 
        if ((pos_rot < 1019) && ((pos_rot % 2) == 0)) {
          elev(2);          
        }
        else if ((pos_rot > 1019) && ((pos_rot % 2) == 0)) {
          elev(-2);
        }
  }
    while (pos_rot > 0) {
    rot(-1);
    pos_rot = pos_rot - 1;
 
        if ((pos_rot > 1019) && ((pos_rot % 2) == 0)) {
          elev(2);
        }
        else if ((pos_rot < 1019) && ((pos_rot % 2) == 0)) {
          elev(-2);
        }
  }
}

void elev(int num_steps) {
    if (num_steps > 0) {
        while (elev_counter < num_steps) {        
          elev_step(next_elev);
          next_elev = next_elev +1;
          if (next_elev > 7) { next_elev = 0;}
          elev_counter = elev_counter +1;
        }
    }
    else {
        num_steps = abs(num_steps);
        while (elev_counter < num_steps) {        
          elev_step(next_elev);
          next_elev = next_elev - 1;
          if (next_elev < 0) { next_elev = 7;}
          elev_counter = elev_counter +1;
        }    
    }
    elev_counter = 0;
}

void rot(int num_steps) {
    if (num_steps > 0) {
    
        while (rot_counter < num_steps) {        
          rot_step(next_rot);
          next_rot = next_rot +1;
          if (next_rot > 7) { next_rot = 0;}
          rot_counter = rot_counter +1;
        }
    }
    else {
        num_steps = abs(num_steps);
        while (rot_counter < num_steps) {        
          rot_step(next_rot);
          next_rot = next_rot - 1;
          if (next_rot < 0) { next_rot = 7;}
          rot_counter = rot_counter +1;
        }    
    }

    rot_counter = 0;  
}

void rot_step(int go_step) {
  switch (go_step) {
      case 0:  // 0001
        digitalWrite(motor_pin_1, 0);  // Blue
        digitalWrite(motor_pin_2, 0);  // Pink
        digitalWrite(motor_pin_3, 0);  // Yellow
        digitalWrite(motor_pin_4, 1);  // Orange
      break;
      case 1:  // 0011
        digitalWrite(motor_pin_1, 0);
        digitalWrite(motor_pin_2, 0);
        digitalWrite(motor_pin_3, 1);
        digitalWrite(motor_pin_4, 1);
      break;
      case 2:  //0010
        digitalWrite(motor_pin_1, 0);
        digitalWrite(motor_pin_2, 0);
        digitalWrite(motor_pin_3, 1);
        digitalWrite(motor_pin_4, 0);
      break;
      case 3:  //0110
        digitalWrite(motor_pin_1, 0);
        digitalWrite(motor_pin_2, 1);
        digitalWrite(motor_pin_3, 1);
        digitalWrite(motor_pin_4, 0);
      break;
      case 4:  // 0100
        digitalWrite(motor_pin_1, 0);
        digitalWrite(motor_pin_2, 1);
        digitalWrite(motor_pin_3, 0);
        digitalWrite(motor_pin_4, 0);
      break;
      case 5:  // 1100
        digitalWrite(motor_pin_1, 1);
        digitalWrite(motor_pin_2, 1);
        digitalWrite(motor_pin_3, 0);
        digitalWrite(motor_pin_4, 0);
      break;
      case 6:  //1000
        digitalWrite(motor_pin_1, 1);
        digitalWrite(motor_pin_2, 0);
        digitalWrite(motor_pin_3, 0);
        digitalWrite(motor_pin_4, 0);
      break;
      case 7:  //1001
        digitalWrite(motor_pin_1, 1);
        digitalWrite(motor_pin_2, 0);
        digitalWrite(motor_pin_3, 0);
        digitalWrite(motor_pin_4, 1);
      break;
    }
    delay(step_delay);
}

void elev_step(int go_step) {
  //Serial.println(go_step);
  switch (go_step) {
      case 0:  // 0001
        digitalWrite(motor_pin_5, LOW);
        digitalWrite(motor_pin_6, LOW);
        digitalWrite(motor_pin_7, LOW);
        digitalWrite(motor_pin_8, HIGH);
      break;
      case 1:  // 0011
        digitalWrite(motor_pin_5, LOW);
        digitalWrite(motor_pin_6, LOW);
        digitalWrite(motor_pin_7, HIGH);
        digitalWrite(motor_pin_8, HIGH);
      break;
      case 2:  //0010
        digitalWrite(motor_pin_5, LOW);
        digitalWrite(motor_pin_6, LOW);
        digitalWrite(motor_pin_7, HIGH);
        digitalWrite(motor_pin_8, LOW);
      break;
      case 3:  //0110
        digitalWrite(motor_pin_5, LOW);
        digitalWrite(motor_pin_6, HIGH);
        digitalWrite(motor_pin_7, HIGH);
        digitalWrite(motor_pin_8, LOW);
      break;
      case 4:  // 0100
        digitalWrite(motor_pin_5, LOW);
        digitalWrite(motor_pin_6, HIGH);
        digitalWrite(motor_pin_7, LOW);
        digitalWrite(motor_pin_8, LOW);
      break;
      case 5:  // 1100
        digitalWrite(motor_pin_5, HIGH);
        digitalWrite(motor_pin_6, HIGH);
        digitalWrite(motor_pin_7, LOW);
        digitalWrite(motor_pin_8, LOW);
      break;
      case 6:  //1000
        digitalWrite(motor_pin_5, HIGH);
        digitalWrite(motor_pin_6, LOW);
        digitalWrite(motor_pin_7, LOW);
        digitalWrite(motor_pin_8, LOW);
      break;
      case 7:  //1001
        digitalWrite(motor_pin_5, HIGH);
        digitalWrite(motor_pin_6, LOW);
        digitalWrite(motor_pin_7, LOW);
        digitalWrite(motor_pin_8, HIGH);
      break;
    }
    delay(step_delay);
}

That’s it!

ESP8266 Deep Sleep Energy Saving

After reading many post of people getting months of ESP8266 running time off batteries I decided to set up my own to see why my battery life was terrible:

Parts:
Node MCU ESP8266 (CH349G Serial Chip, AMS1117 Voltage Regulator)
LiPo Battery: 2S 850mAh (7.4V)
ADS1115 ADC (to measure voltage, 500K Voltage divider)

Test setup:
Two ESP8266 setups were completed, one ESP was standard and the other had the LED and Serial Chip disconnected to conserve Battery.

Test Program:
ESP Wake every 20 seconds (with radio disabled)
Take voltage reading and store in RTC memory
Deep Sleep
……………………………………………………………..
Every 5 minutes (15 wake cycles)
Take voltage reading
Connect to network and transmit all data to Influx Database.
Disconnect from network
Deep Sleep

Results:
You can see from the below screenshot the battery voltage over the duration of the test:

Unmodified ESP8266:
Time from 8.36V to 7.28V (97% to 7% of Li-Po capacity) was 87hrs and 20mins (3.6 Days)

Modified ESP8266: (No LED or Serial Chip)
Time from 8.36V to 7.28V (97% to 7% of Li-Po capacity) was 101hrs and 16mins (4.2 Days)

Conclusion:
Months of usage seem far from achievable with a minimal setup and all precautions taken. Actually the ESP seems pretty unusable on a battery for anything more than a measurement every few hours.

Further Improvements:
The stock voltage regulator is a known power drain, an alternative is recommended but I did not get around to that yet.

That’s it!

ESP8266 logging to InfluxDB

The ESP8266 is a $5 IOT device with huge capabilities. In this post we will log data to a remote Influx database running on a RaspberryPi.

I am programming the ESP8266 in the Arduino IDE, the ESP8266 library is required, you can find it here. I have a test code file (of copy from below) that you can upload after entering your InfluxDB I.P. Address, SSID & Password and it will start logging data immediately.

#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <InfluxDb.h>

#define INFLUXDB_HOST "192.168.1.1"   //Enter IP of device running Influx Database
#define WIFI_SSID "SSID"              //Enter SSID of your WIFI Access Point
#define WIFI_PASS "PASSWORD"          //Enter Password of your WIFI Access Point

ESP8266WiFiMulti WiFiMulti;
Influxdb influx(INFLUXDB_HOST);

void setup() {
  Serial.begin(9600);
  WiFiMulti.addAP(WIFI_SSID, WIFI_PASS);
  Serial.print("Connecting to WIFI");
  while (WiFiMulti.run() != WL_CONNECTED) {
    Serial.print(".");
    delay(100);
  }
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());

  influx.setDb("esp8266_test");

  Serial.println("Setup Complete.");
}

int loopCount = 0;

void loop() {
  loopCount++;

  InfluxData row("data");
  row.addTag("Device", "ESP8266");
  row.addTag("Sensor", "Temp");
  row.addTag("Unit", "Celsius");
  row.addValue("LoopCount", loopCount);
  row.addValue("RandomValue", random(10, 40));

  influx.write(row);
  delay(5000);
}

The Arduino Serial Terminal will display something like the below so you can if it is working. (My previous tutorial shows setting up InfluxDB, ensure you have the database “esp8266_test” created as we are going to write to that.)

 --> writing to esp8266_test:
data,Device=ESP8266,Sensor=Temp,Unit=Celsius LoopCount=256.00,RandomValue=37.00
 <-- Response: 204 ""
 --> writing to esp8266_test:
data,Device=ESP8266,Sensor=Temp,Unit=Celsius LoopCount=257.00,RandomValue=20.00
 <-- Response: 204

On the Influx Database we can look at the data by:

influx
USE esp8266_test
select * from data limit 50

Below you can see the export from my database (I have shortened the time field for neatness). You can see I reset the ESP8266 a couple of times due to the LoopCount value.

time        Device  LoopCount RandomValue Sensor Unit    
----        ------  --------- ----------- ------ ----   
52808175073 ESP8266 1         38          Temp   Celsius                              
63108846141 ESP8266 2         35          Temp   Celsius                              
69802517277 ESP8266 1         13          Temp   Celsius                              
79892112240 ESP8266 2         12          Temp   Celsius                              
89961602267 ESP8266 3         14          Temp   Celsius                              
99998928411 ESP8266 4         22          Temp   Celsius                              
10053683452 ESP8266 5         10          Temp   Celsius                              
20120378415 ESP8266 6         28          Temp   Celsius                              
30175745403 ESP8266 7         14          Temp   Celsius                              
40732248123 ESP8266 8         38          Temp   Celsius                              
51232948067 ESP8266 9         15          Temp   Celsius                              
61322347831 ESP8266 10        13          Temp   Celsius                              
71424432515 ESP8266 11        19          Temp   Celsius                              
84740185749 ESP8266 1         18          Temp   Celsius                              
94790343615 ESP8266 2         21          Temp   Celsius                              
04839215465 ESP8266 3         13          Temp   Celsius                              
31864448941 ESP8266 1         32          Temp   Celsius                              
41956355523 ESP8266 2         36          Temp   Celsius                              
52018136222 ESP8266 3         30          Temp   Celsius                              
62083037888 ESP8266 4         22          Temp   Celsius       

That’s it!