Monitor CO gas levels with Beebotte

This tutorial presents how Beebotte can be used to monitor Carbon-monoxide levels in the air. We will use Arduino Uno board to report data to Beebotte and an MQ-7 sensor to detect CO gas levels.
This tutorial assumes that you have successfully registered to Beebotte and obtained your API and Secret keys.

Tutorial contents:

Required Hardware

We need the following hardware to complete this tutorial:

  • Arduino UNO board or equivalent
  • Arduino Ethernet Shield to connect to the Internet
  • MQ-7 Carbon Monoxide gas sensor module for Arduino
  • LED
  • Jumper wires

Required Software and libraries

We will use the Arduino IDE to write our code, compile it and upload it to the board. We will use community provided Arduino libraries for MQTT connectivity and JSON parsing/decoding:

Open your Arduino IDE, click on Sketch tab and go to Include Library to install PubSubClient and ArduinoJson libraries.

Wiring

In this tutorial, we will use a ready made MQ-7 sensor module. If you wish to build your own module, you can buy the required parts and solder them to a PCB (this is beyond the scope of this tutorial).

The MQ-7 module has 4 pins:

  • Analog Output AOUT: Gives an analog voltage output in proportion to the amount of CO level the sensor detects.
  • Digital Output DOUT: If the analog voltage reaches a certain threshold, it will set the digital pin DOUT to high. Once DOUT pin is high, the arduino will trigger the LED to turn on.
  • Ground GND
  • 5V VCC

In this example, we will send AOUT and DOUT values to Beebotte to be able to monitor CO levels from anywhere using a Dashboard.

Follow the wiring diagram below to setup your hardware:


Create a Channel

In your account home page, click on Create New and follow the instructions to create your channel.

For the purpose of this tutorial, we will create a channel named Arduino and add a resource co with type number and another resource overLimit with type boolean.


Sending data to Beebotte

will use Beebotte MQTT endpoint to send sensor data to the created channel. We need to specify the channel token, and to indicate the names of the channel and resources.

Include dependencies:

#include <SPI.h>
#include <Ethernet.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>

In the code snippet below, we will initialize constants, definitions, MQTT client and all the pin connections of the sensor and the LED. Two variables, overLimit and rawValue, are also declared. These will be used to store the values of the analog pin AOUT and digital pin DOUT.

#define bbt "mqtt.beebotte.com" // Domain name of Beebotte MQTT service
#define Token "token:xxxxxxxxxxxxx" // Set your channel token here
#define Channel "Arduino" // Replace with your channel name
#define CoResource "co" //Replace with your resource name
#define OverLimitResource "overLimit" //Replace with your resource name
#define Write true

EthernetClient ethClient;
PubSubClient client(ethClient);

byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
long lastReconnectAttempt = 0;

// interval for sending CO readings to Beebotte
const long interval = 10000; // 10 seconds

// last time CO sensor data were sent to Beebotte
unsigned long lastReadingMillis = 0;

const char chars[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
char id[17];

// Set the static IP address to use if the DHCP fails to assign
// Feel free to change this according to your network settings
IPAddress ip(192, 168, 1, 50);
IPAddress gateway(192,168,1,1);
IPAddress subnet(255,255,255,0);

const int aOutPin=0; //the AOUT pin connected to analog pin A0 of Arduino
const int dOutPin=8; //the DOUT pin connected to digital pin D8 of Arduino
const int ledPin=13; //the anode of the LED connected to digital pin D13

int rawValue;
int overLimit;

In the Sketch setup function we will open serial communications for debugging and initialize the Ethernet connection.

void setup() {
  Serial.begin(9600); //Sets the data rate for for serial data transmission
  pinMode(dOutPin, INPUT); //sets the pin as an input to the arduino
  pinMode(ledPin, OUTPUT); //sets the pin as an output of the Arduino

  client.setServer(BBT, 1883);
  // Open serial communications and wait for port to open:
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }

  // start the Ethernet connection:
  if (Ethernet.begin(mac) == 0) {
    Serial.println("Failed to configure Ethernet using DHCP");
    // try to configure using IP address instead of DHCP:
     Ethernet.begin(mac, ip, gateway, subnet);
  }

  // give the Ethernet shield a second to initialize:
  delay(1000);
  Serial.println("connecting...");
  lastReconnectAttempt = 0;
}

Reading sensor data and publishing data to Beebotte

void readSensorData()
{
    rawValue= analogRead(aOutPin); //reads CO sensor's AOUT pin
    overLimit = digitalRead(dOutPin); //reads CO sensor's DOUT pin

    if (!isnan(rawValue )) {
        publish(CoResource, rawValue, Write);
    }

    if (overLimit== HIGH){
        digitalWrite(ledPin, HIGH);//if limit has been reached, LED turns on
        publish(OverLimitResource, true, Write);
    } else {
        digitalWrite(ledPin, LOW);//if threshold not reached, LED remains off
        publish(OverLimitResource, false, Write);
    }
}

// publishes data to the specified resource
void publish(const char* resource, float data, bool persist)
{
    StaticJsonBuffer<128> jsonOutBuffer;
    JsonObject& root = jsonOutBuffer.createObject();
    root["channel"] = Channel;
    root["resource"] = resource;
    if (persist) {
        root["write"] = true;
    }
    root["data"] = data;

    // Now print the JSON into a char buffer
    char buffer[128];
    root.printTo(buffer, sizeof(buffer));

    // Create the topic to publish to
    char topic[64];
    sprintf(topic, "%s/%s", Channel, resource);

    // Now publish the char buffer to Beebotte
    client.publish(topic, buffer);
}

A utility function to generate random ID for the MQTT connection

const char chars[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
char id[17];

const char * generateID()
{
  randomSeed(analogRead(0));
  int i = 0;
  for(i = 0; i < sizeof(id) - 1; i++) {
    id[i] = chars[random(sizeof(chars))];
  }
  id[sizeof(id) -1] = '\0';

  return id;
}

The Sketch's loop and MQTT reconnect function in case connection to Beebotte is lost:

// reconnects to Beebotte MQTT server
boolean reconnect() {
  if (client.connect(generateID(), TOKEN, "")) {
    Serial.println("Connected to Beebotte MQTT");
  }
  return client.connected();
}

void loop()
{
  if (!client.connected()) {
    long now = millis();
    if (now - lastReconnectAttempt > 5000) {
      lastReconnectAttempt = now;
      // Attempt to reconnect
      if (reconnect()) {
        lastReconnectAttempt = 0;
      }
    }
  } else {
    // Client connected

    // read sensor data every interval
    // and publish values to Beebotte
    unsigned long currentMillis = millis();
    if (currentMillis - lastReadingMillis >= interval) {
      // save the last time we read the sensor data
      lastReadingMillis = currentMillis;

      readSensorData();
    }

    client.loop();
  }
}

The complete code along with other Arduino examples can be accessed on Github.

Creating a Dashboard to display your data

In your account page, go to My Dashboards and click Create Dashboard; enter a friendly name and a short description for your dashboard then add 2 Attribute Widgets and 2 Timeline widgets. For every widget, indicate the channel and resource where data will be read from. Voilà.