Introduction

The project combines Arduino compatible EtherTen, or EtherMega, 802.3af Power-over-Ethernet regulator, few sensor modules and MQTT.

I decided to use this design because of simplicity of Arduino-like system combined with ready to use sensor modules and the fact that it can be powered and connected over a single network cable.

The current iteration is based on my previous work at The Foundation on the sensor station project and also my previous experiments with MQTT, EtherMega and Beaglebone.

I am running two sensor stations at the moment. Garden and Office.

Garden Station

Consists of EtherTen with 802.3af PoE regulator and sensor modules DHT22 (temperature and humidity), BMP180 (barometric pressure) and Keyes photo sensor (light).

This isn’t just a weather station

I consider this station a bit more than a weather station, although it partly is a weather station. Some of the sensors and sensors modules that are, or will be plugged in aren’t weather specific eg. seismic sensor.

Hardware

First thing I had to do was to solder the PoE regulator onto the EtherTen.

As an enclosure for EtherTen+PoE regulator I used water-proof plastic case.

I drilled a few holes in the enclosure and installed glands so I can get the network cable and wires for the sensor modules through it.

Initial version of Garden Sensor Station

My dad suggested that I should make a proper weather-station-like box for the enclosure and the sensor modules.

I made the box from left-overs of 2x2” oregon, 7mm thick plywood, a bit of foam insulation and some netting and painted the final result with vivid white colour.

Finally I put the enclosure and the sensor modules into the box and placed the prototype in the garden.

Garden Sensor Station prototype

Office Station

I use the office station as a testing ground to try out new sensors and sensor modules.

It consist of EtherMega with 802.3af PoE regulator, default sensor modules DHT22 (temperature and humidity), DS18B20 (temperature) and Keyes photo sensor (light) and to test out new things eg. barometric pressure, CO level, seismic activity, …

Testing Office Station

Source Code

The following is the current source code for the Garden station.

/* {{{ Node - Sensor Station - Garden

 Node - Sensor Station - Garden
 ------------------------------

 Sensor Station Node (v1) located in the garden does the following:

 1) Temperature - read temperature from DHT22 sensor
 2) Humudity - read humidity from DHT22 sensor
 3) Dew point - calculate dew point from temperature and humudity values obtained from DHT22 sensor
 4) Light - light intensity (0-1024) from Keyes photo sensor
 5) Pressure - barometric pressure from BMP180 sensor

 MQTT channels:

 /h/h/temperature
 /h/h/pressure
 /h/h/humudity
 /h/h/dewpoint
 /h/h/light
 /h/s

}}} */
// {{{ include
#include <SPI.h>
#include <Ethernet.h>
#include <PubSubClient.h>
#include <Wire.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <dht.h>
#include <SFE_BMP180.h>
// }}}
// {{{ Variables
boolean dosetup;
int DATA_INT;
char DATA_CHAR[10];
char TOPIC[10];
char JSON[80];
String json;
// }}}
// {{{ SFE_BMP180 barometric pressure sensor
SFE_BMP180 pressure;
// altitude of the sensor station in meters
#define ALTITUDE 8.0
char status;
double T,P,p0,a;
// }}}
// {{{ Temperature Sensor DS18B20
// Data wire is plugged into port 7 on the Arduino
#define ONE_WIRE_BUS 7
// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);
// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);
// }}}
// {{{ DHT22 sensor
#define dht_dpin A0
dht DHT;
// }}}
// {{{ Light
#define light_pin A1
// }}}
// {{{ Timer setup
long PREVIOUS_TIME = 0;
const long INTERVAL = 60000;
// }}}
// {{{ Networking
// MQTT server
byte server[] = {192,168,1,103};
// MQTT client - this node's IP address
byte ip[] = {192,168,1,112};
// MAC address
byte mac[] = {0xFE,0xED,0xDE,0xAD,0xBF,0xFF};
// }}}
// {{{ MQTT
// MQTT message buffer
//char message_buff[10];

// MQTT callback
void callback(char* topic, byte* payload, unsigned int length);

// MQTT PubSub client
EthernetClient ethClient;
PubSubClient client(server, 1883, callback, ethClient);

// MQTT callback function
void callback(char* topic, byte* payload, unsigned int length) {
  // Convert payload to string
  //int i = 0;
  //for(i=0; i&lt;length; i++) {
  //  message_buff[i] = payload[i];
  //}
  //message_buff[i] = '\0';
  //String msg = String(message_buff);
  //String cmd = String(splitCommand(msg, '/'));
  //String command = String(cmd[0]);
  //String command_value = String(cmd[1]);
  // XXX implement commands and msg from MQTT server below
}
// }}}
// {{{ setup()
void setup() {
  dosetup = false;
  // Start up the one-wire sensors library
  sensors.begin();
  // Start the node at the IP address
  Ethernet.begin(mac, ip);
  // Start MQTT client,
  client.connect("garden");
  // Start barometric pressure sensor
  pressure.begin();
}
// }}}
// {{{ freeRam()
int freeRam () {
  extern int __heap_start, *__brkval;
  int v;
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
}
// }}}
// {{{ Loop
void loop() {
  unsigned long current_time = millis();

  // Start MQTT client
  client.loop();

  if(current_time &lt; 23000) {
    // when the millis() buffer overflows,
    // reset also PREVIOUS_TIME
    if(PREVIOUS_TIME > 230000) {
      PREVIOUS_TIME = 0;
    }
    // init
    if(dosetup == false) {
      client.publish("/h/s","garden:up");
      publishToMQTT();
      dosetup = true;
      if(PREVIOUS_TIME == 0) {
        PREVIOUS_TIME = current_time;
      }
    }
  }

  if(current_time - PREVIOUS_TIME > INTERVAL)  {
    PREVIOUS_TIME = current_time;
    publishToMQTT();
  }
}
// }}}
// {{{ publishToMQTT()
void publishToMQTT() {
  publishDHT22();
  publishLight();
  publishPressure();
  client.publish("/h/s","garden:published");
}
// }}}
// {{{ publishPressure
void publishPressure() {
  status = pressure.startTemperature();
  if (status != 0)
  {
    // XXX FIXME DO NOT use delay() Wait for the measurement to complete:
    delay(status);
    status = pressure.getTemperature(T);
    if (status != 0)
    {
      status = pressure.startPressure(3);
      if (status != 0)
      {
        // Wait for the measurement to complete:
        delay(status);
        status = pressure.getPressure(P,T);
        if (status != 0)
        {
          String data = "{\"a\":";
          dtostrf(P, 2, 2, DATA_CHAR);
          data += DATA_CHAR;
          data += ", \"r\":";
          p0 = pressure.sealevel(P,ALTITUDE); // 8 meters, TT, NZ
          dtostrf(p0, 2, 2, DATA_CHAR);
          data += DATA_CHAR;
          data += ", \"alt\":";
          a = pressure.altitude(P,p0);
          dtostrf(a, 2, 2, DATA_CHAR);
          data += DATA_CHAR;
          data += "}";
          publishJSON("/h/g/p", "p", data, "BMP180");
        }
      }
    }
  }
}
// }}}
// {{{ publishLight
void publishLight() {
  String light_value = String(analogRead(light_pin), DEC);
  json = getJSON("l", light_value, "K853518");
  json.toCharArray(JSON, 100);
  client.publish("/h/g/l", JSON);
}
// }}}
// {{{ publishDHT22
void publishDHT22() {
  DHT.read22(dht_dpin);
  publishDHT();
}
// }}}
// {{{ publishDHT
void publishDHT() {
  // dht22 temperature
  dtostrf(DHT.temperature, 2, 2, DATA_CHAR);
  json = getJSON("t", DATA_CHAR, "DHT22");
  json.toCharArray(JSON, 100);
  client.publish("/h/g/t", JSON);
  // dht22 humidity
  dtostrf(DHT.humidity, 2, 2, DATA_CHAR);
  json = getJSON("h", DATA_CHAR, "DHT22");
  json.toCharArray(JSON, 100);
  client.publish("/h/g/h", JSON);
  // dht22 dewpoint
  dtostrf(dewPoint(DHT.temperature, DHT.humidity), 2, 2, DATA_CHAR);
  json = getJSON("dp", DATA_CHAR, "DHT22");
  json.toCharArray(JSON, 100);
  client.publish("/h/g/dp", JSON);
}
// }}}
// {{{ publishJSON(String topic, String key, String value, String source)
void publishJSON(String topic, String key, String value, String source) {
  json = getJSON(key, value, source);
  json.toCharArray(JSON, 100);
  topic.toCharArray(TOPIC, 30);
  client.publish(TOPIC,JSON);
}
// }}}
// {{{ getJSON(String key, String value, String source) {
String getJSON(String key, String value, String source) {
  String j = "{\"";
  j += key;
  j += "\": ";
  j += value;
  j += ", \"s\": \"";
  j += source;
  j += "\"}";
  return j;
}
// }}}
// {{{ dewPoint(double celsius, double humidity)
double dewPoint(double celsius, double humidity) {
  // (1) Saturation Vapor Pressure = ESGG(T)
  double RATIO = 373.15 / (273.15 + celsius);
  double RHS = -7.90298 * (RATIO - 1);
  RHS += 5.02808 * log10(RATIO);
  RHS += -1.3816e-7 * (pow(10, (11.344 * (1 - 1/RATIO ))) - 1) ;
  RHS += 8.1328e-3 * (pow(10, (-3.49149 * (RATIO - 1))) - 1) ;
  RHS += log10(1013.246);
  // factor -3 is to adjust units - Vapor Pressure SVP * humidity
  double VP = pow(10, RHS - 3) * humidity;
  // (2) DEWPOINT = F(Vapor Pressure)
  double T = log(VP/0.61078);   // temp var
  return (241.88 * T) / (17.558 - T);
}
// }}}