Arduino Environmental Monitoring

Periodically measure environmental conditions and send them to WolkAbout IoT Platform to monitor the environment remotely.

18_01__-_arduino_-_hackster_WVrKYA9nlF

Things used in this project

Hardware components

Arduino MKR1000
Arduino MKR1000
× 1
MikroElektronika Environment click
× 1

Software apps and online services

Arduino IDE
Arduino IDE
WolkAbout IoT Platform
WolkAbout IoT Platform

Hand tools and fabrication machines

Adafruit Female/Female jumper wires

Story

Select Control

and then select Temperature from your device.

Repeat the process for Pressure and Humidity. Create another widget of the type Card, select Altitude and create another card widget for Gas Resistance. You can rearrange the widgets as you like it.

After adding widgets for the whole individual sensor reading you expect to receive from your device, the following dashboard will be ready to receive the data from your device.

So, go ahead and deploy the device to the environment you have chosen and monitor the conditions remotely.

Schematics

Code

Import this sketch into Arduino IDE and edit the WiFi credentials and device credentials
#include <Adafruit_Sensor.h>
#include <Adafruit_BME680.h>
#include <bme680_defs.h>
#include <bme680.h>

#include <WiFi101.h>
#include <RTCZero.h>
#include <FlashStorage.h>

#include "WolkConn.h"
#include "MQTTClient.h"
/*Number of outbound_message_t to store*/
#define STORAGE_SIZE 32
#define SEALEVELPRESSURE_HPA (1013.25)

/*Circular buffer to store outbound messages to persist*/
typedef struct{

  boolean valid;

  outbound_message_t outbound_messages[STORAGE_SIZE];

  uint32_t head;
  uint32_t tail;

  boolean empty;
  boolean full;

} Messages;

static Messages data;
/*Connection details*/
const char* ssid = "ssid";
const char* wifi_pass = "wifi_pass";

const char *device_key = "device_key";
const char *device_password = "device_password";
const char* hostname = "api-demo.wolkabout.com";
int portno = 1883;

WiFiClient espClient;
PubSubClient client(espClient);

/* WolkConnect-Arduino Connector context */
static wolk_ctx_t wolk;
/* Init flash storage */
FlashStorage(flash_store, Messages);
/*Init i2c sensor communication*/
Adafruit_BME680 bme;

RTCZero rtc;

bool read;
/*Read sensor every minute. If you change this parameter
make sure that it's <60*/
const byte readEvery = 1;
bool publish;
/*Publish every 10 minutes. If you change this parameter
make sure that it's <60*/
const byte publishEvery = 10;
byte publishMin;

/*Flash storage and custom persistence implementation*/
void _flash_store()
{
  data.valid = true;
  flash_store.write(data);
}
void increase_pointer(uint32_t* pointer)
{
    if ((*pointer) == (STORAGE_SIZE - 1))
    {
        (*pointer) = 0;
    }
    else
    {
        (*pointer)++;
    }
}

void _init()
{
    data = flash_store.read();

    if (data.valid == false)
    {
      data.head = 0;
      data.tail = 0;

      data.empty = true;
      data.full = false;

    }
}

bool _push(outbound_message_t* outbound_message)
{
    if(data.full)
    {
        increase_pointer(&data.head);
    }

    memcpy(&data.outbound_messages[data.tail], outbound_message, sizeof(outbound_message_t));

    increase_pointer(&data.tail);
    
    data.empty = false;
    data.full = (data.tail == data.head);

    return true;
}

bool _peek(outbound_message_t* outbound_message)
{
    memcpy(outbound_message, &data.outbound_messages[data.head], sizeof(outbound_message_t));
    return true;
}

bool _pop(outbound_message_t* outbound_message)
{
    memcpy(outbound_message, &data.outbound_messages[data.head], sizeof(outbound_message_t));
    
    increase_pointer(&data.head);
    
    data.full = false;
    data.empty = (data.tail == data.head);

    return true;
}

bool _is_empty()
{
    return data.empty;
}
void init_wifi()
{
  if ( WiFi.status() != WL_CONNECTED) {
    while (WiFi.begin(ssid, wifi_pass) != WL_CONNECTED) {
      delay(1000);
    }
  }
}
void setup_wifi() 
{

  delay(10);

  if ( WiFi.status() != WL_CONNECTED) {
    int numAttempts = 0;
    while (WiFi.begin(ssid, wifi_pass) != WL_CONNECTED) {
      numAttempts++;
      if(numAttempts == 10){
        Serial.println("Couldn't reach WiFi!");
        break;
      }
      delay(1000);
    }
  }
}

void setup() {
  
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, LOW);
  /*Initialize the circular buffer structure*/
  _init();
  
  init_wifi();

  wolk_init(&wolk, NULL, NULL, NULL, NULL,
            device_key, device_password, &client, hostname, portno, PROTOCOL_JSON_SINGLE, NULL, NULL);

  wolk_init_custom_persistence(&wolk, _push, _peek, _pop, _is_empty);
  
  /*The on board LED will turn on if something went wrong*/
  if(!bme.begin())
  {
    digitalWrite(LED_BUILTIN, HIGH);
  }
  /*Sensor init*/
  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

  delay(200);

  read = true;
  publish = true;

  /*Get current epoch from server*/
  wolk_connect(&wolk);
  delay(100);
  wolk_update_epoch(&wolk);
  while (!(wolk.pong_received)) {
    wolk_process(&wolk, 5);
    digitalWrite(LED_BUILTIN, HIGH);
    delay(1000);
  }
  digitalWrite(LED_BUILTIN, LOW);
  wolk_disconnect(&wolk);
  
  rtc.begin();

  rtc.setEpoch(wolk.epoch_time);

  rtc.setAlarmTime(rtc.getHours(), (rtc.getMinutes() + readEvery) % 60, rtc.getSeconds());
  rtc.enableAlarm(rtc.MATCH_MMSS);

  rtc.attachInterrupt(alarmMatch);
  publishMin = (rtc.getMinutes() + publishEvery) % 60;
  
  WiFi.lowPowerMode();

}

void loop() {
  /*In order to keep the interrupt routine as short as possible
  routine only sets the tasks to be done
  read = true means that the sensor reading should be done
  publish = true means that the readings should be published to platform
  or persisted in flash if the connection is not available
  */
  if(read)
  {
    read = false;
    
    if (!bme.performReading()) {
      digitalWrite(LED_BUILTIN, HIGH);
    }
    
    wolk_add_numeric_sensor_reading(&wolk, "T", bme.temperature, rtc.getEpoch());
    wolk_add_numeric_sensor_reading(&wolk, "H", bme.humidity, rtc.getEpoch());
    wolk_add_numeric_sensor_reading(&wolk, "P", bme.pressure / 100.0, rtc.getEpoch());
    wolk_add_numeric_sensor_reading(&wolk, "GR", bme.gas_resistance, rtc.getEpoch());
    wolk_add_numeric_sensor_reading(&wolk, "A", bme.readAltitude(SEALEVELPRESSURE_HPA), rtc.getEpoch());
    
    /*set new alarm*/
    int alarmMin = (rtc.getMinutes() + readEvery) % 60;
    rtc.setAlarmMinutes(alarmMin);
    delay(100);
  }
  
  if(publish)
  {
    publish = false;
    setup_wifi();
    wolk_connect(&wolk);
    if(!wolk.is_connected)
    {
      _flash_store();
    }
    delay(100);
    if(wolk_publish(&wolk) == W_TRUE)
    {
      _flash_store();
    }
    /*set new publish time*/
    publishMin = (rtc.getMinutes() + publishEvery) % 60;
    delay(100);
    wolk_disconnect(&wolk);
    delay(100);
  }
  delay(100);
  
}
/*Timed interrupt routine*/
void alarmMatch()
{
  read = true;
  if(publishMin == rtc.getMinutes())
  {
    publish = true;
  }
}
Device template that is used to create a device on WolkAbout IoT Platform
{
  "id": 833,
  "name": "Environment click",
  "protocol": "JsonSingleReferenceProtocol",
  "description": "Device template for the BME680 sensor on MikroElektronika's board Environment click",
  "deviceType": "STANDARD",
  "connectivityType": "MQTT_BROKER",
  "published": false,
  "feeds": [
    {
      "id": 1381,
      "name": "Temperature",
      "reference": "T",
      "description": "",
      "unit": {
        "id": 31,
        "name": "CELSIUS",
        "symbol": "℃",
        "readingTypeId": 2,
        "system": "SI",
        "context": null,
        "inUse": true,
        "readingTypeName": "TEMPERATURE"
      },
      "minimum": -40,
      "maximum": 80,
      "readingType": {
        "id": 2,
        "name": "TEMPERATURE",
        "dataType": "NUMERIC",
        "size": 1,
        "precision": 1,
        "labels": null,
        "iconName": "ico_temperature"
      }
    },
    {
      "id": 1382,
      "name": "Humidity",
      "reference": "H",
      "description": "",
      "unit": {
        "id": 124,
        "name": "HUMIDITY_PERCENT",
        "symbol": "%",
        "readingTypeId": 4,
        "system": "NON_SI",
        "context": null,
        "inUse": true,
        "readingTypeName": "HUMIDITY"
      },
      "minimum": 0,
      "maximum": 100,
      "readingType": {
        "id": 4,
        "name": "HUMIDITY",
        "dataType": "NUMERIC",
        "size": 1,
        "precision": 1,
        "labels": null,
        "iconName": "ico_humidity"
      }
    },
    {
      "id": 1383,
      "name": "Pressure",
      "reference": "P",
      "description": "",
      "unit": {
        "id": 112,
        "name": "MILLIBAR",
        "symbol": "mb",
        "readingTypeId": 3,
        "system": "NON_SI",
        "context": null,
        "inUse": true,
        "readingTypeName": "PRESSURE"
      },
      "minimum": 300,
      "maximum": 1100,
      "readingType": {
        "id": 3,
        "name": "PRESSURE",
        "dataType": "NUMERIC",
        "size": 1,
        "precision": 1,
        "labels": null,
        "iconName": "ico_pressure"
      }
    },
    {
      "id": 1384,
      "name": "Gas Resistance",
      "reference": "GR",
      "description": "",
      "unit": {
        "id": 23,
        "name": "OHM",
        "symbol": "Ω",
        "readingTypeId": 43,
        "system": "SI",
        "context": null,
        "inUse": true,
        "readingTypeName": "ELECTRIC_RESISTANCE"
      },
      "minimum": 0,
      "maximum": 100000,
      "readingType": {
        "id": 43,
        "name": "ELECTRIC_RESISTANCE",
        "dataType": "NUMERIC",
        "size": 1,
        "precision": 1,
        "labels": null,
        "iconName": null
      }
    },
    {
      "id": 1385,
      "name": "Altitude",
      "reference": "A",
      "description": "",
      "unit": {
        "id": 26,
        "name": "METRE",
        "symbol": "m",
        "readingTypeId": 22,
        "system": "SI",
        "context": null,
        "inUse": true,
        "readingTypeName": "LENGHT"
      },
      "minimum": -1000,
      "maximum": 3000,
      "readingType": {
        "id": 22,
        "name": "LENGHT",
        "dataType": "NUMERIC",
        "size": 1,
        "precision": 1,
        "labels": null,
        "iconName": "ico_length"
      }
    }
  ],
  "actuators": [],
  "alarms": [],
  "configs": [],
  "generallyAvailable": false
}

WolkAbout Environment Monitoring

This repository contains the Arduino sketch used in this project

Arduino WiFi 101 Shield

Wifi library for the Arduino WiFi 101 Shield

Wifi library for the Arduino WiFi 101 Shield — Read More

Adafruit Unified Sensor Driver

Unified sensor library required for all sensors

Common sensor library — Read More

Adafruit BME680 Library

Library used for the Environment click to get sensor readings

RTC Library for Arduino

RTC Library for SAMD21 based boards

FlashStorage library for Arduino

A convenient way to store data into Flash memory on the ATSAMD21

A convenient way to store data into Flash memory on the ATSAMD21 — Read More

WolkConnect-Arduino

Arduino library which provides easy connectivity to WolkAbout IoT Platform.

Arduino library which provides easy connectivity to WolkAbout IoT Platform. — Read More

 

Source : Arduino Environmental Monitoring


About The Author

Ibrar Ayyub

I am an experienced technical writer holding a Master's degree in computer science from BZU Multan, Pakistan University. With a background spanning various industries, particularly in home automation and engineering, I have honed my skills in crafting clear and concise content. Proficient in leveraging infographics and diagrams, I strive to simplify complex concepts for readers. My strength lies in thorough research and presenting information in a structured and logical format.

Follow Us:
LinkedinTwitter

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top