Energy monitor -- code quality

I'm looking for a little feed back on this sketch. Basically I'm using an IR photo-transistor to listen for the test pulses my electric smart meter sends out every 2 W hr. Time between pulses is sent out the serial interface to a Visual Basic program, that passes the data onto RRDtool. Since the Arduino wasn't very busy I added on a RTC, SDcard, and some DS18B20's.

Overall everything works Ok. I do have some memory problems. The queuearray library that I'm using to buffer the pulse timing, was hogging memory anytime it had to resize it's array. I've fixed it by increasing the default size in the library.

The RTC and DS18B20 libraries annoyed me. It seems they all had some kind of compromise. I really wanted the TIME library for it's unix timestamp function, but I couldn't get it to set the clock properly. Kept coming up several hours off. The ds1307new library is okay, but it looks like it is doing alot of extra work than I really need.

I still need to write function to set RTC time. Need some kind of rolling log file on the sdcard, takes a really long time to dump at 115200 bps. Need to be able to dump data from a given time period. My goal is for the Arduino to be self-sufficient. Dump data for processing when asked. Right now I have to keep my desktop running, which goes against the whole motivation of the project--saving energy. Need to figure out a clean way of dropping the first 2 IR samples. The first sample is always inaccurate.

Looking for tips and pointers. Not really a C++ guy. Any different strategies or techniques I should consider. I haven't had this Arduino less than a month. Really amazed at it's flexibility, and how easy it is. Thanx.

//1Wire
#include <OneWire.h>
#include <DallasTemperature.h>

// Data wire is plugged into port 3 on the Arduino
#define ONE_WIRE_BUS 3
#define TEMPERATURE_PRECISION 9

// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);

// Pass our oneWire reference to Dallas Temperature. 
DallasTemperature sensors(&oneWire);

// arrays to hold device addresses
DeviceAddress waterHeater={0x28,0x4E,0x57,0x3F,0x03,0x0,0x0,0x8A};
DeviceAddress room={0x28,0x95,0x5E,0x3F,0x03,0x00,0x00,0x45};

boolean oneWirePresent=false;

//Real Time Clock
#include <Wire.h>
#include <DS1307new.h>
boolean rtcPresent=false;

//SD Card
#include <SD.h>
const int chipSelect=10;
boolean sdCardPresent=false;

//FIFO Queue 
#include <QueueArray.h>
QueueArray <unsigned int> queue;

boolean serialOut=true;

const int irSensor=2;

void setup(){
  //Initialize serial port at 9600 baud
  Serial.begin(115200);

  //Init SD
  pinMode(chipSelect,OUTPUT);
  if(SD.begin(chipSelect)){
    sdCardPresent=true;
  }
  
  //Init RTC -- Set SQW pin to 1 Hz to flash LED
  if(RTC.isPresent()){
    RTC.ctrl=0x10;
    RTC.setCTRL();
    rtcPresent=true;
  }

  //Init 1Wire
  sensors.begin();

  //Make sure there are 2 sensors
  if(sensors.getDeviceCount()==2){
    oneWirePresent=true;
    sensors.setResolution(waterHeater,TEMPERATURE_PRECISION);
    sensors.setResolution(room,TEMPERATURE_PRECISION);
    sensors.setWaitForConversion(true);
  }

  //Enable external interrupt of digital I/O pin 2
  pinMode(irSensor,INPUT);
  //Turn on internal pull-up
  digitalWrite(irSensor,HIGH);
  attachInterrupt(0,timePulse,FALLING);
}

void timePulse(){
  //Push current millis to queue
  queue.push(millis());
}

void loop() {
  if(queue.count()>1){
    //Calculate instant power.
    unsigned int oldTime=queue.pop();
    unsigned int newTime=queue.peek();
    float currentW=(float)7200000/(newTime-oldTime);

    //Send out serial with 1 digit 00.0
    if(serialOut){
      Serial.print("W:");
      Serial.println(currentW,1);
    }
    
    // If the SD card is available, log the current wattage
    if(sdCardPresent){
      File dataFile=SD.open("watt.txt",FILE_WRITE);
      if(dataFile){
        if(rtcPresent){
          RTC.getTime();
          dataFile.print(RTC.time2000);
          dataFile.print(":");
        }
        dataFile.println(currentW);
        dataFile.close();
      }
    }
  }
}

//Handle incoming serial
void serialEvent(){
  while (Serial.available()){
    //Incoming commands begin with !
    if(Serial.read()=='!'){
      //Delay, otherwise missing the next character, why!?!?
      delay(1);
      switch(Serial.read()){
        case 's':
          serialOut = false;
          break;
        case 'S':
          serialOut = true;
          break;
        case 'd':{
          //Dump log file
          File dataFile=SD.open("watt.txt",FILE_READ);
          if(dataFile){
            while(dataFile.available()){
              Serial.write(dataFile.read());
            }
            dataFile.close();
          }
          break;
          }
        case 't':
          //Print current time
          RTC.getTime();
          Serial.println(RTC.time2000);
          Serial.print(RTC.month);
          Serial.print("-");
          Serial.print(RTC.day);
          Serial.print("-");
          Serial.print(RTC.year);
          Serial.print(" ");
          Serial.print(RTC.hour);
          Serial.print(":");
          Serial.print(RTC.minute);
          Serial.print(":");
          Serial.println(RTC.second);
          break;
        case 'T':
          //Set RTC time
          break;
        case 'w':
          if(oneWirePresent){
            sensors.requestTemperatures();
            float tempC=sensors.getTempC(waterHeater);
            Serial.print("WH:");
            Serial.println(DallasTemperature::toFahrenheit(tempC));
          }
          break;
        case 'r':
          if(oneWirePresent){
            sensors.requestTemperatures(); //request by addr not working for some reason
            float tempC=sensors.getTempC(room);
            Serial.print("R:");
            Serial.println(DallasTemperature::toFahrenheit(tempC));
          }
          break;
      }
    }
  }
}

Moderator edit: added tags

Generated graphs are at http://kmitchel.dyndns.org/rrdtool

Kind of a jumbled mess. I want to get dynamically generated graphs going. Long term graphs are a problem. I can't seem to get clean usable graphs, too messy to tell what is going on. Really easy to tell when the refrigerator cycles on and off, A/C and water heater. Plasma TV adds alot of jitter; it's power consumption is relative to the content being displayed.

Water heater sensor needs bonded to the tank better. It's just shoved between the tank and insulation through a service access. Screwed up only ordering 2 ds18b20's. Really want a sensor on each heating element. Can think of another half dozen places I want them.

Hacking the water meter on my water softner is next on the list. Hoping I can do it without an optocoupler.

What I do is use a plug/small nas computer that polls my ardunio every 5 min. That way the ardunio doesn't need to know the time. The ardunio just keeps a count of the number of pulses and passes it on and then resets the count. I also do an internal 1 min count for each of the 5 minutes and pass that along too. The small plug computer save the data to a txt file and also to pachube

I've been wanting a NAS. Just not in the toy budget right now. Looking into finding an old wireless access point running dd-wrt for now. Need something decent enough to run Apache/PHP, which an okay NAS should be able to handle. We use 3 Netbooks, so the desktop is virtually useless. Want a NAS to fill it's role for a fraction of the power. Not something that is going to happen immediately. That is were the sdcard/RTC comes in. Both were stupid simple to implement. I used a micro sd card adapter as a socket. Wired it up with the resistor method.

Hi,

there are a few places where you test whether devices are present which is good, but then you don't do anything useful with the error if you find it. For example at the end of set up you should set some indicator LEDs or send a message to serial if your xxxxpresent variables are false or your device count is less than two.

Duane B

Rcarduino.blogspot.com

//Delay, otherwise missing the next character, why!?!?

You have the classic issue of reading the serial port when you don't know that there is anything there. serial.Available told you that there was something to read - the exclamation point, but at that point the character following it has not arrived. The arduino is fast enough that even at 115200 baud, reading again immediately will get -1 (no data). Your delay masks this problem, but it's better to ensure that there's something there before you read.

Basically I'm using an IR photo-transistor to listen for the test pulses my electric smart meter sends

Maybe you need a microphone to see it, instead.

wildbill:

//Delay, otherwise missing the next character, why!?!?

You have the classic issue of reading the serial port when you don't know that there is anything there. serial.Available told you that there was something to read - the exclamation point, but at that point the character following it has not arrived. The arduino is fast enough that even at 115200 baud, reading again immediately will get -1 (no data). Your delay masks this problem, but it's better to ensure that there's something there before you read.

Makes perfect sense, now. Will rewrite.

PaulS:

Basically I'm using an IR photo-transistor to listen for the test pulses my electric smart meter sends

Maybe you need a microphone to see it, instead.

:slight_smile:

More like a megaphone to tell my wife to turn stuff off.

PaulS:

Basically I'm using an IR photo-transistor to listen for the test pulses my electric smart meter sends

Maybe you need a microphone to see it, instead.

:slight_smile:

More like a megaphone to tell my wife to turn stuff off.