The MSP430G2553 was programmed using
Energia. This final script is essentially a culmination of code from my previous posts, with an emphasis on maximizing battery life. As such, I have decided to only have the microcontroller poll the sensors every 30 s, so most of its operation will be in deep sleep mode.
What the Code Needs to Do
- Wake from deep sleep mode
- Read temperature/relative humidity
- Read barometric pressure
- Read voltage
- Compile readings into a string
- Transmit string
- Enter deep sleep mode
Power Consumption
I found a very informative
post on the 43oh forums detailing deep sleep mode, specifically how to enter it. For my purposes, I want the deepest possible sleep mode without relying on an external signal to wake the microprocessor. As such, LPM3 is as "low" as you can go before requiring an external "wakeup" signal.
I did some very rudimentary current measurements with a multimeter. The current peaks at around 7 mA for ~1 s, then idles at around 90 μA.
Areas For Improvement
- The BMP180 returns temperature readings. Either use this value or edit the library so it doesn't read the temperature.
- All unit conversions are processed at the weatherstation. It might be more efficient to simply transmit the raw data and have the receiver, which is not battery powered, process it.
Show Code
// Author: Zac DeMeo
// 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)));
}