Project in progress
An Environmental Monitoring Tool

An Environmental Monitoring Tool © GPL3+

An environmental monitoring system using a Nano, with GPS, BME680, VEML6070, MiCS-5524, BH1750, a 128x64 OLED, and NeoPixels.

  • 4,224 views
  • 3 comments
  • 19 respects

Components and supplies

Apps and online services

About this project

I created this device to be personal. It will eventually be paired with a Raspberry Pi with a 3.5" LCD to also display radar. The end product will allow me to see the current weather conditions no matter where I am at, including live radar using current GPS data.

It currently uses:

  • Arduino Nano (Rev3.0)
  • Adafruit BME680 (Temperature, humidity, barometric pressure, & AQI)
  • Adafruit MiCS-5524 Gas Sensor (CO level)
  • Adafruit VEML6070 (UV index)
  • BH1750 (Light intensity)
  • NEO 6M GPS (Location services)
  • OLED Monochrome 2.4" 128x64 I2C (Values display)
  • Adafruit NeoPixel Stick (At-a-glance display)

It will show temperature, humidity, barometric pressure, (in both inHg, & mb,) air quality index, UV index, and luminosity; along with current latitude/longitude, altitude, speed, heading, and time in UTC.

The Nano, BME680, and CO sensors are currently on an Adafruit Perma-Proto quarter size board mounted in a plastic craft container. The UV and Lux sensors are mounted to a piece of proto board and placed on the top inside of the unit.

I have encountered a bug in the code that I have not figured out yet...it seems that whenever the BME680 code is running the GPS values for altitude, FIX, ACQUIRED, and TRACKED satellites are lost. I’m still trying to figure it out.

The NeoPixels show current values in a color-coded format that, generally, follow NWS formats.

  • 0=Temp level (subjective colors)
  • 1=Pressure reading (subjective colors)
  • 2=Humidity level (subjective colors)
  • 3= GPS Fix (green-fixed/red-no fix)
  • 4=Light intensity (subjective colors)
  • 5= UV Index (follows NWS guidelines)
  • 6= CO level in PPM (subjective colors)
  • 7=Air Quality Index (follows NWS guidelines)

I tried placing the NeoPixels behind the white label, Fig. 1.1, but it was too bright. I ended up placing a strip of black electrical tape over the NeoPixels to diffuse the brightness, hence their appearance, Fig. 1.2.

The BME680 and CO sensors are both mounted internally, and on headers, Fig. 2, while the UV and Lux sensors are mounted on the top of the unit on pref-board, Fig. 3, with a piece cut out for light exposure, Fig. 4. The GPS antenna is just kinda sitting there.

My last step is to mount a 5mm fan internally to move air across the internal sensors.

Here are a few pictures of the case modifications:

Just as an FYI, you can build this project using a .96” 128x64 IIC OLED and it will drop in place without any code modifications.

Code

LocAware v4Arduino
#include <NMEAGPS.h>
#include <GPSport.h>
#include <U8x8lib.h>
#include <Adafruit_NeoPixel.h>
#include <Adafruit_Sensor.h>
#include "Adafruit_BME680.h"
#include "Adafruit_VEML6070.h"
#include <BH1750.h>

#define PIN 12
#define NUMPIXELS 8
int delayval = 50;

static NMEAGPS  gps;
static gps_fix  fix;

Adafruit_BME680 bme;
BH1750 lightMeter;
Adafruit_VEML6070 uv = Adafruit_VEML6070();
U8X8_SSD1306_128X64_NONAME_HW_I2C u8x8(/* reset=*/ U8X8_PIN_NONE);
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);


void setup() {
  pixels.begin();
  gpsPort.begin(9600);
  bme.begin();
  lightMeter.begin();
  uv.begin(VEML6070_1_T);
  //Serial.begin(9600);
  
  u8x8.begin();
  u8x8.setFont(u8x8_font_chroma48medium8_r);

  u8x8.clearDisplay();
  delay(10);
  u8x8.drawString(4,3,"LocAware");
  u8x8.drawString(3,4,"by PyreMage");
  u8x8.drawString(6,5,"v4.0");
  delay(1500);
  u8x8.clearDisplay();
  u8x8.drawString(0,0," Acquiring Fix");

  u8x8.inverse();
  
  u8x8.drawString(0, 1, "T");//Temp
  u8x8.drawString(8, 1, "P");//Pressure
  
  u8x8.drawString(0, 2, "H");//Humidity
  u8x8.drawString(8, 2, "P");//Pressure

  u8x8.drawString(0, 3, "L");//Lux
  u8x8.drawString(8, 3, "UVI");//UV index

  u8x8.drawString(0, 4, "CO");//CO Gas
  u8x8.drawString(8, 4, "AQI");//AQI 

  u8x8.drawString(0, 5, "Sp");//Speed
  u8x8.drawString(11, 5, "Hd");//Heading
  
  u8x8.drawString(0, 6, "A");//Altitude
  
  u8x8.drawString(0, 7, "F");//Fix
  u8x8.drawString(3, 7, "T");//Tracking
  u8x8.drawString(6, 7, "A");//Acquired
  u8x8.drawString(10, 7, "U");//UTC Time
  
  u8x8.noInverse();

  /* 
  Neopixels definition
  0=Temp
  1=Pressure
  2=Humidity
  4=Lux
  3=GPS Fix
  5=UVI
  6=CO
  7=AQI  
  */

  bme.setTemperatureOversampling(BME680_OS_8X);
  bme.setHumidityOversampling(BME680_OS_2X);
  bme.setPressureOversampling(BME680_OS_4X);
  bme.setIIRFilterSize(BME680_FILTER_SIZE_3);
  bme.setGasHeater(320, 150); // 320*C for 150 ms


    for(int i=0;i<NUMPIXELS;i++){
    pixels.setPixelColor(i, pixels.Color(0,255,0));
    pixels.show();
    delay(delayval);}
    for(int i=0;i<NUMPIXELS;i++){
    pixels.setPixelColor(i, pixels.Color(0,0,0));
    pixels.show();
    delay(delayval);}
}

  
void loop() {
    uint16_t lux = lightMeter.readLightLevel();
    
    char tmpchar[4]; 
    int bmetmp=(bme.readTemperature() * 9/5 + 32-10);
    dtostrf((bme.readTemperature() * 9/5 + 32-10), 3, 2, tmpchar);
    if (bmetmp>100){pixels.setPixelColor(1, pixels.Color(255,0,0));//red
    pixels.show();}else
    if (bmetmp>90  && bmetmp<99){pixels.setPixelColor(0, pixels.Color(255,125,0));//orange
    pixels.show();}else
    if (bmetmp>80  && bmetmp<89){pixels.setPixelColor(0, pixels.Color(255,255,0));//yellow
    pixels.show();}else
    if (bmetmp>70  && bmetmp<79){pixels.setPixelColor(0, pixels.Color(0,255,125));//turquiose
    pixels.show();}else
    if (bmetmp>60  && bmetmp<69){pixels.setPixelColor(0, pixels.Color(0,255,0));//green
    pixels.show();}else
    if (bmetmp<59){pixels.setPixelColor(0, pixels.Color(0,0,255));//blue
    pixels.show();}else
    if (bmetmp<1){pixels.setPixelColor(0, pixels.Color(0,0,0));pixels.show();}//off
    u8x8.drawString(1, 1, tmpchar);
    u8x8.drawString(6, 1, "F");
    delay(10);

    char prschar[4]; 
    dtostrf((bme.readPressure()/3365.3*1+.15), 3, 2, prschar);
    u8x8.drawString(9, 1, prschar);
    u8x8.drawString(14, 1, "Hg");

    char prs2char[4]; 
    dtostrf(bme.readPressure()*.01+12, 4, 2, prs2char);
    int bmeprs=(bme.readPressure()*.01+12);
    dtostrf((bme.readTemperature() * 9/5 + 32-10), 3, 2, tmpchar);
    if (bmeprs>1020){pixels.setPixelColor(1, pixels.Color(255,125,0));//orange
    pixels.show();}else
    if (bmeprs>1010  && bmeprs<1019){pixels.setPixelColor(1, pixels.Color(0,255,0));//green
    pixels.show();}else
    if (bmeprs>1000  && bmeprs<1009){pixels.setPixelColor(1, pixels.Color(0,255,125));//turquiose
    pixels.show();}else
    if (bmeprs>990  && bmeprs<9999){pixels.setPixelColor(1, pixels.Color(255,125,0));//orange
    pixels.show();}else
    if (bmeprs>970  && bmeprs<989){pixels.setPixelColor(1, pixels.Color(255,255,0));//yellow
    pixels.show();}else
    if (bmeprs<969){pixels.setPixelColor(1, pixels.Color(255,0,0));//red
    pixels.show();}else
    if (bmeprs<1){pixels.setPixelColor(1, pixels.Color(0,0,0));pixels.show();}//off
    u8x8.drawString(9, 2, prs2char);
    //u8x8.drawString(14, 1, "mB");

    char humchar[4]; 
    int bmehum=(bme.readHumidity()+14.5);
    dtostrf(bme.readHumidity()+14.5, 3, 2, humchar);
    if (bmehum>100){pixels.setPixelColor(2, pixels.Color(255,0,0));//red
    pixels.show();}else
    if (bmehum>90  && bmehum<99){pixels.setPixelColor(2, pixels.Color(255,125,0));//orange
    pixels.show();}else
    if (bmehum>80  && bmehum<89){pixels.setPixelColor(2, pixels.Color(255,255,0));//yellow
    pixels.show();}else
    if (bmehum>70  && bmehum<79){pixels.setPixelColor(2, pixels.Color(0,0,255));//blue
    pixels.show();}else
    if (bmehum>60  && bmehum<69){pixels.setPixelColor(2, pixels.Color(0,255,125));//turquiose
    pixels.show();}else
    if (bmehum<59){pixels.setPixelColor(2, pixels.Color(0,255,0));//green
    pixels.show();}else
    if (bmehum<1){pixels.setPixelColor(2, pixels.Color(0,0,0));pixels.show();}//off
    u8x8.drawString(1, 2, humchar);
    u8x8.drawString(6, 2, "%");

    char luxchar[5];
    dtostrf(lux, 5, 0, luxchar);
    //Serial.println(luxchar);
    if (lux>34001){pixels.setPixelColor(4, pixels.Color(255,0,0));//red
    pixels.show();}else
    if (lux>20001  && lux<34000){pixels.setPixelColor(4, pixels.Color(255,125,0));//orange
    pixels.show();}else
    if (lux>10001  && lux<20000){pixels.setPixelColor(4, pixels.Color(255,255,0));//yellow
    pixels.show();}else
    if (lux>5001  && lux<10000){pixels.setPixelColor(4, pixels.Color(0,0,255));//blue
    pixels.show();}else
    if (lux>2000  && lux<5000){pixels.setPixelColor(4, pixels.Color(0,255,125));//turquiose
    pixels.show();}else
    if (lux<2000){pixels.setPixelColor(4, pixels.Color(0,255,0));//green
    pixels.show();}else
    if (lux<1){pixels.setPixelColor(4, pixels.Color(0,0,0));pixels.show();}//off
    u8x8.drawString(1, 3, "     ");
    u8x8.drawString(1, 3, luxchar);

    char uvchar[4];
    uv.readUV();
    if (uv.readUV()>2055) {u8x8.drawString(11, 3, "      ");u8x8.drawString(11, 3, "Extre");
    pixels.setPixelColor(5, pixels.Color(255,0,0));//red
    pixels.show();}else
    if (uv.readUV()>1494  && uv.readUV()<2054) {u8x8.drawString(11, 3, "     ");u8x8.drawString(11, 3, "VHigh");
    pixels.setPixelColor(5, pixels.Color(255,125,0));//orange
    pixels.show();}else
    if (uv.readUV()>1121  && uv.readUV()<1493) {u8x8.drawString(11, 3, "     ");u8x8.drawString(11, 3, "High");
    pixels.setPixelColor(5, pixels.Color(255,255,0));//yellow
    pixels.show();}else
    if (uv.readUV()>561  && uv.readUV()<1120) {u8x8.drawString(11, 3, "     ");u8x8.drawString(11, 3, "Moder");
    pixels.setPixelColor(5, pixels.Color(0,0,255));//blue
    pixels.show();}else
    if (uv.readUV()<560) {u8x8.drawString(11, 3, "     ");u8x8.drawString(11, 3, "Low");
    pixels.setPixelColor(5, pixels.Color(0,255,0));//green
    pixels.show();}else
    if (uv.readUV()<1){pixels.setPixelColor(5, pixels.Color(0,0,0));pixels.show();}//off

    char vocchar[5];
    dtostrf(bme.gas_resistance, 5, 0, vocchar);
    
    if (bme.gas_resistance>140901) {u8x8.drawString(11, 4, "     ");u8x8.drawString(11, 4, "Good");
    pixels.setPixelColor(7, pixels.Color(0,255,0));//green
    pixels.show();}else
    if (bme.gas_resistance>75001  && bme.gas_resistance<140900) {u8x8.drawString(11, 4, "     ");u8x8.drawString(11, 4, "Moder");
    pixels.setPixelColor(7, pixels.Color(0,255,125));//turquiose
    pixels.show();}else
    if (bme.gas_resistance>37400  && bme.gas_resistance<75000) {u8x8.drawString(11, 4, "     ");u8x8.drawString(11, 4, "USG");
    pixels.setPixelColor(7, pixels.Color(0,0,255));//blue
    pixels.show();}else
    if (bme.gas_resistance>18770  && bme.gas_resistance<37399) {u8x8.drawString(11, 4, "     ");u8x8.drawString(11, 4, "Unhlt");
    pixels.setPixelColor(7, pixels.Color(255,0,255));//magenta
    pixels.show();}else
    if (bme.gas_resistance>9001  && bme.gas_resistance<18771) {u8x8.drawString(11, 4, "     ");u8x8.drawString(11, 4, "VUnhl");
    pixels.setPixelColor(7, pixels.Color(225,125,0));//orange
    pixels.show();}else
    if (bme.gas_resistance>8371  && bme.gas_resistance<9000) {u8x8.drawString(11, 4, "     ");u8x8.drawString(11, 4, "Hazar");
    pixels.setPixelColor(7, pixels.Color(255,0,0));//red
    pixels.show();}else
    if (bme.gas_resistance<8370) {u8x8.drawString(11, 4, "     ");u8x8.drawString(11, 4, "Vacat");
    pixels.setPixelColor(7, pixels.Color(255,0,0));//red
    pixels.show();}else
    if (bme.gas_resistance<1){pixels.setPixelColor(7, pixels.Color(0,0,0));pixels.show();}//off

    char gaschar[4];
    int reading = analogRead(A0);
    dtostrf(reading, 3, 0, gaschar);
    u8x8.drawString(2, 4, gaschar); 
    u8x8.drawString(5, 4, "PPM");  
    //Serial.println(reading);
    if (reading>1000){pixels.setPixelColor(6, pixels.Color(255,0,0));pixels.show();}//red
    if (reading>501 && reading<999){pixels.setPixelColor(6, pixels.Color(225,125,0));pixels.show();}else//orange 
    if (reading>151 && reading<500){pixels.setPixelColor(6, pixels.Color(255,255,0));pixels.show();}else//yellow
    if (reading>71 && reading<150){pixels.setPixelColor(6, pixels.Color(0,0,255));pixels.show();}else//blue
    if (reading>1 && reading<70){pixels.setPixelColor(6, pixels.Color(0,255,0));pixels.show();}else//green
    if (reading<1){pixels.setPixelColor(6, pixels.Color(0,0,0));pixels.show();}//off

  GPSloop();

}


static void GPSloop()
{
  pixels.setPixelColor(3, pixels.Color(255,0,0));pixels.show();//red
  while (gps.available( gpsPort )) {
    pixels.setPixelColor(3, pixels.Color(0,255,0));pixels.show();//green
    GetGPS();
  }
}

static void GetGPS()
{

  fix = gps.read();

  int totalSatellites, trackedSatellites;
  totalSatellites = gps.sat_count;
  for (uint8_t w = 0; w < totalSatellites; w++) {
    if (gps.satellites[w].tracked) {
      trackedSatellites++;
 }
}
  
    enum {BufSizeTracked = 3}; //Space for 2 characters + NULL
    char trackedchar[BufSizeTracked];
    snprintf (trackedchar, BufSizeTracked, "%d", trackedSatellites);
    u8x8.drawString(4, 7, "  ");
    u8x8.drawString(4, 7, trackedchar);
  
    enum {BufSizeTotal = 3};
    char availchar[BufSizeTotal];
    snprintf (availchar, BufSizeTotal, "%d", totalSatellites);
    u8x8.drawString(7, 7, "  ");
    u8x8.drawString(8, 7, availchar);

  if (fix.valid.time) {

    enum {BufSizeTime = 3};
    int hour = fix.dateTime.hours;
    int minute = fix.dateTime.minutes;

    char hourchar[BufSizeTime];
    char minutechar[BufSizeTime];
    snprintf (hourchar, BufSizeTime, "%d", hour);
    snprintf (minutechar, BufSizeTime, "%d", minute);
    if ( hour < 10 )
    {
      snprintf (hourchar, BufSizeTime, "%02d", hour);
    }
    if ( minute < 10 )
    {
      snprintf (minutechar, BufSizeTime, "%02d", minute);
    }

    u8x8.drawString(11, 7, hourchar);
    u8x8.drawString(13, 7, ":");
    u8x8.drawString(14, 7, minutechar);
  }
    char latchar[7]; // Buffer big enough for 4-character float
    dtostrf(fix.latitude(), 3, 4, latchar);
    u8x8.drawString(0, 0, "               ");
    u8x8.drawString(0, 0, latchar);
    char longchar[7];
    dtostrf(fix.longitude(), 3, 4, longchar);
    u8x8.drawString(8, 0, longchar);

    char altchar[5]; // Buffer big enough for 4-character float
    dtostrf((fix.altitude()*3.28084), 3, 2, altchar);
    u8x8.drawString(1, 6, "      ");
    u8x8.drawString(1, 6, altchar);
    u8x8.drawString(7, 6, "ft");

    char headchar[3]; 
    dtostrf (fix.heading(), 3, 0, headchar); 
    if (headchar<10){u8x8.drawString(12, 5, "00");u8x8.drawString(14, 5, headchar);}else
    if (headchar<99){u8x8.drawString(12, 5, "0");u8x8.drawString(13, 5, headchar);}else
    u8x8.drawString(12, 5, headchar);   

    char spdchar[4]; 
    dtostrf((fix.speed_kph()*0.621371), 2, 2, spdchar); 
    u8x8.drawString(2, 5, spdchar); 
    u8x8.drawString(7, 5, "Mph");

    char satchar2[3];
    dtostrf(fix.satellites, 2, 0, satchar2);
    u8x8.drawString(1, 7, "  ");
    u8x8.drawString(1, 7, satchar2);

}

Schematics

Breadboard images
Here is a breadboard of the basic circuit.
Locaware v4 bb uiethiew0j

Comments

Similar projects you might like

Arduino Environmental Monitoring

Project showcase by Prajay Basu

  • 13,742 views
  • 1 comment
  • 25 respects

Factory/Warehouse/Control Shed/ Environment Monitoring

Project tutorial by Muhammad Afzal

  • 1,674 views
  • 0 comments
  • 7 respects
Add projectSign up / Login