Antonio Ruiz
Published © GPL3+

Incorporating GPS and solar-power to environmental sensors

We developed a solar-powered sensing device, able to determine and track key environmental parameters in real time.

AdvancedWork in progress835
Incorporating GPS and solar-power to environmental sensors

Things used in this project

Hardware components

Adafruit Medium 6V 2W Solar panel - 2.0 Watt
×1
Sensirion SCD30 CO2 sensor module
×1
Gravity: Analog Capacitive Soil Moisture Sensor- Corrosion Resistant
DFRobot Gravity: Analog Capacitive Soil Moisture Sensor- Corrosion Resistant
×1
Soil Moisture & Temperature Sensor
Seeed Studio Soil Moisture & Temperature Sensor
×1
Rechargeable Battery, Lithium Ion
Rechargeable Battery, Lithium Ion
×1
Adafruit Solar Lithium Ion/Polymer charger
×1
Seeed Studio Grove - GPS
×1

Software apps and online services

Arduino IDE
Arduino IDE

Story

Read more

Code

Code used for the present project

Arduino
#include <Seeed_FS.h>
#include "TFT_eSPI.h"
#include <SPI.h>
#include <SD/Seeed_SD.h>
#include <Wire.h>
#include "SparkFun_SCD30_Arduino_Library.h"
#include <RawImage.h>
#include <SoftwareSerial.h>
#include <TinyGPS++.h>
    
//We define the class types for the different sensors we will be employing throughout the programme (gps, and light and gas sensors)
SCD30 airSensor;
TinyGPSPlus gps;

//We also define the class types for the display of objects in the screen
TFT_eSPI tft;

//We indicate the pins for the GPS device
SoftwareSerial mySerial(2, 3);

//Here we define the variables employed in the code
double dist_LAT = 34.9722899, dist_LONG = 138.3868869;
String p_hour, p_lat, p_lng, p_alt, p_sat, p_date;
String p_my_speed, p_my_course, p_dist_dTo, p_dist_cTo;
String pdist_LAT, pdist_LONG;
String sdist_LAT = String(dist_LAT);
String sdist_LONG = String(dist_LONG);
String Date[10], Dat;
int menu = 1;
byte RX1;
uint8_t Cut = 78;
char buffer[64], Data [64], Message;   // buffer array for data receive over serial port
int count=0;                           // counter for buffer array
double Val1, Val2;
int count3 = 0;
int y, Latc, Lonc, i, j, Lat[10], val1, val2, val3, s, p, z, tUV, tlux;
float tTemp, tHum, tCO2, Buff;
uint8_t val[2];
double LatLim1, LatLim2, LonLim1, LonLim2;
String N_Stemp, p_Temp, p_Stemp, N_mois, N_EC, N_CO2, N_Hum, N_Temp, p_STemp, p_mois, p_EC, p_CO2, p_Hum;
int Log_f = 0;
String N_date, hour0;
unsigned long p_time;
int N_y, N_m, N_d;
String hr1, min1, sec1;
String F_name, N_Mois, p_Mois, nhour0[5];
bool sd;
float tEC, tMois, tStemp;
int l, Lng[10];
String N_lux, N_UV, p_lux, p_UV;
int Num = 0;
int k = 1;
float lux[10], Temp[10], Hum[10], CO2[10], Stemp[10], EC[10], Mois[10],  UV[10];
String Longitude[10], Latitude[10], Alt[10], Sat[10];
static const int MAX_SATELLITES = 40;
float uUV, ulux, uCO2, uSTemp, uHum, uTemp, uEC, uMois;

//We will initialise the custom types from the TinyGPS++ library, to determinethe position, elevation, and number of satellites
TinyGPSCustom ExtLat(gps, "GPGGA", 3); 
TinyGPSCustom ExtLng(gps, "GPGGA", 5); 
TinyGPSCustom totalGPGSVMessages(gps, "GPGSV", 1); 
TinyGPSCustom messageNumber(gps, "GPGSV", 2);     
TinyGPSCustom satsInView(gps, "GPGSV", 3);         
TinyGPSCustom satNumber[4]; // to be initialized later
TinyGPSCustom elevation[4];
TinyGPSCustom azimuth[4];
TinyGPSCustom snr[4];

//Define the addresses of the soil probe device for the determination of Temperature, conductivity and moisture
const byte Addr[] = {0x01, 0x04, 0x00, 0x00, 0x00, 0x03, 0xB0, 0x0B};

struct
{
  bool active;
  int elevation;
  int azimuth;
  int snr;
  int dsp;
} sats[MAX_SATELLITES];

    volatile uint32_t newlines = 0UL;
    
    static void handleRxChar( uint8_t c )
    {
      if (c == '\n')
        newlines++;
    }

void setup() {

  mySerial.begin(9600);
  tft.begin();
  tft.setRotation(3);

//This command initialises the SD device included in the Wio terminal
        if (!SD.begin(SDCARD_SS_PIN, SDCARD_SPI)) {
   sd = false;
   }
       else {
          SerialUSB.println("initialization done.");
//First, we need to initialise the devices that will be used on this project, and the Serial communications
  Wire.begin();
  airSensor.begin();
  Serial.begin(9600);
        sd = true;
    }
        for (int i=0; i<4; ++i)
    {
      satNumber[i].begin(gps, "GPGSV", 4 + 4 * i); // offsets 4, 8, 12, 16
      elevation[i].begin(gps, "GPGSV", 5 + 4 * i); // offsets 5, 9, 13, 17
      azimuth[i].begin(gps, "GPGSV", 6 + 4 * i); // offsets 6, 10, 14, 18
      snr[i].begin(gps, "GPGSV", 7 + 4 * i); // offsets 7, 11, 15, 19
    }

//We set the initial values for some of the variables employed throughout the loop 
p = 0;
j = 0;
p = 0;
s = 1;
Num = 0;
count = 0;

//These variables indicate the minimum and maximum values of latitude and longitude on the displayed map 
LatLim1 = 50;
LatLim2 = 55;
LonLim1 = 50;
LonLim2 = 60;

//These commands set the pin used on the programme, for the measurement of UV light intensity as input pins
pinMode (A7, INPUT);
pinMode (A4, INPUT);
pinMode (A6, INPUT);

//These commands initialise the push buttons in the Wio Terminal
pinMode(WIO_5S_PRESS, INPUT_PULLUP);
pinMode(WIO_KEY_A, INPUT_PULLUP);
pinMode(WIO_KEY_B, INPUT_PULLUP);
pinMode(WIO_KEY_C, INPUT_PULLUP);
pinMode(WIO_5S_LEFT, INPUT_PULLUP);
pinMode(WIO_5S_RIGHT, INPUT_PULLUP);
}

void loop() {

//These commands take the measurements from the SCD30 sensor
if (airSensor.dataAvailable())
{
tTemp = airSensor.getTemperature();
tHum = airSensor.getHumidity();
tCO2 = airSensor.getCO2(); 
}

//Since the UV sensor has an analog output, we read the signals form the pins where it has been connected
for (i=0; i<10; i++){
tUV = tUV + analogRead (A7);
}

tUV = tUV/10;

//We convert the environmental measurements to string variables by using the following command 
//These will be the variables displayed on the screen
N_UV = String(tUV);
N_Temp = String(tTemp);
N_Hum = String(tHum);
N_CO2 = String(tCO2);
  
long N_mjd;

//Command to save the data from the sensors and the GPS to a SD card. This function will be executed every 6s
while (mySerial.available() > 0) {
char c = mySerial.read();
gps.encode(c);
}

//calculate the latitude and longitude values, and transform it to a string for the display
    double lat0 = gps.location.lat();
    double lat1 = (lat0 -int(lat0))*60;
    double lat2 = (lat1 - int(lat1))*60;
    String lat3 = String(int(lat0))+':' + String(int(lat1))+':'+String(lat2) + ' ' + String(ExtLat.value());
    double lng0 = gps.location.lng();
    double lng1 = (lng0 -int(lng0))*60;
    double lng2 = (lng1 - int(lng1))*60;
    String lng3 = String(int(lng0))+':' + String(int(lng1))+':'+String(lng2) + ' ' + String(ExtLng.value());

//calculate the time and transform it to a string for the display
    int hr = gps.time.hour()+9;
    if (hr>24) hr -= 24;
    String hr0 = '0'+String(hr);
     hr1 = hr0.substring(hr0.length()-2); 
    String min0 = '0'+String(gps.time.minute());
     min1 = min0.substring(min0.length()-2); 
    String sec0 = '0'+String(gps.time.second());
     sec1 = sec0.substring(sec0.length()-2);
    hour0 =  hr1+':'+min1+':'+sec1;

//Calculate date and transform it to a string for the display
    if (gps.date.isUpdated()) {
        int y = gps.date.year();
        int m = gps.date.month();
        int d = gps.date.day();
        if(m<3){y--; m+=12;}
        long mjd = int(361.7015*y)+int(y/400)-int(y/100)+int(30.59*(m-2))+d-678912;
        if(gps.time.hour()>14) 
            N_mjd = 58580+(mjd-51412);
        else
            N_mjd = 58579+(mjd-51412);
        long n = N_mjd+678881;
        long a = 4*n+3+3*(4*(n+1)/146097+1);
        long b = 5*((a % 1461)/4)+2;
        N_y = int(a/1461);
        N_m = int(b/153+3);
        N_d = int((b % 153)/5+1);
        if (N_m>12){N_y++; N_m-=12;}
        N_date = String(N_y)+'/'+('0'+String(N_m)).substring(('0'+String(N_m)).length()-2)+'/'+('0'+String(N_d)).substring(('0'+String(N_d)).length()-2);
    }

//When the user presses the pushbutton on the Wio terminal, the RS485 communications are triggered for the soil measurements
//The location where the measurements have been taken is also fixed and displayed on the Wio terminal screen
if (digitalRead(WIO_5S_PRESS) == LOW) {

//The Soil measurements will only take place if the Wio terminal is on the data display mode
if (menu ==1){  

//This code saves the current position at the moment of initialising the measurement, for display in the lcd screen
lng0 = lng0*100;
Lng [p] = map(lng0, LonLim1, LonLim2, 0, 240);
Lat [p] = map (lat0, LatLim1, LatLim2, 0, 320);
lng0 = lng0/100;

//We perform the soil temperature and moisture measurements
for (i=0;i<10;i++){
tStemp = tStemp + analogRead (A4);
tMois = tMois + analogRead (A6);
}
tStemp = tStemp/10;
tStemp = tStemp*(-0.0826) + 108.65;
tMois = tMois/10;
tMois = tMois*5/1024;

N_Stemp = String (tStemp);
N_Mois = String (tMois);

Date[p] =  N_date;
nhour0[p] = hour0;
Alt[p] = String(gps.altitude.meters());
Sat[p] = String(gps.satellites.value());
Temp[p] = tTemp;
Hum[p] = tHum;
CO2[p] = tCO2;
UV[p] = tUV;
Stemp[p] = tStemp;
Mois[p] = tMois;
Latitude[p] = lat3;
Longitude[p] = lng3;

//These lines determine the number of points that will be displayed on the map screen
p++;
count++;
if (p>10){
  p = 0;
  count = 10;
}

//The value of s determines the first time that the titles of the measurements are displayed on the Wio Terminal screen
s = 1;  

  }
}

//This condition will show the value of the measurements
if (menu == 1){
  
//The first time that the menu is displayed, we need to initialise the screen colour and the measurement titles
    if (s==1){
    tft.fillScreen (TFT_BLACK);
    tft.setTextSize(3);
    tft.drawString("GPS",50,3);
    tft.drawString("SCD30",30,150);
    tft.drawString("Soil",200,3);
    tft.drawString("Light",200,150);
    tft.setTextSize(1);
    tft.drawString("Date",20,33);
    tft.drawString("Time",20,53);
    tft.drawString("LAT",20,70);
    tft.drawString("LONG",20,87);
    tft.drawString("ALT (m)",20,107);
    tft.drawString("Satellites",20,127);
    tft.drawString("Temp (oC)",20,180);
    tft.drawString("Hum (%)",20,200);
    tft.drawString("CO2 (ppm)",20,220);
    tft.drawString("Temp (oC)",180,33);
    tft.drawString("Moisture (V)",180,53);
    tft.drawString("UV ",180,180);
    }

    if (N_date != p_date || s == 1) {    //Displays the date
      tft.fillRect(50,33,70,20,TFT_BLACK);
      tft.drawString(N_date,50,33);
      p_date = N_date;
    }
    
    if (hour0 != p_hour || s == 1) {    //Displays the time
      tft.fillRect(50,53,100,16,TFT_BLACK);
      tft.drawString(hour0,50,53);
      p_hour = hour0;
    }
    
    if (lat3 != p_lat || s == 1) {      //Displays the latitude
      tft.fillRect(50,70,110,10,TFT_BLACK);
      tft.drawString(lat3,50,70);
      p_lat = lat3;
    }
    
    if (lng3 != p_lng || s == 1) {      //Displays the longitude
      tft.fillRect(50,90,87,10,TFT_BLACK);
      tft.drawString(lng3,50,90);
      p_lng = lng3;
    }

    if (String(gps.altitude.meters()) != p_alt || s == 1) { //Displays the altitude
      tft.fillRect(70,107,107,10,TFT_BLACK);
      tft.drawString(String(gps.altitude.meters()),70,107);
      p_alt = String(gps.altitude.meters());
    }
    
    if (String(gps.satellites.value()) != p_sat || s == 1) {  //Displays the number of satellites
      tft.fillRect(90,127,110,10, TFT_BLACK);
      tft.drawString(String(gps.satellites.value()),90,127);
      p_sat = String(gps.satellites.value());
    }
    
    tft.fillRect(0,140,320,2,TFT_YELLOW);
    tft.fillRect(160,0,2,240,TFT_YELLOW);


if (N_Temp != p_Temp || s == 1){ //Displays the environmental temperature
    tft.fillRect(80,180,32,16,TFT_BLACK);
    tft.drawString(N_Temp, 80, 180);
    p_Temp = N_Temp;
}

if (N_Hum != p_Hum || s == 1){ //Displays the air humidity
    tft.fillRect(90,200,32,16,TFT_BLACK);
    tft.drawString(N_Hum, 80, 200);
    p_Hum = N_Hum;
}

if (N_CO2 != p_CO2 || s == 1){ //Displays the concentration of CO2
    tft.fillRect(80,220,32,16,TFT_BLACK);
    tft.drawString(N_CO2, 80, 220); 
    p_CO2 = N_CO2;
}

if (N_Stemp != p_Stemp || s == 1){ //Displays the temperature in soil
    tft.fillRect(240,33,32,16,TFT_BLACK);
    tft.drawString(N_Stemp, 240, 33);
    p_Stemp = N_Stemp;
}

if (N_Mois != p_Mois || s == 1){ //Displays the volumetric soil water content
    tft.fillRect(270,53,32,16,TFT_BLACK);
    tft.drawString(N_Mois, 260, 53); 
    p_Mois = N_Mois;
}

if (N_UV != p_UV || s == 1){ //Displays the intensity from the UV sensor
    tft.fillRect(200,180,80,16,TFT_BLACK);
    tft.drawString(N_UV, 200, 180); 
    p_UV = N_UV;
}

s = 0;
}

if (menu == 2){
  
   if (digitalRead(WIO_5S_RIGHT) == LOW) {
      Num = Num + 1;
      l = 1;
   }
   if (digitalRead(WIO_5S_LEFT) == LOW) {
      Num = Num - 1;
      l = 1;
   }

   if (Num<0){
   Num = count; 
   }

   if (Num>count-1){
   Num = 0; 
   }

  Dat = String(Num);
  
if (l == 1){

    tft.fillScreen (TFT_BLACK);
    tft.setTextSize(3);
    tft.drawString("GPS",50,3);
    tft.drawString("SCD30",30,150);
    tft.drawString("Soil",170,3);
    tft.drawString("Light",200,150);
    tft.setTextColor(TFT_YELLOW);
    tft.drawString("Sample",170,210);
    tft.drawString(Dat,285,210);
    tft.setTextColor(TFT_WHITE);
    tft.setTextSize(1);
    tft.drawString("Date",20,33);
    tft.drawString("Time",20,53);
    tft.drawString("LAT",20,70);
    tft.drawString("LONG",20,87);
    tft.drawString("ALT (m)",20,107);
    tft.drawString("Satellites",20,127);
    tft.drawString("Temp (oC)",20,180);
    tft.drawString("Hum (%)",20,200);
    tft.drawString("CO2 (ppm)",20,220);
    tft.drawString("Temp (oC)",180,33);
    tft.drawString("Moisture (%)",180,53);
    tft.drawString("UV: ",180,180);

    tft.fillRect(0,140,320,2,TFT_YELLOW);
    tft.fillRect(160,0,2,240,TFT_YELLOW);

    Dat = String (Date[Num]);
    //Date
    tft.drawString(Dat,50,33);

    Dat = String (nhour0[Num]);
    tft.drawString(Dat,50,53);
      p_hour = hour0;

    Dat = String (Latitude[Num]);
      tft.drawString(Dat,50,70);

    Dat = String (Longitude[Num]);
      tft.drawString(Dat,50,90);

    Dat = Alt[Num];
      tft.drawString(Dat,70,107);

    Dat = Sat[Num];
      tft.drawString(Dat,90,127);

    Dat = String(Temp[Num]);
    tft.drawString(Dat, 80, 180);

    Dat = String(Hum[Num]);
    tft.drawString(Dat, 50, 200);

    Dat = String(CO2[Num]);
    tft.drawString(Dat, 80, 220); 

    Dat = String(Stemp[Num]);
    tft.drawString(Dat, 240, 33);

    Dat = String(Mois[Num]);
    tft.drawString(Dat, 260, 53);
   
    Dat = String(UV [Num]);
    tft.drawString(Dat, 240, 180); 
}
  l = 0;
  
}

//We initialise the map display, to see our current position and the places where measurements have been taken
if (menu == 3){
  delay (100);
if (k==1){

//Command to display the map, stored in the SD card memory
drawImage<uint16_t>("map.bmp", 0, 0);

//This function takes the coordinates,and the maximum coordinates indicated by the user to map them and show them on the screen
lng0 = lng0*100;
Latc = map (lat0, LatLim1, LatLim2, 0, 320);
Lonc = map (lng0, LonLim1, LonLim2, 0, 240);
lng0 = lng0/100;

if (l==1){
//We draw a green circle at the current position
tft.fillCircle(Latc, Lonc, 4, TFT_RED);
}

if (l==0){
//We draw a green circle at a past position (only if we are on menu 2)
tft.fillCircle(Lat[Num], Lng[Num], 4, TFT_BLUE);  
}

delay (100);
}
k = 0;
}


//This codes are used to detect the input from the push buttons and change between the menus
   if (digitalRead(WIO_KEY_C) == LOW) {
    menu = 1;
    l = 1;
    s = 1;
    k = 1;
   }
   else if (digitalRead(WIO_KEY_B) == LOW) {
    menu = 2;
    s = 1;
    l = 1;
    k = 1;
   }
   else if (digitalRead(WIO_KEY_A) == LOW) {
   menu = 3;
   s = 1;
   k = 1;
   }
   
  delay (100);
}

Credits

Antonio Ruiz

Antonio Ruiz

7 projects • 22 followers
Research Associate at University of Cambridge

Comments