Hackster is hosting Hackster Holidays, Ep. 6: Livestream & Giveaway Drawing. Watch previous episodes or stream live on Monday!Stream Hackster Holidays, Ep. 6 on Monday!
V205
Published © CC BY-NC

AVR-IoT Air sensing

Air Quality monitoring system with AVR-IoT

IntermediateFull instructions provided2 hours154
AVR-IoT Air sensing

Things used in this project

Hardware components

AVR IoT Mini Cellular Board
Microchip AVR IoT Mini Cellular Board
×1
SparkFun BME280 QWIIC
×1
SparkFun ENS160 QWIIC
×1
USB Cable, USB Type C Plug
USB Cable, USB Type C Plug
Must be a data cable.
×1
FireBeetle ESP32-E IoT Microcontroller with Header (Supports Wi-Fi & Bluetooth)
DFRobot FireBeetle ESP32-E IoT Microcontroller with Header (Supports Wi-Fi & Bluetooth)
Or a different ESP32 dev board.
×1
Breadboard (generic)
Breadboard (generic)
×1
Buzzer
Buzzer
×1

Software apps and online services

Arduino IDE
Arduino IDE
DxCore
AWS IoT
Amazon Web Services AWS IoT
Amazon Web Services AWS CLI
SparkFun BME280 Arduino Library
SparkFun ENS160 Arduino Library
USB Drivers
If needed.

Hand tools and fabrication machines

Mastech MS8217 Autorange Digital Multimeter
Digilent Mastech MS8217 Autorange Digital Multimeter

Story

Read more

Schematics

ESP32 Wiring

ESP32 Wiring
For the AVR-IoT board, there is no schematic as it is just qwiic sensors

Code

ESP32 Alarm Code

Arduino
ESP32 Alarm Code
#include "secrets.h"
#include <WiFiClientSecure.h>
#include <MQTTClient.h>
#include <ArduinoJson.h>
#include "WiFi.h"

// The MQTT topics that this device should publish/subscribe
#define AWS_IOT_PUBLISH_TOPIC   "esp32/pub"
#define AWS_IOT_SUBSCRIBE_TOPIC "esp32/sub"

WiFiClientSecure net = WiFiClientSecure();
MQTTClient client = MQTTClient(256);
bool alert = false;
void connectAWS()
{
  WiFi.mode(WIFI_STA);
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);

  Serial.println("Connecting to Wi-Fi");

  while (WiFi.status() != WL_CONNECTED){
    delay(500);
    Serial.print(".");
  }

  // Configure WiFiClientSecure to use the AWS IoT device credentials
  net.setCACert(AWS_CERT_CA);
  net.setCertificate(AWS_CERT_CRT);
  net.setPrivateKey(AWS_CERT_PRIVATE);

  // Connect to the MQTT broker on the AWS endpoint we defined earlier
  client.begin(AWS_IOT_ENDPOINT, 8883, net);

  // Create a message handler
  client.onMessage(messageHandler);

  Serial.print("Connecting to AWS IOT");

  while (!client.connect(THINGNAME)) {
    Serial.print(".");
    delay(100);
  }

  if(!client.connected()){
    Serial.println("AWS IoT Timeout!");
    return;
  }

  // Subscribe to a topic
  client.subscribe(AWS_IOT_SUBSCRIBE_TOPIC);

  Serial.println("AWS IoT Connected!");
}

void publishMessage()
{
  StaticJsonDocument<200> doc;
  //doc["time"] = millis();
  //doc["sensor_a0"] = analogRead(0);
  doc["alert"] = alert;
  

  char jsonBuffer[512];
  serializeJson(doc, jsonBuffer); // print to client

  client.publish(AWS_IOT_PUBLISH_TOPIC, jsonBuffer);
}


void messageHandler(String &topic, String &payload) {
  Serial.println("incoming: " + topic + " - " + payload);


  StaticJsonDocument<200> doc;
  deserializeJson(doc, payload);
  const char *message = doc["warn"]; // WARNING: if there is no section called warn, the ESP32 will crash and reboot.
  Serial.println(message);
  const char *test = "true"; 

  if (strcmp(message, test) == 0) { // compare


    Serial.println("Alert/warn true");
    alert = true;
    
    //voice.say(sp2_DANGER);
    //delay(2000); 
    //voice.say(sp2_DANGER);
    //voice.say(sp2_RED);
    //voice.say(sp2_ALERT);

    tone(D2, 1000);
    delay(2000);
    noTone();
  }else{

    alert = false;
  }
}

int playing = 0;
void tone(byte pin, int freq) {
  ledcSetup(0, 2000, 8);   // setup beeper
  ledcAttachPin(pin, 0);   // attach beeper
  ledcWriteTone(0, freq);  // play tone
  playing = pin;           // store pin
}
void noTone() {
  tone(playing, 0);  // Stop tone

}


void setup() {
  Serial.begin(9600);
  connectAWS();
}

void loop() {
  publishMessage();
  client.loop();

  delay(1000);
}

AVR-IoTCellularGasAnalyst.ino

Arduino
This is the main code for the project, may be slightly out of date.
Hopefully this compiles good.
/**
 * This example connects to the device specific endpoint in AWS in order to
 * publish and retrieve MQTT messages.
 */

#include <Arduino.h>

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

#include <low_power.h>

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

#include <Wire.h>
#include "SparkFun_ENS160.h"  // Click here to get the library: http://librarymanager/All#SparkFun_ENS160
#include "SparkFunBME280.h"   // Click here to get the library: http://librarymanager/All#SparkFun_BME280


//wow! so many libraries!

// interupt code from example


#define SW0 PIN_PD2

static volatile bool woke_up_from_button = false;

void button_pressed_callback(void) {
  if (PORTD.INTFLAGS & PIN2_bm) {
    // Reset the interrupt flag so that we can process the next incoming
    // interrupt
    PORTD.INTFLAGS = PIN2_bm;

    woke_up_from_button = true;
  }
}


/*

Header from Sparkfun library examples, keeping this in here to provide credit


Ex1_Combined_Basic_Example_ENS160_BME280.ino

This example shows basic data retrieval from the SparkFun Environmental Combo Breakout
from the Air Quality Sensor (ENS160) and Atmospheric Sensor (BME280).

This example shows how to read sensor readings from the ENS160 (air quality index tVOC, and eCO2)
and BME280 (humidity, pressure, and current temperature) over I2C.

Modified by:
Ho Yun "Bobby" Chan @ SparkFun Electronics August, 2023
Basic Example for the ENS160 Originally Written by:
Elias Santistevan @ SparkFun Electronics October, 2022
Basic Example for the ENS160 Originally Written by:
Nathan Seidle @ SparkFun Electronics March 9th, 2018

Products:
Air Quality Sensor  (ENS160)             -  https://www.sparkfun.com/products/20844
Humidity and Temperature Sensor (BME280) -  https://www.sparkfun.com/products/13676

Repository:
https://github.com/sparkfun/SparkFun_Indoor_Air_Quality_Sensor-ENS160_Arduino_Library

SparkFun code, firmware, and software is released under the MIT
License(http://opensource.org/licenses/MIT).

*/




SparkFun_ENS160 myENS;
BME280 myBME280;

int ensStatus;

// AWS defines which topic you are allowed to subscribe and publish too. This is
// defined by the policy The default policy with the Microchip IoT Provisioning
// Tool allows for publishing and subscribing on thing_id/topic. If you want to
// publish and subscribe on other topics, see the AWS IoT Core Policy
// documentation.
const char MQTT_SUB_TOPIC_FMT[] PROGMEM = "%s/sensors";
const char MQTT_PUB_TOPIC_FMT[] PROGMEM = "%s/sensors";

char mqtt_sub_topic[128];
char mqtt_pub_topic[128];


void setup() {
  Log.begin(115200);  // set up Serial interface using Log module
  Wire1.begin();

  Serial3.begin(115200);  // set up Serial3 , probably not needed
  Log.info(F("STARTING " __FILE__ " from " __DATE__ __TIME__));



  if (!myENS.begin(Wire1)) {
    Serial3.println("Did not begin.");
    while (1)
      ;
  }

  if (myBME280.beginI2C(Wire1) == false)  //Begin communication over I2C
  {
    Serial3.println("The sensor did not respond. Please check wiring.");
    while (1)
      ;  //Freeze
  }



  // Reset the indoor air quality sensor's settings.
  if (myENS.setOperatingMode(SFE_ENS160_RESET)) {
    Serial3.println("Ready.");
  }
  delay(100);


  // Device needs to be set to idle to apply any settings.
  // myENS.setOperatingMode(SFE_ENS160_IDLE);

  // Set to standard operation
  // Others include SFE_ENS160_DEEP_SLEEP and SFE_ENS160_IDLE
  myENS.setOperatingMode(SFE_ENS160_STANDARD);

  // There are four values here:
  // 0 - Operating ok: Standard Operation
  // 1 - Warm-up: occurs for 3 minutes after power-on.
  // 2 - Initial Start-up: Occurs for the first hour of operation.
  //                                              and only once in sensor's lifetime.
  // 3 - No Valid Output
  ensStatus = myENS.getFlags();
  Serial3.print("Gas Sensor Status Flag: ");
  Serial3.println(ensStatus);

  LedCtrl.begin();
  LedCtrl.startupCycle();

  // Attach interrupt callback to detect if SW0 was pressed during sleep
  pinConfigure(SW0, PIN_DIR_INPUT);
  attachInterrupt(SW0, button_pressed_callback, FALLING);

  // Now we configure the low power module for power down configuration, where
  // the LTE modem and the CPU will be powered down
  LowPower.configurePowerDown();

  Veml3328.begin();  // begin and shutdown onboard sensors to save power
  Mcp9808.begin();
  Veml3328.shutdown();
  Mcp9808.shutdown();


  Log.info(F("Starting MQTT for AWS example\r\n"));

  if (!initTopics()) {
    Log.error(F("Unable to initialize the MQTT topics. Stopping..."));
    while (1) {}
  }

  if (!Lte.begin()) {
    Log.error(F("Failed to connect to operator"));
    while (1) {}
  }

  // Attempt to connect to AWS
  if (MqttClient.beginAWS()) {
    MqttClient.subscribe(mqtt_sub_topic);
  } else {
    Log.error(F("Failed to connect to AWS"));
    while (1) {}
  }







  for (uint8_t i = 0; i < 3; i++) {



    myBME280.setMode(MODE_FORCED);  //Wake up sensor and take reading
    long startTime = millis();
    while (myBME280.isMeasuring() == false)
      ;  //Wait for sensor to start measurment
    while (myBME.isMeasuring() == true)
      ;  //Hang out while sensor completes the reading
    long endTime = millis();
    int AQI = myENS.getAQI();
    int TVOC = myENS.getTVOC();
    int ECO2 = myENS.getECO2();
    int humidity = myBME280.readFloatHumidity();
    int tempC = myBME280.readTempC();

    bool warn = false;

    if (TVOC > 300) {
      warn = true;
    }



    //String publishMessage = "{\"aqi\":"  + String(AQI)  +String(", \"tvoc\": ") +String(TVOC) +String(", \"ecotwo\": ") +String(ECO2)+ String("\"}");
    String publishMessage = "{\"aqi\":" + String(AQI) + String(", \"tvoc\": ") + String(TVOC) + String(", \"eco2\": ") + String(ECO2) + String(", \"hum\": ") + String(humidity) + String(", \"warn\": ") + String(warn) + String(", \"tempC\": ") + String(tempC) + String("\"}");

    char publishMessageChar[30] = "";
    publishMessage.toCharArray(publishMessageChar, 100);


    const bool published_successfully =
      MqttClient.publish(mqtt_pub_topic, publishMessageChar);
    //MqttClient.publish(mqtt_pub_topic, "{\"light\": 9, \"temp\": 9}");

    if (published_successfully) {
      Log.info(F("Published message"));
    } else {
      Log.error(F("Failed to publish\r\n"));
    }

    delay(2000);

    String message = MqttClient.readMessage(mqtt_sub_topic);

    // Read message will return an empty string if there were no new
    // messages, so anything other than that means that there were a
    // new message
    if (message != "") {
      Log.infof(F("Got new message: %s\r\n"), message.c_str());
    }

    delay(60000);
  }

  delay(200);


  // Test MQTT publish and receive
  /*
  for (uint8_t i = 0; i < 3; i++) {

    const bool published_successfully =
      MqttClient.publish(mqtt_pub_topic, "{\"light\": 9, \"temp\": 9}");

    if (published_successfully) {
      Log.info(F("Published message"));
    } else {
      Log.error(F("Failed to publish\r\n"));
    }

    delay(2000);

    String message = MqttClient.readMessage(mqtt_sub_topic);

    // Read message will return an empty string if there were no new
    // messages, so anything other than that means that there were a
    // new message
    if (message != "") {
      Log.infof(F("Got new message: %s\r\n"), message.c_str());
    }

    delay(2000);
  }
  */
  Log.info(F("Closing MQTT connection"));

  MqttClient.end();
}

void loop() {

  
  for (uint8_t i = 0; i < 3; i++) {

    int AQI = myENS.getAQI();
    int TVOC = myENS.getTVOC();
    int ECO2 = myENS.getECO2();
    int humidity = myBME280.readFloatHumidity();
    int tempC = myBME280.readTempC();

    bool warn = false;

    if(TVOC > 300 || ECO2 > 300 ||AQI > 300){
      warn = true; // warning true

    }else{

      warn = false;
    }


    
    
    String publishMessage = "{\"aqi\":"  + String(AQI)  +String(", \"tvoc\": ") +String(TVOC) +String(", \"eco2\": ") +String(ECO2)+ String(", \"hum\": ") +String(humidity) +String(", \"warn\": ") +String(warn)+ String(", \"tempC\": ") +String(tempC)+String("\"}");

    char publishMessageChar[40] = "";
    publishMessage.toCharArray(publishMessageChar,100); //must convert to C string, String won't work


    const bool published_successfully =
      MqttClient.publish(mqtt_pub_topic, publishMessageChar);
     
    if (published_successfully) {
      Log.info(F("Published message"));
    } else {
      Log.error(F("Failed to publish\r\n"));
    }

    delay(2000);

    String message = MqttClient.readMessage(mqtt_sub_topic);

    // Read message will return an empty string if there were no new
    // messages, so anything other than that means that there were a
    // new message
    if (message != "") {
      Log.infof(F("Got new message: %s\r\n"), message.c_str());
    }

    delay(60000);
  }
  
  delay(200);




  Log.info("Powering down...");

  // Allow some time for the log message to be transmitted before we power down
  delay(100);

  // Power down for 600 seconds
  LowPower.powerDown(600);

  if (woke_up_from_button) {
    Log.info("SW0 was pressed, force wake up");
    woke_up_from_button = false;
  }
  Log.info("Woke up!");

  // Do work ...
  Log.info("Doing work...");

}


bool initTopics() {
  ATCA_STATUS status = ECC608.begin();

  if (status != ATCA_SUCCESS) {
    Log.errorf(F("Failed to initialize ECC, error code: %X\r\n"), status);
    return false;
  }

  // Find the thing ID and set the publish and subscription topics
  uint8_t thing_name[128];
  size_t thing_name_length = sizeof(thing_name);

  status =
    ECC608.readProvisionItem(AWS_THINGNAME, thing_name, &thing_name_length);

  if (status != ATCA_SUCCESS) {
    Log.errorf(
      F("Could not retrieve thingname from the ECC, error code: %X\r\n"),
      status);
    return false;
  }

  snprintf_P(mqtt_sub_topic,
             sizeof(mqtt_sub_topic),
             MQTT_SUB_TOPIC_FMT,
             thing_name);
  snprintf_P(mqtt_pub_topic,
             sizeof(mqtt_pub_topic),
             MQTT_PUB_TOPIC_FMT,
             thing_name);

  return true;
}

GitHub Repository

This is the GitHub Repository for this project. currently a work in progress

Credits

V205

V205

4 projects • 7 followers
V205 is a maker driven by ideas.

Comments