ARM

Spark Core Weather Station

In my previous blog post I described my first encounter with the Spark Core. Today I want to demonstrate a first simple code example. For this I connected the Spark Core to a Weather Shield from Sparkfun. The shield offers sensors for light, humidity, temperature and pressure. It can even be extended with rain and wind sensors as well as GPS.
.Spark Core Weather
The shield comes with a nice set of libraries and examples that I used as a starting point. To keep things really simple, I combine the entire Weather Shield source with the sensor library functions and the setup() and loop() into a single file. This did not take long and compiled quickly. I also removed the wind and rain related functionality as I did not plan to use those. The source code below takes measurements every second and writes the them to the USB serial port.

/*
MPL3115A2 Barometric Pressure Sensor Library
By: Nathan Seidle
SparkFun Electronics
Date: September 22nd, 2013
License: This code is public domain but you buy me a beer if you use this and we meet someday (Beerware license).
This library allows an Arduino to read from the MPL3115A2 low-cost high-precision pressure sensor.
If you have feature suggestions or need support please use the github support page: https://github.com/sparkfun/MPL3115A2_Breakout
Hardware Setup: The MPL3115A2 lives on the I2C bus. Attach the SDA pin to A4, SCL to A5. Use inline 10k resistors
if you have a 5V board. If you are using the SparkFun breakout board you *do not* need 4.7k pull-up resistors
on the bus (they are built-in).
Link to the breakout board product:
Software:
.begin() Gets sensor on the I2C bus.
.readAltitude() Returns float with meters above sealevel. Ex: 1638.94
.readAltitudeFt() Returns float with feet above sealevel. Ex: 5376.68
.readPressure() Returns float with barometric pressure in Pa. Ex: 83351.25
.readTemp() Returns float with current temperature in Celsius. Ex: 23.37
.readTempF() Returns float with current temperature in Fahrenheit. Ex: 73.96
.setModeBarometer() Puts the sensor into Pascal measurement mode.
.setModeAltimeter() Puts the sensor into altimetery mode.
.setModeStandy() Puts the sensor into Standby mode. Required when changing CTRL1 register.
.setModeActive() Start taking measurements!
.setOversampleRate(byte) Sets the # of samples from 1 to 128. See datasheet.
.enableEventFlags() Sets the fundamental event flags. Required during setup.
*/
/*******************************************************************************************/
//#include "MPL3115A2.h"
/*******************************************************************************************/
#define MPL3115A2_ADDRESS 0x60 // Unshifted 7-bit I2C address for sensor
#define STATUS 0x00
#define OUT_P_MSB 0x01
#define OUT_P_CSB 0x02
#define OUT_P_LSB 0x03
#define OUT_T_MSB 0x04
#define OUT_T_LSB 0x05
#define DR_STATUS 0x06
#define OUT_P_DELTA_MSB 0x07
#define OUT_P_DELTA_CSB 0x08
#define OUT_P_DELTA_LSB 0x09
#define OUT_T_DELTA_MSB 0x0A
#define OUT_T_DELTA_LSB 0x0B
#define WHO_AM_I 0x0C
#define F_STATUS 0x0D
#define F_DATA 0x0E
#define F_SETUP 0x0F
#define TIME_DLY 0x10
#define SYSMOD 0x11
#define INT_SOURCE 0x12
#define PT_DATA_CFG 0x13
#define BAR_IN_MSB 0x14
#define BAR_IN_LSB 0x15
#define P_TGT_MSB 0x16
#define P_TGT_LSB 0x17
#define T_TGT 0x18
#define P_WND_MSB 0x19
#define P_WND_LSB 0x1A
#define T_WND 0x1B
#define P_MIN_MSB 0x1C
#define P_MIN_CSB 0x1D
#define P_MIN_LSB 0x1E
#define T_MIN_MSB 0x1F
#define T_MIN_LSB 0x20
#define P_MAX_MSB 0x21
#define P_MAX_CSB 0x22
#define P_MAX_LSB 0x23
#define T_MAX_MSB 0x24
#define T_MAX_LSB 0x25
#define CTRL_REG1 0x26
#define CTRL_REG2 0x27
#define CTRL_REG3 0x28
#define CTRL_REG4 0x29
#define CTRL_REG5 0x2A
#define OFF_P 0x2B
#define OFF_T 0x2C
#define OFF_H 0x2D
class MPL3115A2 {
public:
MPL3115A2();
//Public Functions
bool begin(); // Gets sensor on the I2C bus.
float readAltitude(); // Returns float with meters above sealevel. Ex: 1638.94
float readAltitudeFt(); // Returns float with feet above sealevel. Ex: 5376.68
float readPressure(); // Returns float with barometric pressure in Pa. Ex: 83351.25
float readTemperature(); // Returns float with current temperature in Celsius. Ex: 23.37
float readTemperatureF(); // Returns float with current temperature in Fahrenheit. Ex: 73.96
void setModeBarometer(); // Puts the sensor into Pascal measurement mode.
void setModeAltimeter(); // Puts the sensor into altimetery mode.
void setModeStandby(); // Puts the sensor into Standby mode. Required when changing CTRL1 register.
void setModeActive(); // Start taking measurements!
void setOversampleRate(byte); // Sets the # of samples from 1 to 128. See datasheet.
void enableEventFlags(); // Sets the fundamental event flags. Required during setup.
//Public Variables
private:
//Private Functions
void toggleOneShot();
byte IIC_Read(byte regAddr);
void IIC_Write(byte regAddr, byte value);
//Private Variables
};
/*******************************************************************************************/
MPL3115A2::MPL3115A2()
{
//Set initial values for private vars
}
//Begin
/*******************************************************************************************/
//Start I2C communication
bool MPL3115A2::begin(void)
{
Wire.begin();
}
//Returns the number of meters above sea level
//Returns -1 if no new data is available
float MPL3115A2::readAltitude()
{
toggleOneShot(); //Toggle the OST bit causing the sensor to immediately take another reading
//Wait for PDR bit, indicates we have new pressure data
int counter = 0;
while( (IIC_Read(STATUS) & (1<<1)) == 0)
{
if(++counter > 600) return(-999); //Error out after max of 512ms for a read
delay(1);
}
// Read pressure registers
Wire.beginTransmission(MPL3115A2_ADDRESS);
Wire.write(OUT_P_MSB); // Address of data to get
Wire.endTransmission(false); // Send data to I2C dev with option for a repeated start. THIS IS NECESSARY and not supported before Arduino V1.0.1!
Wire.requestFrom(MPL3115A2_ADDRESS, 3); // Request three bytes
//Wait for data to become available
counter = 0;
while(Wire.available() < 3)
{
if(counter++ > 100) return(-999); //Error out
delay(1);
}
byte msb, csb, lsb;
msb = Wire.read();
csb = Wire.read();
lsb = Wire.read();
// The least significant bytes l_altitude and l_temp are 4-bit,
// fractional values, so you must cast the calulation in (float),
// shift the value over 4 spots to the right and divide by 16 (since
// there are 16 values in 4-bits).
float tempcsb = (lsb>>4)/16.0;
float altitude = (float)( (msb << 8) | csb) + tempcsb;
return(altitude);
}
//Returns the number of feet above sea level
float MPL3115A2::readAltitudeFt()
{
return(readAltitude() * 3.28084);
}
//Reads the current pressure in Pa
//Unit must be set in barometric pressure mode
//Returns -1 if no new data is available
float MPL3115A2::readPressure()
{
//Check PDR bit, if it's not set then toggle OST
if(IIC_Read(STATUS) & (1<<2) == 0) toggleOneShot(); //Toggle the OST bit causing the sensor to immediately take another reading
//Wait for PDR bit, indicates we have new pressure data
int counter = 0;
while(IIC_Read(STATUS) & (1<<2) == 0)
{
if(++counter > 600) return(-999); //Error out after max of 512ms for a read
delay(1);
}
// Read pressure registers
Wire.beginTransmission(MPL3115A2_ADDRESS);
Wire.write(OUT_P_MSB); // Address of data to get
Wire.endTransmission(false); // Send data to I2C dev with option for a repeated start. THIS IS NECESSARY and not supported before Arduino V1.0.1!
Wire.requestFrom(MPL3115A2_ADDRESS, 3); // Request three bytes
//Wait for data to become available
counter = 0;
while(Wire.available() < 3)
{
if(counter++ > 100) return(-999); //Error out
delay(1);
}
byte msb, csb, lsb;
msb = Wire.read();
csb = Wire.read();
lsb = Wire.read();
toggleOneShot(); //Toggle the OST bit causing the sensor to immediately take another reading
// Pressure comes back as a left shifted 20 bit number
long pressure_whole = (long)msb<<16 | (long)csb<<8 | (long)lsb;
pressure_whole >>= 6; //Pressure is an 18 bit number with 2 bits of decimal. Get rid of decimal portion.
lsb &= 0b00110000; //Bits 5/4 represent the fractional component
lsb >>= 4; //Get it right aligned
float pressure_decimal = (float)lsb/4.0; //Turn it into fraction
float pressure = (float)pressure_whole + pressure_decimal;
return(pressure);
}
float MPL3115A2::readTemperature()
{
if(IIC_Read(STATUS) & (1<<1) == 0) toggleOneShot(); //Toggle the OST bit causing the sensor to immediately take another reading
//Wait for TDR bit, indicates we have new temp data
int counter = 0;
while( (IIC_Read(STATUS) & (1<<1)) == 0)
{
if(++counter > 600) return(-999); //Error out after max of 512ms for a read
delay(1);
}
// Read temperature registers
Wire.beginTransmission(MPL3115A2_ADDRESS);
Wire.write(OUT_T_MSB); // Address of data to get
Wire.endTransmission(false); // Send data to I2C dev with option for a repeated start. THIS IS NECESSARY and not supported before Arduino V1.0.1!
Wire.requestFrom(MPL3115A2_ADDRESS, 2); // Request two bytes
//Wait for data to become available
counter = 0;
while(Wire.available() < 2)
{
if(counter++ > 100) return(-999); //Error out
delay(1);
}
byte msb, lsb;
msb = Wire.read();
lsb = Wire.read();
toggleOneShot(); //Toggle the OST bit causing the sensor to immediately take another reading
//Negative temperature fix by D.D.G.
short foo = 0;
bool negSign = false;
//Check for 2s compliment
if(msb > 0x7F)
{
foo = ~((msb << 8) + lsb) + 1; //2’s complement
msb = foo >> 8;
lsb = foo & 0x00F0;
negSign = true;
}
// The least significant bytes l_altitude and l_temp are 4-bit,
// fractional values, so you must cast the calulation in (float),
// shift the value over 4 spots to the right and divide by 16 (since
// there are 16 values in 4-bits).
float templsb = (lsb>>4)/16.0; //temp, fraction of a degree
float temperature = (float)(msb + templsb);
if (negSign) temperature = 0 - temperature;
return(temperature);
}
//Give me temperature in fahrenheit!
float MPL3115A2::readTemperatureF()
{
return((readTemperature() * 9.0)/ 5.0 + 32.0); // Convert celsius to fahrenheit
}
//Sets the mode to Barometer
//CTRL_REG1, ALT bit
void MPL3115A2::setModeBarometer()
{
byte tempSetting = IIC_Read(CTRL_REG1); //Read current settings
tempSetting &= ~(1<<7); //Clear ALT bit
IIC_Write(CTRL_REG1, tempSetting);
}
//Sets the mode to Altimeter
//CTRL_REG1, ALT bit
void MPL3115A2::setModeAltimeter()
{
byte tempSetting = IIC_Read(CTRL_REG1); //Read current settings
tempSetting |= (1<<7); //Set ALT bit
IIC_Write(CTRL_REG1, tempSetting);
}
//Puts the sensor in standby mode
//This is needed so that we can modify the major control registers
void MPL3115A2::setModeStandby()
{
byte tempSetting = IIC_Read(CTRL_REG1); //Read current settings
tempSetting &= ~(1<<0); //Clear SBYB bit for Standby mode
IIC_Write(CTRL_REG1, tempSetting);
}
//Puts the sensor in active mode
//This is needed so that we can modify the major control registers
void MPL3115A2::setModeActive()
{
byte tempSetting = IIC_Read(CTRL_REG1); //Read current settings
tempSetting |= (1<<0); //Set SBYB bit for Active mode
IIC_Write(CTRL_REG1, tempSetting);
}
//Call with a rate from 0 to 7. See page 33 for table of ratios.
//Sets the over sample rate. Datasheet calls for 128 but you can set it
//from 1 to 128 samples. The higher the oversample rate the greater
//the time between data samples.
void MPL3115A2::setOversampleRate(byte sampleRate)
{
if(sampleRate > 7) sampleRate = 7; //OS cannot be larger than 0b.0111
sampleRate <<= 3; //Align it for the CTRL_REG1 register
byte tempSetting = IIC_Read(CTRL_REG1); //Read current settings
tempSetting &= 0b11000111; //Clear out old OS bits
tempSetting |= sampleRate; //Mask in new OS bits
IIC_Write(CTRL_REG1, tempSetting);
}
//Enables the pressure and temp measurement event flags so that we can
//test against them. This is recommended in datasheet during setup.
void MPL3115A2::enableEventFlags()
{
IIC_Write(PT_DATA_CFG, 0x07); // Enable all three pressure and temp event flags
}
//Clears then sets the OST bit which causes the sensor to immediately take another reading
//Needed to sample faster than 1Hz
void MPL3115A2::toggleOneShot(void)
{
byte tempSetting = IIC_Read(CTRL_REG1); //Read current settings
tempSetting &= ~(1<<1); //Clear OST bit
IIC_Write(CTRL_REG1, tempSetting);
tempSetting = IIC_Read(CTRL_REG1); //Read current settings to be safe
tempSetting |= (1<<1); //Set OST bit
IIC_Write(CTRL_REG1, tempSetting);
}
// These are the two I2C functions in this sketch.
byte MPL3115A2::IIC_Read(byte regAddr)
{
// This function reads one byte over IIC
Wire.beginTransmission(MPL3115A2_ADDRESS);
Wire.write(regAddr); // Address of CTRL_REG1
Wire.endTransmission(false); // Send data to I2C dev with option for a repeated start. THIS IS NECESSARY and not supported before Arduino V1.0.1!
Wire.requestFrom(MPL3115A2_ADDRESS, 1); // Request the data...
return Wire.read();
}
void MPL3115A2::IIC_Write(byte regAddr, byte value)
{
// This function writes one byto over IIC
Wire.beginTransmission(MPL3115A2_ADDRESS);
Wire.write(regAddr);
Wire.write(value);
Wire.endTransmission(true);
}
/*
HTU21D Humidity Sensor Library
By: Nathan Seidle
SparkFun Electronics
Date: September 22nd, 2013
License: This code is public domain but you buy me a beer if you use this and we meet someday (Beerware license).
This library allows an Arduino to read from the HTU21D low-cost high-precision humidity sensor.
If you have feature suggestions or need support please use the github support page: https://github.com/sparkfun/HTU21D
Hardware Setup: The HTU21D lives on the I2C bus. Attach the SDA pin to A4, SCL to A5. If you are using the SparkFun
breakout board you *do not* need 4.7k pull-up resistors on the bus (they are built-in).
Link to the breakout board product:
Software:
Call HTU21D.Begin() in setup.
HTU21D.ReadHumidity() will return a float containing the humidity. Ex: 54.7
HTU21D.ReadTemperature() will return a float containing the temperature in Celsius. Ex: 24.1
HTU21D.SetResolution(byte: 0b.76543210) sets the resolution of the readings.
HTU21D.check_crc(message, check_value) verifies the 8-bit CRC generated by the sensor
HTU21D.read_user_register() returns the user register. Used to set resolution.
*/
//#include <Wire.h>
/*******************************************************************************************/
//#include "HTU21D.h"
/*******************************************************************************************/
#define HTDU21D_ADDRESS 0x40 //Unshifted 7-bit I2C address for the sensor
#define TRIGGER_TEMP_MEASURE_HOLD 0xE3
#define TRIGGER_HUMD_MEASURE_HOLD 0xE5
#define TRIGGER_TEMP_MEASURE_NOHOLD 0xF3
#define TRIGGER_HUMD_MEASURE_NOHOLD 0xF5
#define WRITE_USER_REG 0xE6
#define READ_USER_REG 0xE7
#define SOFT_RESET 0xFE
class HTU21D {
public:
HTU21D();
//Public Functions
bool begin();
float readHumidity(void);
float readTemperature(void);
void setResolution(byte resBits);
//Public Variables
private:
//Private Functions
byte read_user_register(void);
byte check_crc(uint16_t message_from_sensor, uint8_t check_value_from_sensor);
//Private Variables
};
/*******************************************************************************************/
HTU21D::HTU21D()
{
//Set initial values for private vars
}
//Begin
/*******************************************************************************************/
//Start I2C communication
bool HTU21D::begin(void)
{
Wire.begin();
}
//Read the humidity
/*******************************************************************************************/
//Calc humidity and return it to the user
//Returns 998 if I2C timed out
//Returns 999 if CRC is wrong
float HTU21D::readHumidity(void)
{
//Request a humidity reading
Wire.beginTransmission(HTDU21D_ADDRESS);
Wire.write(TRIGGER_HUMD_MEASURE_NOHOLD); //Measure humidity with no bus holding
Wire.endTransmission();
//Hang out while measurement is taken. 50mS max, page 4 of datasheet.
delay(55);
//Comes back in three bytes, data(MSB) / data(LSB) / Checksum
Wire.requestFrom(HTDU21D_ADDRESS, 3);
//Wait for data to become available
int counter = 0;
while(Wire.available() < 3)
{
counter++;
delay(1);
if(counter > 100) return 998; //Error out
}
byte msb, lsb, checksum;
msb = Wire.read();
lsb = Wire.read();
checksum = Wire.read();
/* //Used for testing
byte msb, lsb, checksum;
msb = 0x4E;
lsb = 0x85;
checksum = 0x6B;*/
unsigned int rawHumidity = ((unsigned int) msb << 8) | (unsigned int) lsb;
if(check_crc(rawHumidity, checksum) != 0) return(999); //Error out
//sensorStatus = rawHumidity & 0x0003; //Grab only the right two bits
rawHumidity &= 0xFFFC; //Zero out the status bits but keep them in place
//Given the raw humidity data, calculate the actual relative humidity
float tempRH = rawHumidity / (float)65536; //2^16 = 65536
float rh = -6 + (125 * tempRH); //From page 14
return(rh);
}
//Read the temperature
/*******************************************************************************************/
//Calc temperature and return it to the user
//Returns 998 if I2C timed out
//Returns 999 if CRC is wrong
float HTU21D::readTemperature(void)
{
//Request the temperature
Wire.beginTransmission(HTDU21D_ADDRESS);
Wire.write(TRIGGER_TEMP_MEASURE_NOHOLD);
Wire.endTransmission();
//Hang out while measurement is taken. 50mS max, page 4 of datasheet.
delay(55);
//Comes back in three bytes, data(MSB) / data(LSB) / Checksum
Wire.requestFrom(HTDU21D_ADDRESS, 3);
//Wait for data to become available
int counter = 0;
while(Wire.available() < 3)
{
counter++;
delay(1);
if(counter > 100) return 998; //Error out
}
unsigned char msb, lsb, checksum;
msb = Wire.read();
lsb = Wire.read();
checksum = Wire.read();
/* //Used for testing
byte msb, lsb, checksum;
msb = 0x68;
lsb = 0x3A;
checksum = 0x7C; */
unsigned int rawTemperature = ((unsigned int) msb << 8) | (unsigned int) lsb;
if(check_crc(rawTemperature, checksum) != 0) return(999); //Error out
//sensorStatus = rawTemperature & 0x0003; //Grab only the right two bits
rawTemperature &= 0xFFFC; //Zero out the status bits but keep them in place
//Given the raw temperature data, calculate the actual temperature
float tempTemperature = rawTemperature / (float)65536; //2^16 = 65536
float realTemperature = -46.85 + (175.72 * tempTemperature); //From page 14
return(realTemperature);
}
//Set sensor resolution
/*******************************************************************************************/
//Sets the sensor resolution to one of four levels
//Page 12:
// 0/0 = 12bit RH, 14bit Temp
// 0/1 = 8bit RH, 12bit Temp
// 1/0 = 10bit RH, 13bit Temp
// 1/1 = 11bit RH, 11bit Temp
//Power on default is 0/0
void HTU21D::setResolution(byte resolution)
{
byte userRegister = read_user_register(); //Go get the current register state
userRegister &= 0b01111110; //Turn off the resolution bits
resolution &= 0b10000001; //Turn off all other bits but resolution bits
userRegister |= resolution; //Mask in the requested resolution bits
//Request a write to user register
Wire.beginTransmission(HTDU21D_ADDRESS);
Wire.write(WRITE_USER_REG); //Write to the user register
Wire.write(userRegister); //Write the new resolution bits
Wire.endTransmission();
}
//Read the user register
byte HTU21D::read_user_register(void)
{
byte userRegister;
//Request the user register
Wire.beginTransmission(HTDU21D_ADDRESS);
Wire.write(READ_USER_REG); //Read the user register
Wire.endTransmission();
//Read result
Wire.requestFrom(HTDU21D_ADDRESS, 1);
userRegister = Wire.read();
return(userRegister);
}
//Give this function the 2 byte message (measurement) and the check_value byte from the HTU21D
//If it returns 0, then the transmission was good
//If it returns something other than 0, then the communication was corrupted
//From: http://www.nongnu.org/avr-libc/user-manual/group__util__crc.html
//POLYNOMIAL = 0x0131 = x^8 + x^5 + x^4 + 1 : http://en.wikipedia.org/wiki/Computation_of_cyclic_redundancy_checks
#define SHIFTED_DIVISOR 0x988000 //This is the 0x0131 polynomial shifted to farthest left of three bytes
byte HTU21D::check_crc(uint16_t message_from_sensor, uint8_t check_value_from_sensor)
{
//Test cases from datasheet:
//message = 0xDC, checkvalue is 0x79
//message = 0x683A, checkvalue is 0x7C
//message = 0x4E85, checkvalue is 0x6B
uint32_t remainder = (uint32_t)message_from_sensor << 8; //Pad with 8 bits because we have to add in the check value
remainder |= check_value_from_sensor; //Add on the check value
uint32_t divsor = (uint32_t)SHIFTED_DIVISOR;
for (int i = 0 ; i < 16 ; i++) //Operate on only 16 positions of max 24. The remaining 8 are our remainder and should be zero when we're done.
{
//Serial.print("remainder: ");
//Serial.println(remainder, BIN);
//Serial.print("divsor: ");
//Serial.println(divsor, BIN);
//Serial.println();
if( remainder & (uint32_t)1<<(23 - i) ) //Check if there is a one in the left position
remainder ^= divsor;
divsor >>= 1; //Rotate the divsor max 16 times so that we have 8 bits left of a remainder
}
return (byte)remainder;
}
/*
Weather Shield Example
By: Nathan Seidle
SparkFun Electronics
Date: November 16th, 2013
License: This code is public domain but you buy me a beer if you use this and we meet someday (Beerware license).
Much of this is based on Mike Grusin's USB Weather Board code: https://www.sparkfun.com/products/10586
This code reads all the various sensors (wind speed, direction, rain gauge, humidty, pressure, light, batt_lvl)
and reports it over the serial comm port. This can be easily routed to an datalogger (such as OpenLog) or
a wireless transmitter (such as Electric Imp).
Measurements are reported once a second but windspeed and rain gauge are tied to interrupts that are
calcualted at each report.
This example code assumes the GPS module is not used.
*/
//#include <Wire.h> //I2C needed for sensors
//#include "MPL3115A2.h" //Pressure sensor
//#include "HTU21D.h" //Humidity sensor
MPL3115A2 myPressure; //Create an instance of the pressure sensor
HTU21D myHumidity; //Create an instance of the humidity sensor
//Hardware pin definitions
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// digital I/O pins
const byte STAT1 = 6; // Blue User LED
const byte STAT2 = 7;
// analog I/O pins
const byte REFERENCE_3V3 = A3;
const byte LIGHT = A1;
const byte BATT = A2;
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//Global Variables
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
long lastSecond; //The millis counter to see when a second rolls by
byte seconds; //When it hits 60, increase the current minute
byte seconds_2m; //Keeps track of the "wind speed/dir avg" over last 2 minutes array of data
byte minutes; //Keeps track of where we are in various arrays of data
byte minutes_10m; //Keeps track of where we are in wind gust/dir over last 10 minutes array of data
//These are all the weather values that wunderground expects:
float humidity = 0; // [%]
float temp_p = 0; // [pressure sensor temperature C]
float temp_h = 0; // [humidity sensor temperature C]
//float baromin = 30.03;// [barom in] - It's hard to calculate baromin locally, do this in the agent
float pressure = 0;
//float dewptf; // [dewpoint F] - It's hard to calculate dewpoint locally, do this in the agent
float battery = 11.8; //[analog value from 0 to 1023]
float light = 455; //[analog value from 0 to 1023]
//Returns the voltage of the light sensor based on the 3.3V rail
//This allows us to ignore what VCC might be (an Arduino plugged into USB has VCC of 4.5 to 5.2V)
float get_light_level()
{
float operatingVoltage = analogRead(REFERENCE_3V3);
float lightSensor = analogRead(LIGHT);
operatingVoltage = 3.3 / operatingVoltage; //The reference voltage is 3.3V
lightSensor = operatingVoltage * lightSensor;
return(lightSensor);
}
//Returns the voltage of the raw pin based on the 3.3V rail
//This allows us to ignore what VCC might be (an Arduino plugged into USB has VCC of 4.5 to 5.2V)
//Battery level is connected to the RAW pin on Arduino and is fed through two 5% resistors:
//3.9K on the high side (R1), and 1K on the low side (R2)
float get_battery_level()
{
float operatingVoltage = analogRead(REFERENCE_3V3);
float rawVoltage = analogRead(BATT);
operatingVoltage = 3.30 / operatingVoltage; //The reference voltage is 3.3V
rawVoltage = operatingVoltage * rawVoltage; //Convert the 0 to 1023 int to actual voltage on BATT pin
rawVoltage *= 4.90; //(3.9k+1k)/1k - multiple BATT voltage by the voltage divider to get actual system voltage
return(rawVoltage);
}
//Calculates each of the variables that wunderground is expecting
void calcWeather()
{
//Calc humidity
humidity = myHumidity.readHumidity();
//Calc temperature from pressure sensor
temp_p = myPressure.readTemperature();
temp_h = myHumidity.readTemperature();
//tempf = ((tempc * 9.0)/ 5.0 + 32.0); // Convert celsius to fahrenheit
//Calc pressure
pressure = myPressure.readPressure();
//Calc light level
light = get_light_level();
//Calc battery level
battery = get_battery_level();
Serial.println();
Serial.print(",humidity=");
Serial.print(humidity, 1);
Serial.print(",temp_h=");
Serial.print(temp_h, 1);
Serial.print(",temp_p=");
Serial.print(temp_p, 1);
Serial.print(",pressure=");
Serial.print(pressure, 2);
Serial.print(",battery=");
Serial.print(battery, 2);
Serial.print(",light=");
Serial.print(light, 2);
Serial.print(",");
Serial.println("#");
}
/*******************************************************************************************/
// MAIN
/*******************************************************************************************/
void setup()
{
Serial.begin(9600);
while(!Serial.available()); // Wait here until the user presses ENTER in the Serial Terminal
Serial.println("Weather Shield Example");
pinMode(STAT1, OUTPUT); //Status LED Blue
pinMode(STAT2, OUTPUT); //Status LED Green
pinMode(REFERENCE_3V3, INPUT);
pinMode(LIGHT, INPUT);
//Configure the pressure sensor
myPressure.begin(); // Get sensor online
myPressure.setModeBarometer(); // Measure pressure in Pascals from 20 to 110 kPa
myPressure.setOversampleRate(7); // Set Oversample to the recommended 128
myPressure.enableEventFlags(); // Enable all three pressure and temp event flags
//Configure the humidity sensor
myHumidity.begin();
seconds = 0;
lastSecond = millis();
Spark.variable("pressure", &pressure, INT);
Spark.variable("humidity", &humidity, INT);
Spark.variable("light", &light, INT);
Spark.variable("battery", &battery, INT);
Spark.variable("temp_p", &temp_p, INT);
Spark.variable("temp_h", &temp_h, INT);
// Serial.println("Weather Shield online!");
}
void loop()
{
//Keep track of which minute it is
if(millis() - lastSecond >= 1000)
{
digitalWrite(STAT1, HIGH); //Blink stat LED
lastSecond += 1000;
//Report all readings every second
//printWeather();
calcWeather(); //Go calc all the various sensors
digitalWrite(STAT1, LOW); //Turn off stat LED
}
delay(1000);
}

For debugging I used the Serial Communication link over USB. Windows users have to install a COM driver. However, MAC and Linux support the Spark Core USB functionality out of the box.
I was really pleased to see how well the Spark Core supports Arduino libraries and well written legacy code. With only a few code modifications I had the sensors up and running.
The setup is now streaming values over a USB cable to a PC. There I captured the values with Tera Term and created a Weather Graph from the comma separated values (CVS). The example below shows the pressure curve of a Bay Area Storm passing by at end of February 2014.
CA Storm
This setup is a somewhat trivial example that a basic Arduino can also do. The project really does not take advantage of the Spak Core’s connectivity to the internet. So stay tuned for my next blog post where I will add internet connectivity to the setup.

Encounter with a Spark

I have tested several IoT platforms over the last couple of weeks. So I was not too keen to checkout yet another one. However, when I got the annoucement that the Spark Core is shipping I could not resist and ordered one. It arrive in the mail today so I thought I will take it for a spin.
The Spark Core comes in a very stylish little box.
Spark Box
Figure1: Spark Box
To my surprise the box did even includes a breadboard:
spark2spark3
Figure 2: Open Spark Core Box
Overall, the box contains the Spark Core board, a breadboard, a micro-USB cable and Spark sticker.
spark4
Figure 3: Box Content
It is amazingly simple to get the board up and running. By following these few simple steps:

  1. Download the Spark App for iPhone or Android
  2. Setup an account by register at spark.io
  3. Power up the Spark Core over the USB cable
  4. Start Spark App and log into your wireless network

If everything works well you will get rewarded with the RGB-LED on the Spark board flashing in rainbow colors. Once the Spark Core is connected to you WiFi and paired with the Spark cloud, it took me only a few minutes to get an on-board blue LED blinking.
It very quickly becomes obvious that the Spark team has done a great job setting up an entire end-to-end IoT solution consisting of:

  1. Spark Hardware
  2. Cloud based IDE
  3. Arduino compatible API
  4. Free for life cloud back-end service with a RESTful API

All the Spark Core software is open source. The board uses a CC3000 WiFi Module from TI combined with a 32-bit ARM Cortex-M3 powered STM32F103 from ST Microelectronics. The Spark team has come up with a nice integration of this hardware and the cloud server back end. It is based on the CoAP protocol specification and allows for an easy and energy efficient integrated IoT solution.
The cloud API offers over-the-air (OTA) firmware updating where the input can either be c/c++ source code or binaries. For those that don’t want to use Spark Builder, their cloud based IDE the web site also promises support for desktop IDEs like Eclipse.
So much for today, I will cover more details in future blogs.

Do you need WiFi Connectivity in your project?

There are a lot of WiFi solutions for Makers out there. However many are either expensive, big or outdated. So it is refreshing to look at the technical data of the little known WiFi module available by the name of RTX4100  from RTX Telecom. You may never have heard of RTX Telecom but this Danish design service company specialized in wireless has been around for many years.  The module is hardly bigger than a Bluetooth module.
RTX_BT1
Figure1: on the left a simple Bluetooth HC-5 module and on the right the RTX4100 WiFi Module.
The RTX41xx uses latest WiFi System in a Package (SiP) technology. It features a Nordic Semiconductors 32-bit ARM Cortex-M3  based low power microcontroller.  The WiFi is based on a AR41xx SiP from one of the leading WiFi chip manufacturer  Qualcomm – Atheros.
The 32-bit application processor is responsible for all the WiFi driver related duties. But an API allows to program custom application into the module. RTX calls the custom programs Co-Located Application or CoLA.  Besides the RTX4100 that offers 24 kBytes flash memory and 3 kBytes RAM for custom applications. RTX also offers a pin compatible RTX4140 that provides much more programmable memory for CoLA applications, 512 kBytes flash and 64 kBytes RAM.
CoLa
The SDK can be downloaded from RTX’s web site together with a comprehensive set of documents and CoLA examples.
On the Hardware side the module offers 30 solder pins that support a variety of  I/O functionality:

  • ADC ports, DAC ports
  • GPIO ports
  • UART, SPI, I2C
  • Timers

RTX has also teamed up with some cloud services. The currently supported cloud partners are: 2lemetryExositeNabto and Sensinode.
For simple applications like WiFi sensors or actors that require a limited set of IOs and CPU/Memory resources RTX41xx modules can be used stand alone. They are also a great choice for embedded projects based on Arduino that need WiFi.  Similar to some of the popular Bluetooth modules you only need a spare UART or SPI interface to talk to the RTX4100.
Unlike Electric Imp that offers you a fully integrated platform form the module all the way up to the cloud, RTX is a much more open and flexible platform where you retain control. However this control also comes at the price that you have to do more software work. The good news is that you don’t have to start from scratch, RTX supports you with quite a bit of Software.

Another IoT Platform – WICED

Broadcom is also jumping on the IoT wagon with the WICED  platform.  The platform is targeting Bluetooth and WiFi applications. The WiFi modules feature the BCM43362 WiFi chip integrated into a System in a Package (SiP) module. The Image below shows a WiFI WICED PCB module with a Murata WiFi SiP Module and a STM32F205 microcontroller. Murata also offers SiP modules that have the ARM microcontroller built in.
wiced_BCM943362WCD4
On the software side the platform is supported by a feature rich SDK and support for OSs:

  • WICED Application Framework including bootloader, flash storage API, over-the-air (OTA) upgrades, factory reset, and system monitor.
  • An open source build system and toolchain based on GNU make (native IAR support coming soon!).
  • A GUI Development Environment based on Eclipse CDT that seamlessly integrates with a JTAG programmer and single-step, thread-aware debugger based on OpenOCD and gdb.
  • A software stack with a choice of several RTOS/TCP stack options including ThreadX/NetXThreadX/NetX Duo and FreeRTOS/LwIP.
  • Support for security and networking features such as SSL/TLS, IPv4/IPv6 networking, and mDNS (Bonjour) device discovery.
  • Simple out-of-box device setup using Apple-licensed MFi technology or via a web browser and softAP/web server.

Broadcom also make a set of software examples available that help getting started quickly:

  • Production ready sample applications.
  • Lots of application snippets demonstrating how to use the rich WICED API feature set.
  • Various test applications to aid manufacturing and certification.
  • All documentation included inside the WICED SDK.

First Steps with the Electric Imp

At the CES 2014 Intel announced the Edison platform that is intended to enable IoT applications.  As pointed out in my earlier blog, there is a pretty similar solution out there. It goes by the name of Electric Imp.
El_Imp
cloudgraphic
The Electric Imp is a platform that consist of several parts:

  • The SD-Card size Electric Imp
  • A web based IDE
  • A cloud service that integrates with the Electric Imp hardware
  • The BlinkUp cell phone app to pair the Imp Hardware with your local network and Electric Imp web services

Such an impressive combination promised some fun, so I was curious how well all the components would work together.  To try it out I got myself an Electric Imp and the related Imp Arduino Shield from Sparkun. In order to use it with Arduino Shields or Arduino Boards you have to also order the headers and solder them on the board. I use the Arduino Stackable Header Kit . The stackable header allow you to use the Imp as a “WiFi Shield” to an Arduino Single Board Computer (SBC) as well as being a standalone SBC. If you only use you Electric Imp as a SBC you won’t need any stackable headers. Also make sure you order the headers that fits your Arduino board. There are different revisions out there. The R3 version has additional pins and will not fit an original Arduino board.
Once the headers are soldered down you can plug the Electric Imp Shield on top of an Arduino board. Signup for an account on http://electricimp.com and download the BlinkUp app to your smartphone. Fire-up the app and log into your Electric Imp account with your credentials. It’s time to power up the Arduino-Imp combo. Hold your Phone screen flush with the front edge of the Electric Imp card and start the pairing process. The phone screen flashes for a while. Once the flashing stops the Electric Imps status light should turn green. You can get detail instructions on the Electric Imp web site or on Sparkfun. So far not a lot of challenges.
Time has come to whip together some code that brings this combo to life. To test the Electric Imp I used some modify code from their web site.

// create a global variabled called led,
// and assign pin9 to it
led8 <- hardware.pin8;
led9 <- hardware.pin9;
// configure led to be a digital output
led8.configure(DIGITAL_OUT);
led9.configure(DIGITAL_OUT);
// create a global variable to store current
// state of the LED
state <- 0;
function blink() {
  // invert the value of state:
  // when state = 1, 1-1 = 0
  // when state = 0, 1-0 = 1
  state = 1-state;
  // write current state to led pin #8
  led8.write(state);
  // schedule imp to sleep for .5 seconds.
  imp.sleep(0.5);
  // write current state to led pin #9
  led9.write(state);
  // schedule imp to wakeup in .5 seconds and do it again.
  imp.wakeup(0.5, blink);
}
// start the loop
blink();

The code above let’s the two LEDs on the Electric Imp Shield blink alternatively for half a second each. So if you see the LEDs blinking, you know that the IDE properly compiled it and downloaded it over the internet into the Electric Imp card where it gets properly executed. Well Done! Stay tuned, in my next installment I will try to get the Electric Imp talk to the Arduino.

Galileo in the Doghouse?

In one of my previous blogs I compared the Galileo board against the Raspberry Pi  Today we are looking at how the Galileo board compares to the affordable Beaglebone Black board. The Beaglebone is an initiative by Texas Instruments (TI). Unlike the Raspberry PI the Beaglebone board is all open source. Anyway let’s look at the key technical data:

Beaglebone Black Intel Galileo
Target price: US$45 US$69
SoC: Texas Instruments Sitara AM3359 Intel Quark X1000
CPU: 1GHz ARM® Cortex-A8, NEON floating-point accelerator, 2x PRU 32-bit microcontrollers 400MHz 32-bit x86 Pentium Class CPU
GPU: SGX530 3D graphics accelerator none
Memory (DRAM): 512MB DDR3 RAM 256 Mbyte
PCIe ports: none PCIe 2.0
USB 2.0 ports: 1 Host,
1 Device
1 Host,
2 Device
Video input: none
Video outputs: HDMI (rev 1.3 & 1.4) none
Audio outputs: stereo via HDMI none
Onboard storage: 2GB 8-bit eMMC on-board flash storage
SD/MMC/SDIO 3.3V card slot
SD/MMC/SDIO 3.3V card slot
Onboard network: 10/100 Ethernet 10/100 Ethernet
Low-level peripherals: Power 5V, 3.3V , VDD_ADC(1.8V)
3.3V I/O on all signals
McASP0, SPI1, I2C, GPIO(69 max), LCD, GPMC, MMC1, MMC2, 7 AIN(1.8V MAX), 4 Timers, 4 Serial Ports, CAN0, EHRPWM(0,2),XDMA Interrupt, Power button, Expansion Board ID
16 × GPIO,
UART, I²C bus, SPI
Power ratings: 210-460 mA @ 5V 550 mA (1.9-2.2W)
Power source: 5 Volt 5 Volt
Size: 86.36 mm x 53.34 mm (3.4 in x 2.1 in) 106.68 mm x 71.12 mm (4.2 in x 2.8 in)

Both boards offer pretty similar technical data. One of the main difference is the absence of a graphics engine and HDMI interfaces in the Galileo’s X1000 Quark processor. This obviously makes the Galileo less of a choice for graphics application. The Beaglebone also offers a higher CPU clock speed which will give you additional punch. However, if you need a miniPCIe slot Galileo is the way to go.
To close the triangle  I also recommend the detailed comparison  of the Raspberry Pi vs. the Beaglebone published in Make Magazine.

Cortino – Another STM32 based Arduino

While searching for some STM32 related data I cam across the Cortino board by Bugblat.
Cortino
Like my Olimex featured in an earlier blog post this board uses a STM32F103 based 32-bit ARM Cortex-M3 CPU. The Cortino board looks like a nicely done Arduino variant. It even includes a FTDI chip. Although Bugblat does not provide support for the Arduino IDE software their product page offers a good overview of available 3rd Party IDEs.
Like for the Olimex, it should not be too difficult to adopt the Maple IDE from Leaflabs. However the Mable IDE is now getting a bit dated.  A more current Arduino 1.5.5 IDE can be created by using the instructions on Makerlab.me’s web page . Note unless you are fluent in Chinese you have to use Goggle Translate. However the code is documented in English and available from Github. For those that want to learn more about the details of supporting a new board there is a good document available on Arduino.cc  Arduino IDE 1.5 3rd party Hardware specification. Another alternative for Mac users is the OS X native Xcode IDE as offered by embedXcode.

How does Galileo Stack up against Raspberry PI

I am interested is to compare Galileo and other DIY single board computers. In this post I want to analyze the Galileo board against the popular Rasberry PI. The first thing to note is that the Rasberry PI is not an Arduino compatible platform. However, there are now extension boards available that allow to use Arduino shields with Raspberry PI. Here are to examples: The first is the AlaMode for Raspberry Pi  the second is the GertDuino: Add-On Board for Raspberry PI . Both boards are priced in the range of a Rasberry PI. So the combination of a Raspberry PI with an Arduino shield extension puts this solution right where the Galileo board is.
Have a look at the table below. It compares the two single board computer’s hardware. The technical data for the Raspberry PI are taken from WikiPedia.

Rasberry PI Model A Rasberry PI Model B Intel Galileo
Target price: US$ 25 US$ 35 US$69
SoC: Broadcom BCM2835 (CPU, GPU, DSP, SDRAM, and single USB port) Intel Quark X1000
CPU: 700 MHz ARM1176JZF-S CPU 400MHz 32-bit x86 Pentium Class CPU
GPU: Broadcom VideoCore IV @ 250 MHz none
Memory (DRAM): 256 MBytes (shared with GPU) 512 MBytes (shared with GPU) as of 15 October 2012 256 Mbyte
PCIe ports: none PCIe 2.0
USB 2.0 ports: 1 Host 2 Host (from LAN9512) 1 Host,
2 Device
Video input: A CSI input connector allows for the connection of a RPF designed camera module none
Video outputs: Composite RCA (PAL and NTSC), HDMI (rev 1.3 & 1.4), raw LCD Panels via DSI none
Audio outputs: 3.5 mm jack, HDMI, and, as of revision 2 boards, I²S audionull none
Onboard storage: SD/MMC/SDIO 3.3V card slot SD/MMC/SDIO 3.3V card slot
Onboard network: None 10/100 Ethernet (from LAN9512) 10/100 Ethernet
Low-level peripherals: 8 × GPIO, UART, I²C bus, SPI bus with two chip selects, I²S audio +3.3 V, +5 V, ground 16 × GPIO,
UART, I²C bus, SPI
Power ratings: 300 mA (1.5 W) 700 mA (3.5 W) 550 mA (1.9-2.2W)
Power source: 5 volt via MicroUSB or GPIO header 5 Volt
Size: 85.60 mm × 53.98 mm (3.370 in × 2.125 in) 106.68 mm x 71.12 mm (4.2 in x 2.8 in)

The Raspberry PI uses a SoC chip that was originally designed for the set-top box market. Therefore it shows a nice lineup of features on the video side. The integrated HDMI port and on-chip graphics accelerator are testimony to this. However for IoT applications the Quark X1000 SoC used by Galileo offers a really nice selection of interfaces.

Olimexino-STM32

I have played around with the Arduino and had fun with it. However I am accustomed to more powerful micro-controllers than the AVR CPU. So it was only a matter of time until I started to look for an ARM based board that offered Arduino shields.  One board that caught my attention was the Olimexino-STM32 board.
OLIMEXINO-STM32-01
It is a nice board that offers the following key features:

  • STM32F103RB based ARM CPU
  • SD-Card slot
  • Mini USB port
  • CAN bus
  • Wide range of supply 9-30V
  • Battery supply plug

The board is a derivative of the Maple board from LeafLabs and also supports their Arduino like IDE.