I received a RaspberrypPi B+ as a Christmas gift last year. My plan was to use this low powered server to host Emoncms.
The data filing into Emoncms was done a node script which queried Arduino periodically over I2C channel and logged the data into Emoncms. The address 04 was identified by running i2cdetect. My script expected 2 data pieces to be send up as 4 bytes each with a max of 12 bytes (4 set aside for future use).
Setup
I started with a 4GB SD card and installed a new image of Rasbian Wheezy which was later upgraded to kernel 4.1. This was followed by installation of Emoncms and nodejs. After all this I had a blank Emoncms site running on my Raspberry.
Communication
Although the Raspberry has plenty of pins, they are all digital. The power measurement needs analog pins for ADC operation. Usually, this feat is accomplished by a secondary board which in my case would be my old Arduino Mini.
Raspberry can talk to Arduino over Serial or I2C ports. Most people use Serial but I thought that it might not be reliable, plus it limits communication to single device. I2C allows multiple slave devices and so I proceeded with I2C communication. It uses same number of ports (3) for connection.
Node provides I2C communication through a package conveniently named i2c which was installed globally:
Node provides I2C communication through a package conveniently named i2c which was installed globally:
npm -g i2c
The data filing into Emoncms was done a node script which queried Arduino periodically over I2C channel and logged the data into Emoncms. The address 04 was identified by running i2cdetect. My script expected 2 data pieces to be send up as 4 bytes each with a max of 12 bytes (4 set aside for future use).
root@raspberrypi:~/dev# i2cdetect -y 1
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- 04 -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
Node Script
const fs = require('fs');
const i2c = require("i2c");
const wire = new i2c(0x04, { device: '/dev/i2c-1', debug: false });
const request = require('request');
setInterval(getI2CData, INTERVAL);
function getI2CData() {
//Just issue a read request for 12 bytes
wire.read(12, function (err, data) {
if (err) logErr(err);
else parseData(data);
});
}
//power1[4 bytes], power2[4 bytes]
function parseData(data) {
for (var i = 0; i < data.length; i++) { //Remove bad data
if (isNaN(parseInt(data[i]))) {
data[i] = 0;
}
}
//data needs to be parsed in this order
var obj = {
power1: bytesToLong(data, 0),
power2: bytesToLong(data, 4)
};
var json = JSON.stringify(obj);
var url = URL + json; //File data
request(url, function (err, response, body) {
if (err) {
logErr(err);
}
});
}
function bytesToLong(arr, index) {
var num = parseInt(arr[index++]);
num |= parseInt(arr[index++]) << 8;
num |= parseInt(arr[index++]) << 16;
num |= parseInt(arr[index++]) << 24;
return num;
}
function bytesToInt(arr, index) {
var num = parseInt(arr[index++]);
num |= parseInt(arr[index++]) << 8;
return num;
}
Arduino Sketch
Arduino basically just listens to I2C read request and sends out data. My first attempt was to gather data in the read request but the I2C response needed to be sent out promptly in the next frame. The power calculation delay caused the data sequence to be disrupted and nothing was received at Raspberry end. It seems the slave can obtain some grace time but I took a different approach to solve this issue. I made Arduino continuously gather data every 2.5 seconds and just write back the last data reading.
//Outputs power1[4 bytes], power2[4 bytes] over I2C
//I2C address is 0x04
//http://blog.retep.org/2014/02/15/connecting-an-arduino-to-a-raspberry-pi-using-i2c/
//GPIO2 (SDA) <-> Pin4 (SDA)
//GPIO3 (SCL) <-> Pin5 (SCL)
#include <wire.h>
#include <emonlib.h>
//http://openenergymonitor.org/emon/buildingblocks/calibration
//New calibration = (correct reading / emonTx reading) * current calibration
#define VOLTAGE 121.5
#define CURRENT_CALIBRATION (100/0.05/21.7)
#define NUMBER_OF_SAMPLES 1000 //200ms at 60Hz, 1172 samples was giving 205ms
#define CURRENT_PIN1 A0 //Analog A0
#define CURRENT_PIN2 A1 //Analog A1
#define OUTPUT_LED 2
#define OUTPUT_LED_BLINK_DELAY 250
#define SLAVE_ADDRESS 0x04
#define CAPTURE_INTERVAL 2500
EnergyMonitor emon1, emon2;
long power1 = 0;
long power2 = 0;
byte bytes[14]; //Bytes array for sending out through I2C
int bytePos; //Index position in bytes
uint32_t nextTime = 0;
void setup()
{
delay(2000); //Delay for allowing serial monitor to be launched
Serial.begin(19200);
pinMode(OUTPUT_LED, OUTPUT);
digitalWrite(OUTPUT_LED, LOW);
emon1.current(CURRENT_PIN1, CURRENT_CALIBRATION);
emon2.current(CURRENT_PIN2, CURRENT_CALIBRATION);
Wire.begin(SLAVE_ADDRESS);
Wire.onRequest(sendData);
Wire.onReceive(receiveData);
}
void loop() {
uint32_t currentTime = millis();
if (currentTime > nextTime) {
double Irms1 = emon1.calcIrms(NUMBER_OF_SAMPLES);
double Irms2 = emon2.calcIrms(NUMBER_OF_SAMPLES);
power1 = (long) (Irms1 * VOLTAGE);
power2 = (long) (Irms2 * VOLTAGE);
Serial.print(power1);
Serial.print(',');
Serial.println(power2);
blink();
nextTime = currentTime + CAPTURE_INTERVAL;
}
}
void receiveData(int byteCount) {
while (Wire.available()) { //This does nothing .. future use
int number = Wire.read();
}
}
void putLongData(long num)
{
bytes[bytePos++] = (num & 0xFF);
bytes[bytePos++] = ((num >> 8) & 0xFF);
bytes[bytePos++] = ((num >> 16) & 0xFF);
bytes[bytePos++] = ((num >> 24) & 0xFF);
}
void putIntData(long num)
{
bytes[bytePos++] = (num & 0xFF);
bytes[bytePos++] = ((num >> 8) & 0xFF);
}
void sendData() {
//gatherData(); //Unable to gather data and return data in time within I2C callback
bytePos = 0;
putLongData(power1); //4 bytes
putLongData(power2); //4 bytes
putIntData(distance); //2 bytes
Wire.write(bytes, 10);
}
Later I started logging local temperature as well; this was done at Nodejs end. The temperature itself was obtained from openweathermap.org. So now I could see power consumption against temperature.
const OPENWEATHERURL = 'http://api.openweathermap.org/data/2.5/' +
'weather?zip=12345,us&&appid=____&mode=json&units=imperial';
setInterval(getOutsideTemp, INTERVAL_WEATHER);
function getOutsideTemp() {
request(OPENWEATHERURL, function (err, response, body) {
if (err) {
logErr(err);
}
else {
try {
var data = JSON.parse(body);
if (data && data.main) {
outsideTemp = data.main.temp;
}
}
catch (ex) { }
}
});
}
![]() |
Power |
![]() |
Power against outside temperature |
I was a little concerned about the reliability of this setup since there were many moving parts. I added logging by rewiring output and error streams to log files. I am happy that the system has been functioning for last 2 months, the only errors logged were due to failed resolution of openweather.org
const Console = console.Console;
const output = fs.createWriteStream('./stdout.log');
const errorOutput = fs.createWriteStream('./stderr.log');
const logger = new Console(output, errorOutput); //process.output
function log(text) {
logger.log((new Date()).toLocaleString() + " " + text);
}
function logErr(text) {
logger.error((new Date()).toLocaleString() + " " + text);
}
The final piece to this equation was to configure the logging to start automatically. I created an init script using the sample provided at init-script-template and adjusted it to start my node script.
dir="/root/dev"
cmd="node EnergyMeter.js"
user="root"
The script was made executable and registered as an init script.
sudo chmod +x /etc/init.d/energymeter
sudo update-rc.d energymeter defaults
That was it; the script would start data logging on system boot.