Mihai Popa
Created March 7, 2024 © Apache-2.0

Portable gas analyzer

The project represent an device that can measure the concentration of several type of gases, along with environmental data

AdvancedFull instructions provided5 hours214

Things used in this project

Hardware components

AVR IoT Mini Cellular Board
Microchip AVR IoT Mini Cellular Board
×1
GY-GPS6MV6
×1
BME280
×1
TFT display 320x240 ILI9341
×1
DFRobot FermionSmoke sensor
×1
DFRobot Fermion Odor sensor
×1
DFRobot Fermion VOC sensor
×1
Fermion: MEMS Carbon Monoxide CO Gas Detection Sensor (Breakout, 5-5000ppm)
DFRobot Fermion: MEMS Carbon Monoxide CO Gas Detection Sensor (Breakout, 5-5000ppm)
×1
DFRobot Fermion CH4 sensor
×1
DFRobot Fermion NH3 sensor
×1
Solar Power Manager 5V
DFRobot Solar Power Manager 5V
×1
DFRobot Semi Flexible Monocrystalline Solar Panel (6V 1A)
×1
Accumulator 3.7V 1200mAh
×1
Texas Instruments CD4052BE
×1
Analog Devices RTC module
×1
3 port DIP switch
×1
16 pins socket
×1

Software apps and online services

Arduino IDE
Arduino IDE
Datacake
Datacake
KiCad
KiCad
PCBWay
EasyEDA
JLCPCB EasyEDA

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
screwdriver, tweezers, patent, other tools used in electronic lab
Drilling machine (generic) with 2 mm drill bit

Story

Read more

Custom parts and enclosures

Gas sensor assembled

Gas sensor antennsa support

This is the top side on the gas sensor support. Is used to hols the GPS and GSM antennas.

Gas sensor power supply support

It is used to hold the solar power supply and 3,7V LiPo battery.

Gas sensor left leg

Gas sensor right leg

Schematics

ODOR sensor datasheet

NH3 sensor datasheet

Adafruit GFX graphic library

Used to display text and characters on TFT ILI9341

CD4051 datasheet

Analog multiplexer

CD4052 datasheet

Analog multiplexer

NEO6VM2 GPS module

GPS module schematic

MEO6VM2 GPS module manual

GPS module user manual

Main board schematic

The main board schematic for gas sensor. This is created in KiCad 7

Main board project

This is the main board gas sensor project. This include the two erros, with reversed connectors J2 and J6

The main board gas sensor reworked

This is the gas sensor main project reworked, witrh J2 and J6 connector in the right places.

Gerber files for main PCB

This are the gerber files used to create PCB; this contain the wronk poisition of J2 and J6 connectors.

Gerber main board reworked

This contain the reworked PCB gerber files.

Gas sensor board schematic

This is a schematic for board with all 6 abnalog gas sensors from DFRobot.

Gas sensor board gerber files and BOM file

Gerber files uset to create PCB for the board that contain all 6 analog gas sensors from DFRobot.

VOC sensor datasheet

CO sensor datasheet

Smoke sensor datasheet

CH4 sensor datasheet

Code

Gas-sensor code

Arduino
/**
Gas sensor application
To connect to Datacake MQTT broker, use your ouw used, password and device ID.
Thece will be set on Datacake IoT web page at https://app.datacake.de/

MqttClient.publish("dtck-pub/gas-sensor-2/xxxxxxxx-yyyy-zzzz-ttttttttttttttttt/board_temp", message_to_publish.c_str());
xxxxxxxx-yyyy-zzzz-ttttttttttttttttt = device ID, generated from Datacake, for MQTT broker.

#define MOTT_USERNAME       "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" //Datacake mqtt broker used name
#define MOTT_PASSWORD       "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" //Datacake mqtt broker used password
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx = user = pass = API key for MQTT generated from Datacake

 */

#include <Arduino.h>
#include <http_client.h>
#include <TimeLib.h>

#include <ecc608.h>
#include <led_ctrl.h>
#include <log.h>
#include <lte.h>
#include <mqtt_client.h>

#include <mcp9808.h>
#include <veml3328.h>

#include "SPI.h"                    //used to comunicate with TFT display
#include "Adafruit_GFX.h"
#include "Adafruit_ILI9341.h"

#include <Wire.h>                   //used to comunicate on I2C
#include <Adafruit_Sensor.h>
#include "Adafruit_BME280.h"

#include <Adafruit_GPS.h>           //used ro comunicate with GPS module

#include <DS3231.h>                 //used to read/write RTC DS3231
DS3231 clock1;

#define SEALEVELPRESSURE_HPA (1013.25)
Adafruit_BME280 bme; // I2C

#define MQTT_SUB_TOPIC     "request_data"

#define MQTT_THING_NAME     "gas-sensor-2" 
#define MQTT_BROKER         "mqtt.datacake.co" 
#define MQTT_PORT           1883
#define MQTT_USE_TLS        false
#define MQTT_KEEPALIVE      1200
#define MQTT_USE_ECC        false
#define MOTT_USERNAME       "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" //Datacake mqtt broker used name
#define MOTT_PASSWORD       "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" //Datacake mqtt broker used password
//#define MQTT_TIME_OUT        (30000)
#define MQTT_PRINT_MESSAGE  true

String message_to_publish;          //used to format the string for Datacake transmission

static uint32_t counter = 0;
#define SerialDebug Serial3         //Add this to print via Serial
#define GPSSerial Serial2           //define hardware serial port the GPS is connected to, can be Serial1, Serial2, Serial3 or Serial4

Adafruit_GPS GPS(&GPSSerial);       //@brief Interface for the GPS

byte year_set;                      //used to set and read date and time from RTC DS3231
byte month_set;
byte date_set;
byte dOW;                           //dOW = day of week
byte hour_set;
byte minute_set;
byte second_set;
bool century = false;
bool h12Flag = false;               //Set 12/24h mode. True is 12-h, false is 24-hour.
bool pmFlag  = false;
char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};


//Pins used to comunicate with TFT display, other than SPI:
#define TFT_DC  PIN_PE2
#define TFT_CS  PIN_PD7
#define TFT_RST PIN_PD5

Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_RST);

//used for GPS
static char latitude[16]  = "0";
static char longitude[16] = "0";
static char time1[24]      = "0";
static bool has_parsed = false; //@brief Whether or not we've parsed one GPS entry. Prevents sending zeros whilst having fix after boot.
//setup gps data new:
// Set GPSECHO to 'false' to turn off echoing the GPS data to the Serial console
// Set GPSECHO to 'true' if you want to debug and listen to the raw GPS sentences
#define GPSECHO false

unsigned long timer_1 = 0; //variable used in delay loops with millis
unsigned long timer_2 = 0;
unsigned long timer_3 = 0;

#define interval_1  5000 //value for delay  5 seconds - used to read data from GPS module
#define interval_2 10000 //value for delay 10 seconds - used in main loop to read senors
#define interval_3 60000 //value for delay 60 seconds - used in main loop to send data to Datacake

int time_zone = 2; //time zone value dependimng to winter/summer hour; for Romania is +2 or +3

uint32_t timer = millis();

float celsius; //used to display temp from board temp sensor

int sensorVOC       = 0;
int sensorCO        = 0;
int sensorSmoke     = 0;
int sensorCH4       = 0;
int sensorODOR      = 0;
int sensorNH3       = 0;
int settings_switch = 0;

int value_to_display = 0; //used in display data on TFT function
int y_value = 40;         //used in display data on TFT function

bool oldvalue = false;           //used to set GSM on and OFF deom DIP switch
bool newvalue = false;           //used to set GSM on and OFF deom DIP switch
bool send_data_over_gsm = false; //usewd to send or not data over GSM

//functions and variable used to connect and disconnect GSM modem to cellular network:
static volatile bool disconnected_event = false;
void disconnectedFromNetwork(void) { disconnected_event = true; }

static volatile bool connected_to_broker = false;
void disconnectedFromBroker(void) { connected_to_broker = false; }

int connection_retry = 0;  //used to count number of connection to GSM and MQTT if fails

unsigned long t_unix_date1;
#define TIMEZONE_URL "worldtimeapi.org"
#define TIMEZONE_URI "/api/timezone/Europe/Oslo.txt"
long getTimeFromResponse(String* resp) 
   {
    int unix_time_index    = resp->indexOf(String("unixtime: "));
    int utx_datetime_index = resp->indexOf(String("utc_datetime"));

    return resp->substring(unix_time_index + 10, utx_datetime_index - 1)
        .toInt();
   }

//******************************************************************************
//************************************ SETUP ***********************************
//******************************************************************************
void setup() 
{
    Wire1.begin();

//************************ 
    Log.begin(115200);
    SerialDebug.begin(115200);
        
//************************ LEDs settings
    LedCtrl.begin();
    LedCtrl.startupCycle();

//************************ GPS settings
    initializeGPS(); 

//************************ TFT settings and title message
    tft.begin();
    tft.fillScreen(ILI9341_BLACK);
    tft.setTextColor(ILI9341_GREEN);
    tft.setTextSize(4);
    tft.setCursor(0, 0);
    tft.println("Gas sensor");

//************************ Set digital pins used for selection of analog sensors:
    pinConfigure(PIN_PA7, PIN_DIR_OUTPUT); //Enable pin from sensor board
    pinConfigure(PIN_PB5, PIN_DIR_OUTPUT); //S0 pin from sensor board
    pinConfigure(PIN_PD0, PIN_DIR_OUTPUT); //S1 pin from sensor board
    pinConfigure(PIN_PE1, PIN_DIR_OUTPUT); //S2 pin from sensor board
    digitalWrite(PIN_PA7, HIGH);           //initial value for Enable pin
    digitalWrite(PIN_PB5, LOW);            //initial value for S0 pin
    digitalWrite(PIN_PD0, LOW);            //initial value for S1 pin
    digitalWrite(PIN_PE1, LOW);            //initial value for S2 pin

//Set analog pin unsed to read the analog sensors:
    analogRead(PIN_PD6);

//************************ Set digital pins used for selection of serial communication (default = GPS):
    pinConfigure(PIN_PD4, PIN_DIR_OUTPUT); //Enable pin from sensor board
    pinConfigure(PIN_PD1, PIN_DIR_OUTPUT); //S0 pin from sensor board
    pinConfigure(PIN_PD3, PIN_DIR_OUTPUT); //S1 pin from sensor board

    digitalWrite(PIN_PD1, HIGH);           //initial value for S0 pin
    digitalWrite(PIN_PD3, LOW);            //initial value for S1 pin
    digitalWrite(PIN_PD4, LOW);            //initial value for Enable pin

//************************ Initialize internal sensors: MCP9808 and Veml3328
    // Initialize MCP9808 library for temperature sensor
    // const int8_t error = Mcp9808.begin(0x18); // Rev1
    const int8_t error = Mcp9808.begin();        // Rev2
    if (error) 
    {
        SerialDebug.println("Error: could not start MCP9808 library");
    }
    Veml3328.begin(); //start colour sensor readings

//************************ BME280 settings 
    unsigned status;  
    // default settings
    //status = bme.begin();  
    // You can also pass in a Wire library object like &Wire2
     status = bme.begin(0x76, &Wire1); //set MBE280 on I2C1
    if (!status) 
    {
        SerialDebug.println("Could not find a valid BME280 sensor, check wiring, address, sensor ID!");
        SerialDebug.print("SensorID was: 0x"); SerialDebug.println(bme.sensorID(),16);
        SerialDebug.print("   ID of 0xFF probably means a bad address, a BMP 180 or BMP 085\n");
        SerialDebug.print("   ID of 0x56-0x58 represents a BMP 280,\n");
        SerialDebug.print("   ID of 0x60 represents a BME 280.\n");
        SerialDebug.print("   ID of 0x61 represents a BME 680.\n");
        while (1) delay(10);
    }    

//************************** Check DIP switch for comunication options:
    check_DIP_switch_at_startup(); //check DIP switch possition 1,2 and 3

//*****Clear the TFT display messages from initialization GSM and MQTT stage:
    tft.setCursor(0, 40);
    tft.fillRect(0,40,240,16,0x0000); //0x0000 = Black  
    tft.setCursor(0, 72);
    tft.fillRect(0,72,240,16,0x0000); //0x0000 = Black  
    tft.setCursor(0, 104);
    tft.fillRect(0,104,240,16,0x0000); //0x0000 = Black  
    tft.setCursor(0, 136);
    tft.fillRect(0,136,240,16,0x0000); //0x0000 = Black  

//*****Fill the name of the sensors on TFT:
    tft.setTextColor(ILI9341_MAGENTA); tft.setTextSize(2); tft.setCursor(0, 40);    tft.println("Col RED:");
    tft.setTextColor(ILI9341_GREEN);   tft.setTextSize(2); tft.setCursor(204, 40);  tft.println("val");    

    tft.setTextColor(ILI9341_MAGENTA); tft.setTextSize(2); tft.setCursor(0, 56);    tft.println("Col GREEN:");
    tft.setTextColor(ILI9341_GREEN);   tft.setTextSize(2); tft.setCursor(204, 56);  tft.println("val");  
    
    tft.setTextColor(ILI9341_MAGENTA); tft.setTextSize(2); tft.setCursor(0, 72);    tft.println("Col BLUE:");
    tft.setTextColor(ILI9341_GREEN);   tft.setTextSize(2); tft.setCursor(204, 72);  tft.println("val");  

    tft.setTextColor(ILI9341_MAGENTA); tft.setTextSize(2); tft.setCursor(0, 88);    tft.println("Col IR:");
    tft.setTextColor(ILI9341_GREEN);   tft.setTextSize(2); tft.setCursor(204, 88);  tft.println("val");  

    tft.setTextColor(ILI9341_MAGENTA); tft.setTextSize(2); tft.setCursor(0, 104);   tft.println("Col CLEAR:");
    tft.setTextColor(ILI9341_GREEN);   tft.setTextSize(2); tft.setCursor(204, 104); tft.println("val");  
    
    tft.setTextColor(ILI9341_WHITE);   tft.setTextSize(2); tft.setCursor(0, 120);   tft.println("Gas VOC:");
    tft.setTextColor(ILI9341_GREEN);   tft.setTextSize(2); tft.setCursor(204 ,120); tft.println("ppm");  

    tft.setTextColor(ILI9341_WHITE);   tft.setTextSize(2); tft.setCursor(0, 136);   tft.println("Gas CO:");
    tft.setTextColor(ILI9341_GREEN);   tft.setTextSize(2); tft.setCursor(204 ,136); tft.println("ppm"); 

    tft.setTextColor(ILI9341_WHITE);   tft.setTextSize(2); tft.setCursor(0, 152);   tft.println("Gas Smoke:");
    tft.setTextColor(ILI9341_GREEN);   tft.setTextSize(2); tft.setCursor(204 ,152); tft.println("ppm"); 

    tft.setTextColor(ILI9341_WHITE);   tft.setTextSize(2); tft.setCursor(0, 168);   tft.println("Gas CH4:");
    tft.setTextColor(ILI9341_GREEN);   tft.setTextSize(2); tft.setCursor(204 ,168); tft.println("ppm"); 

    tft.setTextColor(ILI9341_WHITE);   tft.setTextSize(2); tft.setCursor(0, 184);   tft.println("Gas Odor:");
    tft.setTextColor(ILI9341_GREEN);   tft.setTextSize(2); tft.setCursor(204 ,184); tft.println("ppm"); 

    tft.setTextColor(ILI9341_WHITE);   tft.setTextSize(2); tft.setCursor(0, 200);   tft.println("Gas NH3:");    
    tft.setTextColor(ILI9341_GREEN);   tft.setTextSize(2); tft.setCursor(204 ,200); tft.println("ppm"); 

    tft.setTextColor(ILI9341_CYAN);    tft.setTextSize(2); tft.setCursor(0, 216);   tft.println("Temperature:"); 
    tft.setCursor(190, 216); 
    tft.setTextColor(ILI9341_GREEN);   tft.setTextSize(2); tft.println((char)247);    
    tft.setCursor(202, 216); 
    tft.setTextColor(ILI9341_GREEN);   tft.setTextSize(2); tft.println("C"); 

    tft.setTextColor(ILI9341_CYAN);    tft.setTextSize(2); tft.setCursor(0, 232);  tft.println("Humidity:");
    tft.setCursor(190, 232);
    tft.setTextColor(ILI9341_GREEN);   tft.setTextSize(2); tft.println("%RH");

    tft.setTextColor(ILI9341_CYAN);    tft.setTextSize(2); tft.setCursor(0, 248);  tft.println("Pressure:");
    tft.setCursor(190, 248);
    tft.setTextColor(ILI9341_GREEN);   tft.setTextSize(2); tft.println("mmHg");

    tft.setTextColor(ILI9341_CYAN);    tft.setTextSize(2); tft.setCursor(0, 264);  tft.println("Altitude r:");
    tft.setCursor(190, 264);
    tft.setTextColor(ILI9341_GREEN);   tft.setTextSize(2); tft.println("m");

    tft.setCursor(108, 304);            //display GSM tag on TFT
    tft.fillRect(108,304,48,16,0x0000); // x,y,latime,inaltime,0x0000 = Black
    tft.setTextColor(ILI9341_RED);
    tft.setTextSize(2);
    tft.println("GPS"); 

    tft.setCursor(168, 304); //display GSM tag on TFT
    tft.fillRect(168,304,72,16,0x0000); // x,y,latime,inaltime,0x0000 = Black
    tft.setTextColor(ILI9341_WHITE);
    tft.setTextSize(2);
    tft.println("MQTT"); 

//    i2c_scanner();
//    set_rtc_from_gps();
//    set_rtc_date_and_time();
//    read_date_and_time_from_GSM();
//    set_rtc_date_and_time_from_gsm();    
}

//******************************************************************************
//************************************ Functions *******************************
//******************************************************************************
void check_DIP_switch_at_startup()
{
  //read settings dip switch:
  digitalWrite(PIN_PB5, HIGH); //set value for S0 pin
  digitalWrite(PIN_PD0, HIGH); //set value for S1 pin
  digitalWrite(PIN_PE1, HIGH); //set value for S2 pin
  digitalWrite(PIN_PA7, LOW); //activate sensors
  settings_switch = (float)analogRead(PIN_PD6);
  SerialDebug.println("");
  SerialDebug.printf("Settings switch value: %u\r\n\r\n", settings_switch);
  digitalWrite(PIN_PA7, HIGH); //deactivare sensori 

//set GSM ON/OFF:
  if ((settings_switch>415 and settings_switch<425) or (settings_switch>512 and settings_switch<518))
    {
      SerialDebug.println("Start connection to GSM:");
      display_connection_to_GSM();
      display_connection_to_MQTT();
        tft.setCursor(0, 104);
        tft.fillRect(0,104,240,16,0x0000); //0x0000 = Black   
        tft.setTextColor(ILI9341_YELLOW);
        tft.setTextSize(2);
        tft.setCursor(0, 104);
        tft.println("Setting RTC...");            
      read_date_and_time_from_GSM();    //read date and time from GSM
      set_rtc_date_and_time_from_gsm(); //set RTC
        tft.setCursor(0, 104);
        tft.fillRect(0,104,240,16,0x0000); //0x0000 = Black   
        tft.setTextColor(ILI9341_GREEN);
        tft.setTextSize(2);
        tft.setCursor(0, 104);
        tft.println("RTC set ok!");  
        delay(2500);         
    }
    else 
    {
      SerialDebug.print("");
      SerialDebug.println("GSM connection is not activated!");
        tft.setCursor(0, 40);
        tft.fillRect(0,40,240,16,0x0000); //0x0000 = Black 
        tft.setTextColor(ILI9341_RED);
        tft.setTextSize(2);
        tft.setCursor(0, 40);
        tft.println("GSM is not active!  "); 
        
      delay(2500); 
              
        tft.setCursor(0, 72);
        tft.fillRect(0,72,240,16,0x0000); //0x0000 = Black   
        tft.setTextColor(ILI9341_RED);
        tft.setTextSize(2);
        tft.setCursor(0, 72);
        tft.println("MQTT connection off!");
        
      delay(2500);
           
        tft.setCursor(0, 104);
        tft.fillRect(0,104,240,16,0x0000); //0x0000 = Black   
        tft.setTextColor(ILI9341_RED);
        tft.setTextSize(2);
        tft.setCursor(0, 104);
        tft.println("RTC is not set!!");
         
      delay(5000);        
    }

//set WiFi ON/OFF:
  if ((settings_switch>460 and settings_switch<470) or (settings_switch>512 and settings_switch<518))
    {
      SerialDebug.print("");
      SerialDebug.println("Start connection to WiFi:");
        tft.setCursor(0, 136);
        tft.fillRect(0,136,240,16,0x0000); // x,y,latime,inaltime,0x0000 = Black
        tft.setTextColor(ILI9341_WHITE);
        tft.setCursor(0, 136);
        tft.setTextSize(2);
        tft.println("Connecting to WiFi..");
      delay(5000);         
        tft.setCursor(0, 136);
        tft.fillRect(0,136,240,16,0x0000); // x,y,latime,inaltime,0x0000 = Black
        tft.setTextColor(ILI9341_GREEN);
        tft.setCursor(0, 136);
        tft.setTextSize(2);
        tft.println("Connected to WiFi!");
      delay(5000);  
    }
    else 
    {
      SerialDebug.print("");
      SerialDebug.println("WiFi connection is not activated!");
        tft.setCursor(0, 136);
        tft.fillRect(0,136,48,16,0x0000); // x,y,latime,inaltime,0x0000 = Black
        tft.setTextColor(ILI9341_RED);
        tft.setCursor(0, 136);
        tft.setTextSize(2);
        tft.println("WiFi is not active! ");      
      delay(5000);   
    }
}

void display_connection_to_GSM()
{
//************************** Connecting to GSM:
      tft.setCursor(0, 40);
      tft.fillRect(0,40,240,16,0x0000); //0x0000 = Black  0x001F = Blue 
      tft.setTextColor(ILI9341_WHITE);
      tft.setTextSize(2);
      tft.setCursor(0, 40);
      tft.println("Connecting to GMS...");

    connect_to_GSM(); //Establish LTE connection

      tft.setCursor(0, 40);
      tft.fillRect(0,40,240,16,0x0000); //0x0000 = Black  0x001F = Blue  
      tft.setTextColor(ILI9341_GREEN);
      tft.setTextSize(2);
      tft.setCursor(0, 40);
      tft.println("Connected to GSM!");

    delay(1000); //delay only for showing data on TFT
}

void display_connection_to_MQTT()
{
  //************************** Connecting to MQTT Datacake broker:
      tft.setCursor(0, 72);
      tft.fillRect(0,72,240,16,0x0000); //0x0000 = Black 
      tft.setTextColor(ILI9341_WHITE);
      tft.setTextSize(2);
      tft.setCursor(0, 72);
      tft.println("Connecting to MQTT..");

    connect_to_mqtt_broker(); //Connecting to MQTT broker
   
      tft.setCursor(0, 72);
      tft.fillRect(0,72,240,16,0x0000); //0x0000 = Black   
      tft.setTextColor(ILI9341_GREEN);
      tft.setTextSize(2);
      tft.setCursor(0, 72);
      tft.println("Connected to MQTT!");
    
    delay(2000); //delay only for showing data on TFT
}

void connect_to_GSM()
{
  // Establish LTE connection:
    if (!Lte.begin(30000)) 
    {
        return false;
        Log.error(F("Failed to connect to operator"));
        // Halt here
      //  while (1) {}   
    }
    else {
      return true;
    }
}

void connect_to_mqtt_broker()
{
//Attempt to connect to the broker:
    Log.info(F("Starting MQTT with username and password"));
mqtt_broker_connection:
    if (MqttClient.begin(MQTT_THING_NAME,
                         MQTT_BROKER,
                         MQTT_PORT,
                         MQTT_USE_TLS,
                         MQTT_KEEPALIVE,
                         MQTT_USE_ECC,
                         MOTT_USERNAME,
                         MOTT_PASSWORD,
                         (30000),
                         MQTT_PRINT_MESSAGE)) {
       
        if (MqttClient.subscribe(MQTT_SUB_TOPIC)) 
        {
            Log.infof(F("Subscribed to %s\r\n"), MQTT_SUB_TOPIC);
        } 
          else  
        {
            Log.error(F("Failed to subscribe to topic"));
              connection_retry=connection_retry+1; //increment retry number 
            // Halt here
           // while (1) {}
              Log.info(F("Closing MQTT connection"));
              MqttClient.onDisconnect(disconnectedFromBroker);
              MqttClient.end();
              SerialDebug.println("Restart GSM connection...");
              Lte.onDisconnect(disconnectedFromNetwork);
              Log.info("Got disconnected!");
              delay(100);
              Lte.onDisconnect(disconnectedFromNetwork);
              LedCtrl.begin();
              LedCtrl.startupCycle();
              Lte.begin();
              SerialDebug.println("Try to re-connect to mqtt broker...");           
              goto mqtt_broker_connection;
           }
      } else {
           // Halt here
           // while (1) {}      
           connection_retry=connection_retry+1; //increment retry number 
           Log.info(F("Closing MQTT connection"));
           MqttClient.onDisconnect(disconnectedFromBroker);
           MqttClient.end();
           SerialDebug.println("Restart GSM connection...");
           Lte.onDisconnect(disconnectedFromNetwork);
           Log.info("Got disconnected!"); 
           delay(500);
           Lte.onDisconnect(disconnectedFromNetwork);
           LedCtrl.begin();
           LedCtrl.startupCycle();
           Lte.begin();
           SerialDebug.println("Try to re-connect to mqtt broker...");           
           goto mqtt_broker_connection;       
        }
}

void i2c_scanner()
{
//info here: https://playground.arduino.cc/Main/I2cScanner/
  byte error, address;
  int nDevices;

  SerialDebug.println("Scanning...");

  nDevices = 0;
  for(address = 1; address < 127; address++ ) 
  {
    // The i2c_scanner uses the return value of
    // the Write.endTransmisstion to see if
    // a device did acknowledge to the address.
    Wire1.beginTransmission(address);
    error = Wire1.endTransmission();

    if (error == 0)
    {
      SerialDebug.print("I2C device found at address 0x");
      if (address<16) 
        SerialDebug.print("0");
      SerialDebug.print(address,HEX);
      SerialDebug.println("  !");

      nDevices++;
    }
    else if (error==4) 
    {
      SerialDebug.print("Unknown error at address 0x");
      if (address<16) 
        SerialDebug.print("0");
      SerialDebug.println(address,HEX);
    }    
  }
  if (nDevices == 0)
    SerialDebug.println("No I2C devices found\n");
  else
    SerialDebug.println("done\n");

  delay(5000);           // wait 5 seconds for next scan  
}

void set_RTC()
{
    clock1.setClockMode(false);  // set to 24h
    //setClockMode(true); // set to 12h
    year_set = year_set-2000;     //because the year calculate from GSM in in format 20xx    
    clock1.setYear(year_set);
    clock1.setMonth(month_set);
    clock1.setDate(date_set);
    clock1.setDoW(dOW);
    clock1.setHour(hour_set);
    clock1.setMinute(minute_set);
    clock1.setSecond(second_set);    
}

void read_RTC()
{   
    SerialDebug.print("");
    SerialDebug.println("Display date and time from RTC: ");
    SerialDebug.print("Date: ");   
    if ( clock1.getDoW() >=1 && clock1.getDoW() <=7)
    {
      SerialDebug.print(daysOfTheWeek[clock1.getDoW() - 1]);
    } 
    SerialDebug.print(" - ");   
    SerialDebug.print(clock1.getDate(), DEC); 
    SerialDebug.print("/");
    SerialDebug.print(clock1.getMonth(century), DEC);
    SerialDebug.print("/");
    SerialDebug.print("20");
    SerialDebug.println(clock1.getYear(), DEC);
    SerialDebug.print("Time: ");
    SerialDebug.print(clock1.getHour(h12Flag, pmFlag), DEC);
    SerialDebug.print(":");
    SerialDebug.print(clock1.getMinute(), DEC);
//    SerialDebug.print("Day of week: ");
//    SerialDebug.println(clock1.getDoW(), DEC);  
    SerialDebug.println(); 
    SerialDebug.println();     
}

void set_rtc_from_gps()
{
  year_set   = 24; //(GPS.year, DEC);
  month_set  = 3; //(GPS.month, DEC);
  date_set   = 15; //(GPS.day, DEC);
  dOW        = 6;
  hour_set   = 21; //(GPS.hour + 2, DEC);
  minute_set = 58; //(GPS.minute, DEC);
  second_set = 00; //(GPS.seconds, DEC);

  set_RTC();
  SerialDebug.print("RTC saved!");
  delay(5000);
}

void read_date_and_time_from_GSM()
{
    if (!HttpClient.configure(TIMEZONE_URL, 80, false)) {
        Log.errorf(F("Failed to configure HTTP for the domain %s\r\n"), TIMEZONE_URL);
        return;
    }

    Log.info("--- Configured to HTTP ---");

    HttpResponse response;
    response = HttpClient.get(TIMEZONE_URI);
    if (response.status_code != HttpClient.STATUS_OK) {
        Log.errorf(
            F("Error when performing a GET request on %s%s. Got HTTP status"
              "code = %d. Exiting...\r\n"),
            TIMEZONE_URL,
            TIMEZONE_URI,
            response.status_code);
        return;
    }

    Log.infof(
        F("Successfully performed GET request. HTTP status code = %d, Size "
          "= %d\r\n"),
        response.status_code,
        response.data_size);

    String body = HttpClient.readBody(512);

    if (body == "") {
        Log.errorf(
            F("The returned body from the GET request is empty. Something "
              "went wrong. Exiting...\r\n"));
        return;
    }

    Log.infof(F("Got the time (unixtime) %lu\r\n"), getTimeFromResponse(&body)); 
    SerialDebug.print("");
 //   SerialDebug.println("Converted date and time: ");

    SerialDebug.println("Date and time displayed from GSM: ");
    SerialDebug.print("Date: "); SerialDebug.print(day(getTimeFromResponse(&body)));    date_set  = day(getTimeFromResponse(&body));
    SerialDebug.print(" / ");    SerialDebug.print(month(getTimeFromResponse(&body)));  month_set = month(getTimeFromResponse(&body));
    SerialDebug.print(" / ");    SerialDebug.println(year(getTimeFromResponse(&body))); year_set  = year(getTimeFromResponse(&body));

    SerialDebug.print(weekday(getTimeFromResponse(&body))); dOW = weekday(getTimeFromResponse(&body));
    SerialDebug.print(" - ");
    if ( weekday(getTimeFromResponse(&body)) >=1 && weekday(getTimeFromResponse(&body)) <=7)
    {
      SerialDebug.println(daysOfTheWeek[weekday(getTimeFromResponse(&body)) - 1]);
    }
    else
    {
      SerialDebug.println("Invalid day number!");
    }
    
    SerialDebug.print("Time: "); SerialDebug.print(hour(getTimeFromResponse(&body)));     hour_set   = hour(getTimeFromResponse(&body));
    SerialDebug.print(" : ");    SerialDebug.print(minute(getTimeFromResponse(&body)));   minute_set = minute(getTimeFromResponse(&body));
    SerialDebug.print(" : ");    SerialDebug.println(second(getTimeFromResponse(&body))); second_set = second(getTimeFromResponse(&body));

    SerialDebug.print("");     
}

void set_rtc_date_and_time_from_gsm()
{
   if (date_set <= 0)
     {
        SerialDebug.print("Can not set RTC! No date and time available!");
     }
     else 
     {
        set_RTC();
        SerialDebug.print("RTC saved!");
     }
}

static void initializeGPS(void) //GPS initialization:
{
    GPSSerial.swap(1);
    GPS.begin(9600);
    GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA);   // Enable RMC & GGA output messages
//  GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCONLY);  // uncomment this line to turn on only the "minimum recommended" data
    GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ);      // Set the update rate to 1Hz
    GPS.sendCommand(PGCMD_NOANTENNA); 
    GPS.sendCommand(PGCMD_ANTENNA);  // Request updates on antenna status, comment out to keep quiet
    delay(1000);
    GPSSerial.println(PMTK_Q_RELEASE);    // Ask for firmware version
}

void gps_read_data() //read GPS data:
{
  // read data from the GPS in the 'main loop'
  char c = GPS.read();
  // if you want to debug, this is a good time to do it!
  if (GPSECHO)
    if (c) SerialDebug.print(c);
  // if a sentence is received, we can check the checksum, parse it...
  if (GPS.newNMEAreceived()) {
    // a tricky thing here is if we print the NMEA sentence, or data
    // we end up not listening and catching other sentences!
    // so be very wary if using OUTPUT_ALLDATA and trying to print out data
//    SerialDebug.print(GPS.lastNMEA()); // this also sets the newNMEAreceived() flag to false
    if (!GPS.parse(GPS.lastNMEA())) // this also sets the newNMEAreceived() flag to false
      return; // we can fail to parse a sentence in which case we should just wait for another
  }

  // approximately every 5 seconds or so, print out the current stats
  if (millis() - timer_1 > interval_1) 
  {
    timer_1 = millis(); // reset the timer
    SerialDebug.print("Display date and time from GPS: ");
    SerialDebug.print("\nTime: ");
    if (GPS.hour < 10) { SerialDebug.print('0'); }
    SerialDebug.print(GPS.hour + time_zone, DEC); SerialDebug.print(':');
    if (GPS.minute < 10) { SerialDebug.print('0'); }
    SerialDebug.print(GPS.minute, DEC); SerialDebug.print(':');
    if (GPS.seconds < 10) { SerialDebug.print('0'); }
    SerialDebug.print(GPS.seconds, DEC); SerialDebug.print('.');
    if (GPS.milliseconds < 10) {
      SerialDebug.print("00");
    } else if (GPS.milliseconds > 9 && GPS.milliseconds < 100) {
      SerialDebug.print("0");
    }
    SerialDebug.println(GPS.milliseconds);
    SerialDebug.print("Date: ");
    SerialDebug.print(GPS.day, DEC); SerialDebug.print('/');
    SerialDebug.print(GPS.month, DEC); SerialDebug.print("/20");
    SerialDebug.println(GPS.year, DEC);
    SerialDebug.print("Fix: "); SerialDebug.print((int)GPS.fix);
    SerialDebug.print(" quality: "); SerialDebug.println((int)GPS.fixquality);
    if (GPS.fix) {
//      SerialDebug.print("Location: ");
//      SerialDebug.print(GPS.latitude, 4); SerialDebug.print(GPS.lat);
//      SerialDebug.print(", ");
//      SerialDebug.print(GPS.longitude, 4); SerialDebug.println(GPS.lon);
      SerialDebug.print("Speed (knots): "); SerialDebug.println(GPS.speed);
      SerialDebug.print("Speed (km/h): "); SerialDebug.println(GPS.speed * 1.852);
      SerialDebug.print("Angle: "); SerialDebug.println(GPS.angle);
      SerialDebug.print("Altitude: "); SerialDebug.println(GPS.altitude);
      SerialDebug.print("Satellites: "); SerialDebug.println((int)GPS.satellites);
      SerialDebug.print("Antenna status: "); SerialDebug.println((int)GPS.antenna);
      dtostrf(GPS.latitudeDegrees, 2, 4, latitude);
      dtostrf(GPS.longitudeDegrees, 2, 4, longitude);
      SerialDebug.print("Location: ");
      SerialDebug.print(dtostrf(GPS.latitudeDegrees, 2, 4, latitude));   SerialDebug.print(GPS.lat);
      SerialDebug.print(", ");      
      SerialDebug.print(dtostrf(GPS.longitudeDegrees, 2, 4, longitude)); SerialDebug.println(GPS.lon);                
    }
  }  
}

static void parseGPSMessages() // @brief Checks for new GPS messages and parses the NMEA messages if any.
{
    // Read the incoming messages, needn't do anything with them yet as that is
    // taken care of by the newNMEAReceived() function.
    if (GPS.available()) {
        GPS.read();
    }

    if (GPS.newNMEAreceived()) {
        if (!GPS.parse(GPS.lastNMEA())) {
            // If we fail to parse the NMEA, wait for the next one
            return;
        } else {

            Log.rawf(F("Timestamp: %d/%d/20%d %d:%d:%d GMT+2 \r\n"),
                     GPS.day,
                     GPS.month,
                     GPS.year,
                     (GPS.hour + 2),
                     GPS.minute,
                     GPS.seconds);

            Log.rawf(F("Fix: %d, quality: %d\r\n"), GPS.fix, GPS.fixquality);

            if (GPS.fix) {

                // Need to convert all floats to strings
                dtostrf(GPS.latitudeDegrees, 2, 4, latitude);
                dtostrf(GPS.longitudeDegrees, 2, 4, longitude);

                sprintf_P(time1,
                          PSTR("%d/%d/20%d %d:%d:%d"),
                          GPS.day,
                          GPS.month,
                          GPS.year,
                          (GPS.hour + 2),
                          GPS.minute,
                          GPS.seconds);

                Log.rawf(F("Location: %s N, %s E\r\n"), latitude, longitude);
                Log.rawf(F("Satellites: %d\r\n"), GPS.satellites);
    //            Log.rawf(F("Speed: %d\r\n"),(GPS.speed)*1.852); 

                Log.rawf(F("\r\n"));

                has_parsed = true;
            } else {
                Log.info(F("Waiting for GPS fix..."));
            }
        }
    }
}

void read_analog_sensors() //read gas sensors:
{
    //read VOC sensor:
    digitalWrite(PIN_PB5, LOW); //set value for S0 pin
    digitalWrite(PIN_PD0, LOW); //set value for S1 pin
    digitalWrite(PIN_PE1, LOW); //set value for S2 pin
    digitalWrite(PIN_PA7, LOW); //activate sensors
    sensorVOC = (float)analogRead(PIN_PD6);
    SerialDebug.printf("Sensor VOC value: %u\r\n", sensorVOC);
    digitalWrite(PIN_PA7, HIGH); //deactivare sensori

    //read CO sensor:
    digitalWrite(PIN_PB5, HIGH); //set value for S0 pin
    digitalWrite(PIN_PD0, LOW); //set value for S1 pin
    digitalWrite(PIN_PE1, LOW); //set value for S2 pin
    digitalWrite(PIN_PA7, LOW); //activate sensors
    sensorCO = (float)analogRead(PIN_PD6);
    SerialDebug.printf("Sensor CO value: %u\r\n", sensorCO);
    digitalWrite(PIN_PA7, HIGH); //deactivare sensori

    //read Smoke sensor:
    digitalWrite(PIN_PB5, LOW); //set value for S0 pin
    digitalWrite(PIN_PD0, HIGH); //set value for S1 pin
    digitalWrite(PIN_PE1, LOW); //set value for S2 pin
    digitalWrite(PIN_PA7, LOW); //activate sensors
    sensorSmoke = (float)analogRead(PIN_PD6);
    SerialDebug.printf("Sensor Smoke value: %u\r\n", sensorSmoke);
    digitalWrite(PIN_PA7, HIGH); //deactivare sensori

    //read CH4 sensor:
    digitalWrite(PIN_PB5, HIGH); //set value for S0 pin
    digitalWrite(PIN_PD0, HIGH); //set value for S1 pin
    digitalWrite(PIN_PE1, LOW); //set value for S2 pin
    digitalWrite(PIN_PA7, LOW); //activate sensors
    sensorCH4 = (float)analogRead(PIN_PD6);
    SerialDebug.printf("Sensor CH4 value: %u\r\n", sensorCH4);
    digitalWrite(PIN_PA7, HIGH); //deactivare sensori

    //read ODOR sensor:
    digitalWrite(PIN_PB5, LOW); //set value for S0 pin
    digitalWrite(PIN_PD0, LOW); //set value for S1 pin
    digitalWrite(PIN_PE1, HIGH); //set value for S2 pin
    digitalWrite(PIN_PA7, LOW); //activate sensors
    sensorODOR = (float)analogRead(PIN_PD6);
    SerialDebug.printf("Sensor ODOR value: %u\r\n", sensorODOR);
    digitalWrite(PIN_PA7, HIGH); //deactivare sensori

    //read NH3 sensor:
    digitalWrite(PIN_PB5, HIGH); //set value for S0 pin
    digitalWrite(PIN_PD0, LOW); //set value for S1 pin
    digitalWrite(PIN_PE1, HIGH); //set value for S2 pin
    digitalWrite(PIN_PA7, LOW); //activate sensors
    sensorNH3 = (float)analogRead(PIN_PD6);
    SerialDebug.printf("Sensor NH3 value: %u\r\n\r\n", sensorNH3);
    digitalWrite(PIN_PA7, HIGH); //deactivare sensori   
}

void settings_from_analog_switch()
{
//read settings dip switch:
  digitalWrite(PIN_PB5, HIGH); //set value for S0 pin
  digitalWrite(PIN_PD0, HIGH); //set value for S1 pin
  digitalWrite(PIN_PE1, HIGH); //set value for S2 pin
  digitalWrite(PIN_PA7, LOW); //activate sensors
  settings_switch = (float)analogRead(PIN_PD6);
  SerialDebug.println("");
  SerialDebug.printf("Settings switch value: %u\r\n\r\n", settings_switch);
  digitalWrite(PIN_PA7, HIGH); //deactivare sensori 

//set GSM ON/OFF:
  if ((settings_switch>415 and settings_switch<425) or (settings_switch>512 and settings_switch<518))
    {
      SerialDebug.println("GSM - ON");
      tft.setCursor(0, 304);
      tft.fillRect(0,304,36,16,0x0000); // x,y,latime,inaltime,0x0000 = Black
      tft.setTextColor(ILI9341_GREEN);
      tft.setTextSize(2);
      tft.println("GSM");
        newvalue = true;        
//        SerialDebug.print("Here set newvalue for GSM On: "); SerialDebug.println(newvalue);  
    }
    else 
    {
      SerialDebug.println("GSM - OFF");
      tft.setCursor(0, 304);
      tft.fillRect(0,304,36,16,0x0000); // x,y,latime,inaltime,0x0000 = Black
      tft.setTextColor(ILI9341_RED);
      tft.setTextSize(2);
      tft.println("GSM");
        newvalue = false;      
//        SerialDebug.print("Here set newvalue for GSM Off: "); SerialDebug.println(newvalue); 
    }

//    SerialDebug.print("Here read newvalue from DIP switch: "); SerialDebug.println(newvalue);  
//    SerialDebug.print("Here read oldvalue from DIP switch: "); SerialDebug.println(oldvalue); 

    if (newvalue != oldvalue)
    {
      SerialDebug.print("Here verify newvalue: "); SerialDebug.println(newvalue);  
      SerialDebug.print("Here verify oldvalue: "); SerialDebug.println(oldvalue);              
      if (newvalue) //check if "newvalue" has value True
      {
         SerialDebug.print("Here set GSM ON "); SerialDebug.println(newvalue);
         send_data_over_gsm = true;
         LedCtrl.startupCycle();
         Lte.begin();         
           tft.setCursor(168, 304); //display GSM tag on TFT
           tft.fillRect(168,304,72,16,0x0000); // x,y,latime,inaltime,0x0000 = Black
           tft.setTextColor(ILI9341_WHITE);
           tft.setTextSize(2);
           tft.println("MQTT"); 
      }
      else
      {
         SerialDebug.print("Here set GSM OFF "); SerialDebug.println(newvalue); 
         send_data_over_gsm = false;
         Lte.onDisconnect(disconnectedFromNetwork);
         Log.info("Got disconnected!"); 
           tft.setCursor(168, 304); //display GSM tag on TFT
           tft.fillRect(168,304,72,16,0x0000); // x,y,latime,inaltime,0x0000 = Black
           tft.setTextColor(ILI9341_RED);
           tft.setTextSize(2);
           tft.println("MQTT");              
      }
//    SerialDebug.print("Here read newvalue before: "); SerialDebug.println(newvalue);  
//    SerialDebug.print("Here read oldvalue before: "); SerialDebug.println(oldvalue);      

    oldvalue = newvalue;  
     
//    SerialDebug.print("Here read newvalue after: "); SerialDebug.println(newvalue);  
//    SerialDebug.print("Here read oldvalue after: "); SerialDebug.println(oldvalue);        
    }


//set WiFi ON/OFF:
  if ((settings_switch>460 and settings_switch<470) or (settings_switch>512 and settings_switch<518))
    {
      SerialDebug.println("WiFi - ON");
      tft.setCursor(48, 304);
      tft.fillRect(48,304,48,16,0x0000); // x,y,latime,inaltime,0x0000 = Black
      tft.setTextColor(ILI9341_GREEN);
      tft.setTextSize(2);
      tft.println("WiFi");  
//    TBD
    }
    else 
    {
      SerialDebug.println("WiFi - OFF");
      tft.setCursor(48, 304);
      tft.fillRect(48,304,48,16,0x0000); // x,y,latime,inaltime,0x0000 = Black
      tft.setTextColor(ILI9341_RED);
      tft.setTextSize(2);
      tft.println("WiFi");       
//    TBD
    }
//set Settings ON/OFF:
  if ((settings_switch>605 and settings_switch<615))
    {
      SerialDebug.println("Settings - ON");
      SerialDebug.println("");  
//    TBD
    }
    else 
    {
      SerialDebug.println("Settings - OFF"); 
      SerialDebug.println("");        
//    TBD
    }

}

void read_bme280() //read BME280 sensor: 
{
...

This file has been truncated, please download it to see its full contents.

Credits

Mihai Popa

Mihai Popa

2 projects • 4 followers

Comments