Blinky's Lab

Monitoring

All posts tagged Monitoring by Blinky's Lab
  • Posted on

    Archived from radmon.org + more - originally posted 18/09/2017

    I've been working on an air quality monitor over the weekend. I have a working model that samples and logs every 5 minutes into various .csv files that are used for drawing graphs. It is a work in progress, still much to do. Take a look here: https://www.schmoozie.co.uk/airquality/

    Please ignore the data as it is not accurate as is just for testing purposes at the moment. I'll be dumping the collected data and starting from scratch when I get this into an enclosure and mounted properly outside. (All data shown on the page is good.)

    enter image description here

    The hardware is very simple. An ESP8266 (Wemos D1 Mini), I2C LCD driver, 16x2 LCD and a Nova SDS011 PM2.5/PM10 sensor. The software on the ESP8266 simply wakes up the sensor for 20 seconds along with the built in Wi-Fi, then records the last sample, puts the sensor to sleep for 5 minutes and shoots it to my web server where some PHP does the magic and writes the log files etc. I'm pretty happy with it so far.

    This did get properly housed and installed and with a BME280 (temp/humidity/pressure) sensor alongside. However the BME 280 failed not long after installing. Sadly I don't have any pictures of the install yet. Here is a good example of it working on bonfire night 2017: enter image description here

    The Arduino code: (is old so not guaranteed to compile with recent libraries)

    #include <LiquidCrystal_I2C.h>
    #include <ESP8266WiFi.h>
    #include <WiFiClientSecure.h>
    #include <Wire.h>
    #include <SPI.h>
    #include <SDS011.h>
    #include <Adafruit_Sensor.h>
    #include <Adafruit_BMP280.h>
    #include <Adafruit_Si7021.h>
    
    // Time to sleep (in milliseconds):
    const int sleepTime = 280000; //280 seconds sleep time
    
    const char* ssid = "Dreamtech";
    const char* password = "lizbo5601";
    const char* host = "172.16.100.100";
    const int httpPort = 80;
    
    float p10, p25;
    int error;
    int countdown;
    bool firstRun = true;
    String str_pm25;
    String str_pm10;
    String str_temp;
    String str_humidity;
    String str_pressure;
    unsigned int stringLength;
    const int greenLED = D0;
    const int redLED = D8;
    const int button = D3;
    bool sampleNow = true;
    unsigned long nextSample;
    int buttonStep = 0;
    int buttonState = 0;
    unsigned long debounce;
    unsigned long hold;
    int menuStep = 1;
    
    bool debug = true;
    bool doSample = true;
    
    SDS011 sds;
    Adafruit_BMP280 bmp; // I2C
    Adafruit_Si7021 sensor = Adafruit_Si7021();
    
    // Set the LCD address to 0x27 for a 16 chars and 2 line display
    LiquidCrystal_I2C lcd(0x27, 16, 2);
    
    void reconnect() {
      if (debug) {
        Serial.print("Connecting to ");
        Serial.println(ssid);
      }
      if (WiFi.status() != WL_CONNECTED) {
        WiFi.begin(ssid, password);
        while (WiFi.status() != WL_CONNECTED) {
          delay(500);
          if (debug) {
            Serial.print(".");
          }
        }
      }
      if (debug) {
        Serial.println("");
        Serial.println("WiFi connected");
        Serial.print("IP address: ");
        Serial.println(WiFi.localIP());
      }
    }
    
    void http_post(String data) {
      if (debug) {
        Serial.print("connecting to ");
        Serial.println(host);
      }
      // Use WiFiClient class to create TCP connections
      WiFiClient client;
      if (!client.connect(host, httpPort)) {
        if (debug) {
          Serial.println("connection failed");
        }
        return;
      }
      String url = "/airquality/postdata.php";
      if (debug) {
        Serial.print("requesting URL: ");
        Serial.println(url);
      }
      String body = String("POST ") + url + " HTTP/1.0\r\n" +
                    "Host: " + host + "\r\n" +
                    "Content-Length: " + data.length() + "\r\n"
                    "Content-Type: application/x-www-form-urlencoded\r\n" +
                    "\r\n" +
                    data;
      if (debug) {
        Serial.print("body: ");
        Serial.println(body);
      }
      client.print(body);
      if (debug) {
        Serial.println("request sent");
      }
      while (client.connected()) {
        String line = client.readStringUntil('\n');
        if (line == "\r") {
          if (debug) {
            Serial.println("headers received");
          }
          break;
        }
      }
      if (debug) {
        Serial.println("closing connection");
      }
    }
    
    void submit_air_conditions(String pm25, String pm10, String temp, String humidity, String pressure) {
      http_post("pm25=" + pm25 + "&pm10=" + pm10 + "&temp=" + temp + "&humidity=" + humidity + "&pressure=" + pressure);
    }
    
    void displayPM (String pm25, String pm10) {
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("PM2.5: " + pm25);
      lcd.setCursor(0, 1);
      lcd.print("PM10: " + pm10);
    }
    
    void displayTempHum (String temp, String hum) {
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("Temp: " + temp + " c");
      lcd.setCursor(0, 1);
      lcd.print("Hum: " + hum + " %");
    }
    
    void displayPres (String pres) {
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("Pressure: ");
      lcd.setCursor(0, 1);
      lcd.print(pres + " hPa");
    }
    
    void setup() {
      Serial.begin(115200);
      WiFi.mode(WIFI_STA);
      pinMode(greenLED, OUTPUT); //Green LED
      pinMode(redLED, OUTPUT); //Red LED
      pinMode(button, INPUT); //Button
      sds.begin(D6, D7);
      if (!bmp.begin()) {
        if (debug) {
          Serial.println(F("Could not find a valid BMP280 sensor, check wiring!"));
        }
        //while (1);
      }
      sensor.begin();
      lcd.begin();
      lcd.backlight();
      lcd.print("   SDS011 Air");
      lcd.setCursor(0, 1);
      lcd.print("  Quality Meter");
      delay(2000);
    }
    
    void loop() {
      if (digitalRead(button) == HIGH) {
        if (buttonState == 1) {
          hold = 0;
          if (millis() > debounce + 5000) {
            buttonStep = 0;
            if (menuStep != 1) {
              if (debug) {
                Serial.println("Main Display");
              }
            displayPM (str_pm25, str_pm10);
            }
            buttonState = 0;
            menuStep = 1;
          }
        }
      }
      if (digitalRead(button) == LOW) {
        buttonState = 1;
        if (hold <= millis()) {
          if (hold != 0) {
            digitalWrite(redLED, HIGH);
            if (debug) {
              Serial.println("CLEAR!");
              Serial.println("");
            }
            lcd.clear();
            lcd.setCursor(0, 0);
            lcd.print("Reserved");
            lcd.setCursor(0, 1);
            lcd.print("Function");
            hold = millis() + 3000;
            buttonStep = 0;
            menuStep = 1;
            delay(2000);
            if (debug) {
              Serial.println("Main Display");
            }
            displayPM (str_pm25, str_pm10);
            digitalWrite(redLED, LOW);
            return;
          }
        }
        if (debounce < millis()) {
          hold = millis() + 3000;
          if (debug) {
            Serial.println("Button Hold = " + String(hold));
          }
          buttonStep += 1;
          if (buttonStep >= 3) {
            buttonStep = 0;
          }
          if (debug) {
            Serial.println(buttonStep);
          }
        }
        if (buttonStep == 0) {
          if (menuStep == 0) {
            if (debug) {
              Serial.println("Main display");
            }
            displayPM (str_pm25, str_pm10);
            menuStep = 1;
          }
        }
        else if (buttonStep == 1) {
          if (menuStep == 1) {
            if (debug) {
              Serial.println("Temp Hum");
            }
            displayTempHum (str_temp, str_humidity);
            menuStep = 2;
          }
        }
        else if (buttonStep == 2) {
          if (menuStep == 2) {
            if (debug) {
              Serial.println("Pressure");
            }
            displayPres (str_pressure);
            menuStep = 0;
          }
        }
        debounce = millis();
        debounce += 200;
      }
      if (nextSample <= millis()) {
        sampleNow = true;
      }
      if (sampleNow) {
        if (doSample) {
          reconnect();
          sds.wakeup();
          if (firstRun) {
            countdown = 30;
            if (debug) {
              Serial.println("Calibrating SDS011 (first run 30 sec)");
            }
            for (int x = 0; x < 5; x++) {
              digitalWrite(greenLED, HIGH);
              lcd.clear();
              lcd.setCursor(0, 0);
              lcd.print("Starting up");
              lcd.setCursor(0, 1);
              lcd.print(String(countdown) + " ");
              countdown -= 1;
              digitalWrite(greenLED, LOW);
              delay(1000);
              digitalWrite(greenLED, HIGH);
              lcd.setCursor(11, 0);
              lcd.print(".");
              lcd.setCursor(0, 1);
              lcd.print(String(countdown) + " ");
              countdown -= 1;
              delay(20);
              digitalWrite(greenLED, LOW);
              delay(980);
              digitalWrite(greenLED, HIGH);
              lcd.setCursor(12, 0);
              lcd.print(".");
              lcd.setCursor(0, 1);
              lcd.print(String(countdown) + " ");
              countdown -= 1;
              delay(20);
              digitalWrite(greenLED, LOW);
              delay(980);
              digitalWrite(greenLED, HIGH);
              lcd.setCursor(13, 0);
              lcd.print(".");
              lcd.setCursor(0, 1);
              lcd.print(String(countdown) + " ");
              countdown -= 1;
              delay(20);
              digitalWrite(greenLED, LOW);
              delay(980);
              digitalWrite(greenLED, HIGH);
              lcd.setCursor(14, 0);
              lcd.print(".");
              lcd.setCursor(0, 1);
              lcd.print(String(countdown) + " ");
              countdown -= 1;
              delay(20);
              digitalWrite(greenLED, LOW);
              delay(980);
              digitalWrite(greenLED, HIGH);
              lcd.setCursor(15, 0);
              lcd.print(".");
              lcd.setCursor(0, 1);
              lcd.print(String(countdown) + " ");
              countdown -= 1;
              delay(20);
              digitalWrite(greenLED, LOW);
              delay(980);
              digitalWrite(greenLED, HIGH);
            }
            firstRun = false;
          }
          else {
            countdown = 20;
            if (debug) {
              Serial.println("Calibrating SDS011 (20 sec)");
            }
            for (int x = 0; x < 4; x++) {
              digitalWrite(greenLED, HIGH);
              lcd.clear();
              lcd.setCursor(0, 0);
              lcd.print("Sampling");
              lcd.setCursor(0, 1);
              lcd.print(String(countdown) + " ");
              countdown -= 1;
              digitalWrite(greenLED, LOW);
              delay(1000);
              digitalWrite(greenLED, HIGH);
              lcd.setCursor(8, 0);
              lcd.print(".");
              lcd.setCursor(0, 1);
              lcd.print(String(countdown) + " ");
              countdown -= 1;
              delay(20);
              digitalWrite(greenLED, LOW);
              delay(980);
              digitalWrite(greenLED, HIGH);
              lcd.setCursor(9, 0);
              lcd.print(".");
              lcd.setCursor(0, 1);
              lcd.print(String(countdown) + " ");
              countdown -= 1;
              delay(20);
              digitalWrite(greenLED, LOW);
              delay(980);
              digitalWrite(greenLED, HIGH);
              lcd.setCursor(10, 0);
              lcd.print(".");
              lcd.setCursor(0, 1);
              lcd.print(String(countdown) + " ");
              countdown -= 1;
              delay(20);
              digitalWrite(greenLED, LOW);
              delay(980);
              digitalWrite(greenLED, HIGH);
              lcd.setCursor(11, 0);
              lcd.print(".");
              lcd.setCursor(0, 1);
              lcd.print(String(countdown) + " ");
              countdown -= 1;
              delay(20);
              digitalWrite(greenLED, LOW);
              delay(980);
              digitalWrite(greenLED, HIGH);
            }
          }
          error = sds.read(&p25, &p10);
          if (!error) {
            str_pm25 = String(p25);
            stringLength = (str_pm25.length());
            str_pm25.remove(stringLength - 1);
            str_pm10 = String(p10);
            stringLength = (str_pm10.length());
            str_pm10.remove(stringLength - 1);
            str_temp = String(sensor.readTemperature(), 2);
            str_humidity = String(sensor.readHumidity(), 2);
            str_pressure = String(bmp.readPressure() / 100);
            if (debug) {
              Serial.println("Air Quality:");
              Serial.println("PM2.5 = " + str_pm25 + " ug/m3");
              Serial.println("PM10 = " + str_pm10 + " ug/m3");
              Serial.println("Temp = " + str_temp + " *c");
              Serial.println("Humidity = " + str_humidity + " %");
              Serial.println("Pressure = " + str_pressure + " hPa");
            }
            submit_air_conditions(str_pm25, str_pm10, str_temp, str_humidity, str_pressure);
            displayPM (str_pm25, str_pm10);
          }
          else {
            if (debug) {
              Serial.println("Error reading sensor");
            }
            lcd.clear();
            lcd.setCursor(0, 0);
            lcd.print("Sensor read");
            lcd.setCursor(0, 1);
            lcd.print("error");
          }
          if (debug) {
            Serial.println("Sleep(" + String(sleepTime) + " milliseconds)\n\n");
          }
          sds.sleep();
          digitalWrite(greenLED, LOW);
        }
        else {
          if (debug) {
            Serial.println("Sampling disabled");
          }
        }
        nextSample = millis() + sleepTime;
        if (debug) {
          Serial.println("Millis: " + String(millis()));
          Serial.println("Next Sample: " + String(nextSample));
        }
        sampleNow = false;
      }
    }
    
    
  • Posted on
    Increase in Radiation During Downpour

    Archived from radmon.org - originally posted 19/07/2017

    Blackpool UK - 19 July 2017 - Started around 17:00 BST / 16:00 UTC

    During quite a downpour today, 20mm in one hour. At it's peak was 65mm/h. Background radiation increased by approx 50% / 11CPM over a 2.5hour period. Not much by any means but still an indication there was some radioactive substance in the rain.

    Please note the Radmon graphs are UTC and the rain graphs are BST (UTC + 1) and so there is an hour difference between them.

    enter image description here

    enter image description here

    enter image description here

    enter image description here

    What caused it? I could be anyone's guess. Radon causing decay products to get caught up in the rain? Radioactive cloud blowing over from somewhere? Radioactive UFO floating about somewhere overhead? 😂 It should be noted that the difference from ~21 CPM to ~30 CPM is miniscule. It is so tiny it causes no concern whatsoever. I found it interesting that the slight rise coincided with a downpour.

  • Posted on

    Archived from radmon.org - originally posted 04/04/2016

    I have finished up building/setting up my home radiation monitoring station and is all up and running. :cheer:

    https://www.schmoozie.co.uk/radmon/ (currently offline.)

    enter image description here

    I won't go into too much detail as it is pretty much just a NetIO GC10, in an enclosure with a couple of buttons and switches added. It is mounted outside under a canopy on my workshop so rain is no issue and I have used all sealed switches/buttons etc with rubber gaskets on each to seal it up nicely. The window for the tube at the front is covered with some Kapton tape to seal that up also. Power is taken from a 5v PSU inside the workshop but here is the interesting thing, (for me anyway) the RS232 output is sent over wireless transceivers to my computer in the house at the other end of the garden. I'm pretty happy with it so far and the only thing I want to do to finish it is either paint it or cover in some kind of vinyl or similar to make it pretty.

    I used two HC11 RS232 transceivers, one connected to the GC10 and the other connected to a USB FTDI adapter (USB <> RS232 adapter). The transceivers work very well so far hardly missing a beat. They worked for me right out of the box with no additional configuration needed. Check them out at this link , but you can get them much cheaper on ebay. I paid £2.96 each including shipping for mine, so it was probably cheaper than buying actual cable to run the RS232 and saved all the hassle or running a cable etc.

    This is the receiver that sits on my windowsill: enter image description here

    I put a bag of silica gel desiccant inside the enclosure to aid in keeping moisture at bay. I have done this for years whenever mounting any kind of enclosure outside and it works a treat, keeping everything dry and electrical contacts clean and shiny.

    Some years ago I went through a period at work of designing and building custom switches/keypads for use in sauna, steam rooms and swimming pools. They were only basic with a couple of buttons on them but they were in very hostile (to electronics) environments. The circuits were potted and that kept the circuit happy and the switches/buttons were hermetically sealed. The case covers were all gasketed but this left the main connections to the 'elements' inside the enclosure. In testing these would last for weeks even months, but they would eventually fail. The failure mode was corrosion on the incoming cable terminals where it was connected to the circuit. At first The design was changed and a cable was soldered directly onto the board and potted in. This worked great provided the installers would install them correctly, but they didn't. Mainly they would cut down the cable, use a terminal block and just shove it behind the switch/keypad. Some even drilled a hole in the back of the enclosure and put the terminal block inside the enclosure pretty much leaving it to the elements.

    I changed the design slightly so the cable was potted in and soldered to the board, like the last, but the cable was only 3" or so with a fairly robust connector. I added a bag of silica gel (as big as I could fit in the enclosure) and that pretty much solved the issues of the terminals/connectors corroding. I learned that no matter how much you think the enclosure is sealed, unless it is airtight, it will eventually pull in some moisture. This is down to the fact that as the enclosure and contents heat up the air expands and may push some out. Then it will cool down, the air inside cools and contracts, creating a very small vacuum in the enclosure. Air from outside with a higher moisture content will be sucked into the enclosure and with that happening every day the enclosure would eventually get enough moisture inside it would condense and corrode the contacts. This was in very harsh environments especially in steam rooms but the same will happen outside, it will just take much longer. I usually change silica gel bags in my stuff outside every year or so and that keeps them nice and happy.

    I have been playing about a bit trying to make my website 'radmon' page look nifty. I'm pretty pleased with the page so far.

    I use radlog to transfer the locally generated graph to my website. I grab the european map from radlog.org in an iframe and then grab each of the six history/trend data images from radlog.org. Because I like a black background and use that on my page, the images right from radlog.org have white backgrounds and look poor, so I run them through a PHP image filter to change the colours negative, cache them locally and display on my page.

    enter image description here