Introduction: Easy Weather Station Using MQTT, XinaBox and Kibana

I built this weather station using XinaBox xCHIPS, which posts the sensor data to an MQTT broker over WiFi with CW01 (ESP8266 based xCHIP).

Step 1: Things Used in This Project

Hardware components

Software apps and online services

Step 2: Story

Intro

I built this weather station to sense the environment around my home just for fun. It was built using the modular platform, XinaBox. It is an easy to use platform that simply clicks together, no hardware hassles. The system runs on the CW01 xChip, which is an ESP8266 based WiFi core. The CW01 publishes the data to an MQTT broker over my home WiFi, where I can subscribe to the data with a Kibana server to visualize and plot the data.

Weather data plotted in Kibana

Step 3: What You Will Need

Apart from the set of xChips, you will need Arduino installed and the following libraries.

  • ESP8266 Board Driver from Arduino Board Manager
  • xCore (XinaBox Library)
  • xSW01 (XinaBox Library)
  • xSL01 (XinaBox Library)
  • ESP8266WiFi (Part of the ESP8266 board files in Arduino)
  • PubSubClient
  • NTPtimeESP

Additional:

  • MQTT Server to publish to.
  • Any visualization software that can subscribe to the MQTT Broker (I used Kibana).

Step 4: Programming the CW01

Click in an IP01 xChip onto the CW01 using an XC10 connector and plug into a USB port on your computer. Open Arduino, select the correct COM port, set the switches on the IP01 to "B" and "DCE" positions. Under Tools select "Generic ESP8266 Module" and set Flash Size to "4M (1M SPIFFS)", the rest can be left as default, then upload the sketch, which I will provide in the code section of this page. (Remember to change the WiFi and MQTT Server details to your own). Connect similar to the picture below.

CW02 and IP01 connected together for programming over USB.

Step 5: Building It

What build? Lol. The xChips make it very easy, simply click them together with the XC10 connectors until it looks like the picture at the top of the page. The only thing to bare in mind, the small XinaBox logos with chip name must all face the same direction, the bus at the top and bottom of the board are NOT the same. Usually the boards have a large logo at the bottom.

Step 6: Running It

Put the device where-ever you want to measure the environment, simply plug a micro-USB cable into the PU02 and power it from a USB phone charger or computer USB port.

Step 7: Code

CW01_Simple_Weather_MQTT C/C++
This is the main file of the Arduino Sketch (ino file). Has to be placed in a folder with the same name.

#include <xCore.h>
#include <xSW01.h>
#include <xSL01.h>
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <NTPtimeESP.h>

#define RED 12
#define GREEN 13
#define BLUE 5

char topic_WS[40];

const char* ssid     = "YourWifiName";
const char* password = "YourWifiPassword";

String    server =  "mqtt.xinabox.cc"; //Use your own mqtt server here
uint16_t  port  =  80;

const char* client_id = "ben_weather_station";
const char* username   = "admin";
const char* MQTT_password = "YourMQTTServerPassword";

String DEVICE = "Hackster_Station";
String ORGANIZATION = "XinaBox";

float SW01_Temperature, SW01_Humidity, SW01_Pressure, SL01_Lux, SL01_UVA, SL01_UVB, SL01_UVIndex;

long tick_sensor_poll = 0, tick_publish_mqtt = 0, tick_serial_print = 0;
int sensor_poll_interval = 1000, publish_mqtt_interval = 5000, serial_print_interval = 5000;

String timeStamp;

// Use WiFiClient class to create TCP connections
WiFiClient CW01_client;

// Setup the MQTT client class by passing in the WiFi client and MQTT server and login details.
PubSubClient client(CW01_client);

strDateTime dateTime;

/*************************** NTP Server Parameters **************************/
NTPtime NTPch("time.google.com");   // Choose server pool as required

void setup() {
  //Begin Serial bus
  Serial.begin(115200);

  //Begin I2C bus
  Wire.begin(2, 14);

  //Set LED Outputs
  pinMode(RED, OUTPUT);
  pinMode(GREEN, OUTPUT);
  pinMode(BLUE, OUTPUT);

  //Set up SL01
  SL01.begin();

  //Set up SW01
  SW01.begin();

  //Setup wifi and MQTT Broker
  setup_wifi();

}

void loop() {
  read_sensors();
  publish_mqtt();
  yield();
}

CONNECTIONS C/C++
Code to connect to WiFi and MQTT broker, also builds JSON to publish to MQTT. Must be in the same folder as the main Arduino sketch. I included it in the project as a tab named "CONNECTIONS".

void setup_wifi(void)
{
  Serial.print("[STATUS] Starting Wifi Connection");

  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    digitalWrite(RED, HIGH);
    delay(250);
    digitalWrite(RED, LOW);
    delay(250);
    Serial.print(".");
  }
  if (WiFi.status() == WL_CONNECTED) {
    digitalWrite(GREEN, HIGH);
  }

  Serial.println();
  Serial.println("[STATUS] WiFi connected.");

  mqtt_connect();
}

void mqtt_connect(void)
{
  Serial.println( "[STATUS] Connecting to MQTT Broker." );

  client.setServer(server.c_str() , port);

  if ( client.connect(client_id, username, MQTT_password) ) {
    Serial.println( "[STATUS] MQTT Broker Connected." );
}

  //Create MQTT Topic
  sprintf(topic_WS, "xinabox/data/ws/%s", client_id); //Replace with your own topic for MQTT Broker.
}

void publish_mqtt(void)
{
  if ((millis() - tick_publish_mqtt) > publish_mqtt_interval)
  {
    Serial.println("[STATUS] Publishing to MQTT Server...");

    // Prepare a JSON payload string
    String payload = "{";
    payload += "\"Weather_Station\": {";

    payload += "\"SW01\": {";
    payload += "\"Temperature(C)\":"; payload += SW01_Temperature; payload += ",";
    payload += "\"Humidity(%)\":"; payload += SW01_Humidity;payload += ",";
    payload += "\"Pressure(Pa)\":"; payload += SW01_Pressure;
    payload += "}";

    payload += ",";

    payload += "\"SL01\": {";
    payload += "\"Lux\":"; payload += SL01_Lux;  payload += ",";
    payload += "\"UVA(mW/m^2)\":"; payload += SL01_UVA;  payload += ",";
    payload += "\"UVB(mW/m^2)\":"; payload += SL01_UVB;  payload += ",";
    payload += "\"UVI\":"; payload += SL01_UVIndex;
    payload += "}";

    payload += ",";

    // insert user specific data
    payload += "\"Input\": {";

    payload += "\"Unit Name\":\""; payload += DEVICE; payload += "\"";

    payload += ",";
    payload += "\"Organization\":";   payload += "\"";
    payload += ORGANIZATION;  payload += "\"";

    payload += "},";

    // Get TimeStamp
    getTimeStamp();

    payload += "\"Timestamp\":{";
    payload += "\"Created\":\"";
    payload += timeStamp;
    payload += "\"";
    payload += "}";

    payload += "}";
    payload += "}";

    // Send payload
    char demo_data[1024];

    payload.toCharArray(demo_data, 1024);

    Serial.println();
    Serial.println("Publishing to MQTT Server...");
    Serial.print("Connection State :");
    Serial.print(" [RC : ");
    Serial.print(client.state());
    Serial.println("]");
    Serial.print("Topic: ");
    Serial.println(topic_WS);
    Serial.println("loc_payload : ");
    Serial.println(demo_data);
    unsigned int length_WS = strlen(demo_data);
    if (client.publish(topic_WS, demo_data, length_WS)) {
      Serial.println("[PUBLISHED]");
      digitalWrite(RED, LOW);
      digitalWrite(GREEN, HIGH);
    } else {
      Serial.print("[FAILED]");
      Serial.print(" [RC : ");
      Serial.print(client.state());
      Serial.println("]");
      digitalWrite(RED, HIGH);
      digitalWrite(GREEN, LOW);
    }
    Serial.println();

    tick_publish_mqtt = millis();
  }
}

// Get timestamp
void getTimeStamp(void) {
  dateTime = NTPch.getNTPtime(0.0, 0);

  if (dateTime.valid) {
    timeStamp = dateTime.year;
    timeStamp += "-";
    if (dateTime.month < 10) {
      timeStamp += "0";
      timeStamp += dateTime.month;
      timeStamp += "-";
    } else {
      timeStamp += dateTime.month;
      timeStamp += "-";
    }
    if (dateTime.day < 10) {
      timeStamp += "0";
      timeStamp += dateTime.day;
    } else {
      timeStamp += dateTime.day;
    }
    timeStamp += "T";
    if (dateTime.hour < 10) {
       timeStamp += "0";
       timeStamp += dateTime.hour;
       timeStamp += ":";
    } else {
      timeStamp += dateTime.hour;
      timeStamp += ":";
    }
    if (dateTime.minute < 10) {
      timeStamp += "0";
      timeStamp += dateTime.minute;
      timeStamp += ":";
    } else {
      timeStamp += dateTime.minute;
      timeStamp += ":"; 
    }
    if (dateTime.second < 10) {
      timeStamp += "0";
      timeStamp += dateTime.second;
    } else {
      timeStamp += dateTime.second;
    }
  }
}

SENSORS C/C++
Very simple code to read the XinaBox sensors. Must be in the same folder as the main Arduino sketch. I included it in the project as a tab named "SENSORS".

void read_sensors(void)
{
  if ((millis() - tick_sensor_poll) > sensor_poll_interval)
  {
    //Get sensor data
    SW01.poll();
    SL01.poll();

    //Move SW01 sensor data into global variables.
    SW01_Temperature = SW01.getTemperature_C();
    SW01_Humidity = SW01.getHumidity();
    SW01_Pressure = SW01.getPressure();

    //Move SL01 sensor data into global variables.
    SL01_Lux = SL01.getLUX();
    SL01_UVA = SL01.getUVA();
    SL01_UVB = SL01.getUVB();
    SL01_UVIndex = SL01.getUVIndex();

    tick_sensor_poll = millis();
  }
}