WolkWriter
Published © GPL3+

Arduino Environmental Monitoring

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

BeginnerFull instructions provided30 minutes6,514
Arduino Environmental Monitoring

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

Read more

Schematics

Arduino-MKR1000 and Environment click

Code

Environment monitoring sketch

Arduino
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);
    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;
  }
}

Environment click-deviceTemplate.json

JSON
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

Adafruit Unified Sensor Driver

Unified sensor library required for all sensors

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

WolkConnect-Arduino

Arduino library which provides easy connectivity to WolkAbout IoT Platform.

Credits

WolkWriter

WolkWriter

15 projects • 23 followers
With WolkAbout IoT Platform, we give you proven technology to develop powerful IoT applications and control your business ecosystem.

Comments