Writing code for the emontx

This guide goes through the various steps required to build your emontx sketch from scratch, so that
you can fully understand what's happening and so have full control over your system.
 
To follow this tutorial you will need an emontx, an FTDI cable and the arduino IDE.

Part 1 – Measuring current with a single CT

#include "EmonLib.h"                   // Include Emon Library
EnergyMonitor ct1;                     // Create an instance

void setup()
{  
  Serial.begin(9600);
  
  ct1.currentTX(1, 115.6);             // CT channel, calibration
}

void loop()
{
  double Irms = ct1.calcIrms(1480);    // Calculate RMS current (1480: no. of samples)
  
  Serial.print(Irms*240.0);	       // Print to serial apparent power
  Serial.print(" ");
  Serial.println(Irms);		       // Print to serial Irms
}

For details on the rms current calculations and real power calculation below that occur in EmonLib.h see this page on AC Power Theory - Arduino maths for an overview.

Part 2 – Adding another CT

To add another CT, create a second instance of the EnergyMonitor class, see ct2:

#include "EmonLib.h"                   // Include Emon Library
EnergyMonitor ct1, ct2;                // Create two instances

void setup()
{  
  Serial.begin(9600);
  
  ct1.currentTX(1, 115.6);             // CT channel 1, calibration.
  ct2.currentTX(2, 115.6);             // CT channel 2, calibration.
}

void loop()
{
  double Irms1 = ct1.calcIrms(1480);   // Calculate RMS current 1
  double Irms2 = ct2.calcIrms(1480);   // Calculate RMS current 2
  
  Serial.print(Irms1*240.0);	       // Print apparent power 1   
  Serial.print(' '); 
  Serial.println(Irms2*240.0);	       // Print apparent power 2
}

Part 3 – Measuring real power by adding AC Voltage measurement.

#include "EmonLib.h"                   // Include Emon Library
EnergyMonitor ct1;                     // Create an instance

void setup()
{  
  Serial.begin(9600);
  
  ct1.voltageTX(238.5, 1.7);           // Calibration, phase_shift
  ct1.currentTX(1, 115.6);             // CT channel, calibration.
}

void loop()
{
  ct1.calcVI(20,2000);                 // Calculate all. No.of wavelengths, time-out
  ct1.serialprint();                   // Print out all variables
}

Part 4 – Measuring temperature

By using direct addressing its possible to make sure that as you add temperature sensors the temperature sensor to variable mapping will not change. To find the addresses of your temperature sensors use the: **temperature_search sketch**

#include <OneWire.h>
#include <DallasTemperature.h>
                                                                                           
OneWire oneWire(4);                    // Setup one-wire on digital input pin 4
DallasTemperature sensors(&oneWire);   // Pass the oneWire reference to Dallas Temperature.

DeviceAddress address_T1 = { 0x28, 0x22, 0x70, 0xEE, 0x02, 0x00, 0x00, 0xB8 };

void setup() {
  Serial.begin(9600);
  sensors.begin();
}

void loop()
{ 
  sensors.requestTemperatures();
  
  double temperature = sensors.getTempC(address_T1);  // Get the temperature of the sensor
  Serial.println(temperature);                        // Print temperature
}

Part 5 – Pulse counting

long pulseCount = 0;
unsigned long pulseTime,lastTime; // Used to measure time between pulses
double power;
int ppwh = 1;                     // pulses per watt hour - found or set on the meter.

void setup() 
{
  Serial.begin(9600);
  
  // pulse detection interrupt (emontx pulse channel - IRQ0 D3)
  attachInterrupt(1, onPulse, FALLING);
}

void loop() 
{ 
  Serial.print(power);
  Serial.print(' ');
  Serial.println(pulseCount * ppwh);  // watt hour elapsed
  delay(1000);
}

// The interrupt routine - runs each time a falling edge of a pulse is detected
void onPulse()                  
{
  lastTime = pulseTime;
  pulseTime = micros();
  pulseCount++;                                               // count pulse               
  power = int((3600000000.0 / (pulseTime - lastTime))/ppwh);  // calculate power
}

Part 6 – Transmitting data via the RFM12

Bare bones low power emontx sketch (based on JeeLabs radioBlip sketch)

#include <JeeLib.h>

// this is added as we're using the watchdog for low-power waiting
ISR(WDT_vect) { Sleepy::watchdogEvent(); }

#include "EmonLib.h"
EnergyMonitor ct1;

// create structure - a neat way of packaging data for RF comms
typedef struct { int power; } PayloadTX;                                
PayloadTX emontx;                                                       


void setup() 
{
  ct1.currentTX(1, 115.6);

  // initialize RFM12B (node id, frequency, group)
  rf12_initialize(10, RF12_433MHZ, 210);      
  rf12_sleep(RF12_SLEEP);
}

void loop() 
{ 
  emontx.power = ct1.calcIrms(1480) * 240.0;

  rf12_sleep(RF12_WAKEUP);
  
  int i = 0; while (!rf12_canSend() && i<10) {rf12_recvDone(); i++;}
 
  rf12_sendStart(0, &emontx, sizeof emontx);
  // set the sync mode to 2 if the fuses are still the Arduino default
  // mode 3 (full powerdown) can only be used with 258 CK startup fuses
  rf12_sendWait(2);
  
  rf12_sleep(RF12_SLEEP);
  Sleepy::loseSomeTime(5000);
}

Basic receiver sketch

#include <JeeLib.h> 

typedef struct { int power; } PayloadTX;  
PayloadTX emontx;        

void setup() 
{
  Serial.begin(9600);

  rf12_initialize(10, RF12_433MHZ, 210);  
}

void loop()
{
  
  if (rf12_recvDone()){      
    if (rf12_crc == 0 && (rf12_hdr & RF12_HDR_CTL) == 0)
    {
      int node_id = (rf12_hdr & 0x1F);    // get node id
      
      if (node_id == 10)                  // if emontx node id
      {
        emontx = *(PayloadTX*) rf12_data; // extract payload
        Serial.println(emontx.power);     // Print power value
      }
    }
  }
  
}

Part 7 – Adding a watchdog for extra reliability

The following sketch is a skeleton watchdog only sketch, it does not do much at all, it is only to illustrate the commands needed and their location

If wdt_reset() is not called in more than 8 seconds such as under a fault condition the atmega will reset it self, otherwise if all is well the watchdog timer will continually be reset and will never reach 8 seconds

#include <avr/wdt.h>     // Include watchdog library                                                   

void setup() 
{ 
  wdt_enable(WDTO_8S);   // Enable watchdog: max 8 seconds
}

void loop() 
{ 
  wdt_reset();           // Reset watchdog
}