This entry is a quick overview of how the weatherstation operates. Previous posts go into more detail about how the device was programmed and constructed.
Schematic
This is how the entire system operates. The transmitter reads sensor data and transmits it. This transmission is picked up by the receiver, which uploads the transmission data to Xively.
Transmitter

The transmitter was built for very low power consumption and is powered by two D batteries. The temperature and pressure sensors are polled every 30 seconds. The MSP430G2553 is in deep sleep mode while not polling or transmitting sensor data.
Power consumption ranges from ~7 mA during sensor polling and ~90 μA while in deep sleep mode.
Components
- TI Launchpad with MSP430G2553
- BMP180 pressure sensor
- DHT22/AM2302 temperature/relative humidity sensor
- 433 MHz transmitter
- 47 kΩ resistor
Transmitter Code
// Uses BMP085 sensor library https://github.com/astuder/BMP085-template-library-Energia
// Modified VirtualWire library from http://xv4y.radioclub.asia/2013/01/09/librairie-virtualwire-1-10-pour-le-msp430/
// Uses modified DHT22 library from https://github.com/RobTillaart/Arduino/tree/master/libraries/DHTlib
// http://forum.43oh.com/topic/4344-how-read-the-battery-voltage/
// Import sensor libraries
#include <Wire.h>
#include <BMP085_t.h>
#include <dht.h>
#include <VirtualWire.h>
// Set sensor pins and other consts
#define DHT22_PIN 8
#define TX_PIN 10
#define TX_RATE 2000
#define DELAY 30000
#define ANALOG_HALFVCC_INPUT 11
static char data[25];
BMP085<0, 0, BMP085_F> PSensor; // Instantiate sensor, 0 = low precision pressure reading, Fahrenheit
dht DHT;
void setup() {
Wire.begin(); // Initialize I2C for pressure sensor
PSensor.begin(); // Initialize pressure sensor
vw_set_tx_pin(TX_PIN); // Set transmitter pin
vw_setup(TX_RATE); // Transmission rate (bits/s)
}
void loop() {
// Init string buffer
strcpy(data, "");
// Get temperature until a good reading is taken
boolean b;
do {
b = getTemp();
} while (!b);
// Get pressure
getPressure();
// Get VCC
getVCC();
// Transmit data
transmit();
// Clear string buffers
cleanup();
// Enter deep sleep mode
mdelay(DELAY);
}
// From http://forum.43oh.com/topic/3270-low-power-mode/?p=28536
// Uses Watchdog timer to enter LPM3 for a specified amount of time
// Timing is not entirely accurate
void mdelay(uint32_t milliseconds) {
WDTCTL = WDT_ADLY_1_9; // WDT triggered from ACLK after ~6ms, millis() will be skewed BTW
uint32_t wakeTime = milliseconds/5;
while(wakeTime>0){
wakeTime--;
/* Wait for WDT interrupt in LMP3 */
__bis_status_register(LPM3_bits+GIE);
}
WDTCTL = WDT_MDLY_0_5; // WDT back to original Energia setting, SMCLK 0.5ms/IRQ
}
// Reads the DHT22 sensor data. Only processes data if sensor returns DHTLIB_OK. Returns true if successful
boolean getTemp() {
int chk = DHT.read22(DHT22_PIN);
if (chk == DHTLIB_OK) {
// Retrieve sensor values
float humidity = DHT.humidity;
float temperature = DHT.temperature;
float dpoint = dewpoint(temperature, humidity);
// Convert to Fahrenheit
dpoint = CtoF(dpoint);
temperature = CtoF(temperature);
// Convert sensor values to strings
char humidityBuffer[5];
char temperatureBuffer[5];
char dpointBuffer[5];
fTos(humidity, humidityBuffer);
fTos(temperature, temperatureBuffer);
fTos(dpoint, dpointBuffer);
strcat(data, humidityBuffer);
strcat(data, ",");
strcat(data, temperatureBuffer);
strcat(data, ",");
strcat(data, dpointBuffer);
return true;
}
return false;
}
// Reads BMP085 sensor data
void getPressure() {
PSensor.refresh(); // read current sensor data
PSensor.calculate(); // run calculations for temperature and pressure
char pressureBuffer[5];
float pressure = (PSensor.pressure+50)*0.0002953; // display pressure in inHg
fTos(pressure, pressureBuffer);
strcat(data, ",");
strcat(data, pressureBuffer);
}
// Modified from http://forum.43oh.com/topic/4344-how-read-the-battery-voltage/?p=39202
void getVCC() {
// start with the 1.5V internal reference
analogReference(INTERNAL1V5);
int vcc = analogRead(ANALOG_HALFVCC_INPUT);
// if overflow, VCC is > 3V, switch to the 2.5V reference
if (vcc==0x3ff) {
analogReference(INTERNAL2V5);
vcc = (int)map(analogRead(ANALOG_HALFVCC_INPUT), 0, 1023, 0, 5000);
} else {
vcc = (int)map(vcc, 0, 1023, 0, 3000);
}
char i[2];
char d[4];
short integer = vcc / 1000;
short decimal = vcc % 1000;
itoa(integer, i, 10);
itoa(decimal, d, 10);
strcat(data, ",");
strcat(data, i);
strcat(data, ".");
strcat(data, d);
}
// Transmits a string using the VirtualWire library
void transmit() {
// Send buffer containing sensor data
vw_send((uint8_t*)data, strlen(data));
vw_wait_tx(); // Wait for the transmission to complete
}
// Takes a float and returns a string
void fTos(float f, char * c) {
int integer = f; // Integer part of float
int decimal = f*10 - integer*10; // Decimal part of float
char buf[5];
char i[5];
char d[5];
itoa(integer, i, 10);
itoa(decimal, d, 10);
strcpy(c, i);
strcat(c, ".");
strcat(c, d);
}
// Prepare to sleep by clearing string buffer
void cleanup() {
data[0] = '\0';
}
// Converts temperature from Celsius to Fahrenheit scale
float CtoF(float temp) {
return (temp * 1.8) + 32;
}
// Calculates the dew point in Celsius, from http://andrew.rsmas.miami.edu/bmcnoldy/Humidity.html
float dewpoint(float T, float RH) {
return 243.04 * (logf(RH/100)+((17.625 * T)/(243.04 + T)))/(17.625-logf(RH/100)-((17.625 * T)/(243.04 + T)));
}
Receiver
The receiver consists of an Arduino with an Ethernet shield. It receives transmissions from the transmitter and uploads them to Xively.
Components
- Arduino
- Arduino Ethernet shield
- Protoshield
- 433 MHz receiver
Receiver Code
// Uses VirtualWire to receive transmissions of weather data. Sends data to Xively for logging.
#include <Xively.h>
#include <HttpClient.h>
#include <VirtualWire.h>
#include <Ethernet.h>
#include <SPI.h>
#define RX_PIN 7
#define RX_RATE 500
#define DELAY 3600
#define FEEDID YOUR_FEED_ID
// Time
unsigned long mark = 0;
// Ethernet shield
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; // Arduino Ethernet shield MAC
EthernetClient xivelyEthernetClient;
// Xively channel key and IDs
const char xivelyKey[] = YOUR_XIVELY_KEY;
char tempID[] = "Temperature";
char presID[] = "Pressure";
char rhID[] = "Relative_Humidity";
char dewpointID[] = "Dewpoint";
char voltID[] = "Battery_Voltage";
char hiID[] = "Heat_Index";
// Define the strings for our datastream IDs
XivelyDatastream datastreams[] = {
XivelyDatastream(tempID, strlen(tempID), DATASTREAM_FLOAT),
XivelyDatastream(presID, strlen(presID), DATASTREAM_FLOAT),
XivelyDatastream(rhID, strlen(rhID), DATASTREAM_FLOAT),
XivelyDatastream(dewpointID, strlen(dewpointID), DATASTREAM_FLOAT),
XivelyDatastream(voltID, strlen(voltID), DATASTREAM_FLOAT),
XivelyDatastream(hiID, strlen(hiID), DATASTREAM_FLOAT)
};
// Create feed
XivelyFeed feed(FEEDID, datastreams, 6);
XivelyClient xivelyClient(xivelyEthernetClient);
// Weather data
// Index 0: Relative humidity (%)
// Index 1: Temperature (F)
// Index 2: Estimated dewpoint (F)
// Index 3: Barometric pressure (inHg)
// Index 4: Vcc on weatherstation (V)
char values[5][8];
void setup() {
vw_setup(RX_RATE);
vw_set_rx_pin(RX_PIN);
vw_rx_start();
getIPAddress();
}
void loop() {
listenForTx();
// Re-establish connection with router every hour
if (millis() - mark >= DELAY) {
getIPAddress();
mark = millis();
}
}
// Get IP address using DHCP
void getIPAddress() {
int connection;
do {
connection = Ethernet.begin(mac);
} while (connection == 0);
}
void listenForTx() {
uint8_t buf[VW_MAX_MESSAGE_LEN];
uint8_t buflen = VW_MAX_MESSAGE_LEN;
if (vw_get_message(buf, &buflen)) {
char message[30]; // String to copy buf into
message[0] = '\0';
// Check if transmission is valid. If all sensors have returned data, message will have 4 "," characters
// If there are not 4 commas, return without parsing data and wait for next transmission
int k = 0;
for (int i = 0; i < buflen; i++) {
if (buf[i] == ',') {
k++;
}
}
if (k != 4) {
return;
}
// Read buffer into string and split at each ","
int j = 0;
for (int i = 0; i < buflen; i++) {
if (buf[i] != ',') {
int length = strlen(message);
message[length] = buf[i];
message[length + 1] = '\0';
if (i == (buflen - 1)) {
strcpy(values[j], message);
break;
}
}
else {
strcpy(values[j], message);
message[0] = '\0';
j++;
}
}
// Log data to Xively
logData();
}
}
void logData() {
float rh = atof(values[0]);
float t = atof(values[1]);
float dp = atof(values[2]);
float p = atof(values[3]);
float v = atof(values[4]);
float hi = heatIndex(t, rh);
// Add sensor values to datastream
datastreams[0].setFloat(t);
datastreams[1].setFloat(p);
datastreams[2].setFloat(rh);
datastreams[3].setFloat(dp);
datastreams[4].setFloat(v);
datastreams[5].setFloat(hi);
// Send data to Xively
int retval = xivelyClient.put(feed, xivelyKey);
// Something's wrong with our connection
// Try to establish a connection with the router
if (retval < 0) {
getIPAddress();
}
}
// Uses NOAA heat index calculation from http://www.hpc.ncep.noaa.gov/html/heatindex_equation.shtml
float heatIndex(float t, float rh) {
float hi;
// First, calculate simple index
hi = 0.5 * (t + 61.0 + ((t-68.0)*1.2) + (rh*0.094));
hi = 0.5*(hi + t);
// When the heat index is >80 F, calculate using alternative method
if (hi >= 80) {
hi = -42.379 + 2.04901523*t + 10.14333127*rh - .22475541*t*rh - .00683783*t*t - .05481717*rh*rh + .00122874*t*t*rh + .00085282*t*rh*rh - .00000199*t*t*rh*rh;
// When the relative humidity is <13% and temperature is [80,112] F
if (rh < 13.0 && t >= 80.0 && t <= 112.0) {
hi = hi - (((13-rh)/4)*sqrt((17-abs(t-95.))/17));
} else if (rh > 85.0 && t >= 80.0 && t <= 87.0) { // When the relative humidity is >85% and temperature is [80,112] F
hi = hi + (((rh-85)/10) * ((87-t)/5));
}
}
return hi;
}
Performance

The transmitter is running on ~3 V. To boost range, it can take as much as 12 V. Currently, the transmitter sits outside--in an unused birdhouse in a shady area. It is approximately 15 ft from the receiver and I have observed no missed transmissions. If I were to place the transmitter farther away, I would need to supply a much higher voltage to the transmitter and/or construct a directional antenna.