Tuesday, March 22, 2011

Code for Current Cost CC128 3phase to SD (and LCD) project

// 3 channels of data from CC128 to SD card with a LCD read out
#include <SdFat.h>
#include <Wire.h>
#include "RTClib.h"
#include <NewSoftSerial.h> // supports 57600 baud
#include <LiquidCrystal.h>
#include <string.h> // for strlen();

//#define DEBUG
#ifdef DEBUG
#define DEBUG_PRINT(x) Serial.print(x)
#define DEBUG_PRINTLN(x) Serial.println(x)
#else
#define DEBUG_PRINT(x)
#define DEBUG_PRINTLN(x)
#endif

#define LOG_INTERVAL  1000 // mills between entries
#define ECHO_TO_SERIAL   1 // echo data to serial port
#define WAIT_TO_START    0 // Wait for serial input in setup()
#define SYNC_INTERVAL 10000 // mills between calls to sync()
uint32_t syncTime = 0;     // time of last sync()  

// LED onboard
#define ledPin 13

// the digital pins that connect to the 3 LEDs. set to non existent pins
#define redLEDpin 301
#define yellowLEDpin 302
#define greenLEDpin 303 //was pin 4
                  
// Define the pins used for the software serial port.  Note that we won't
// actually be transmitting anything over the transmit pin.
#define rxPin 3
#define txPin 300 // again a non existing pin, compiler is OK with this...

// Set up the NewSoftserial port
NewSoftSerial currentcost(rxPin, txPin);

char stringData[16];
int content_length;

// what to look for in the XML data stream - add more if you want.. ie IMAs
char startPwr[] = "<ch1><watts>";
char startPwr2[] = "<ch2><watts>";
char startPwr3[] = "<ch3><watts>";
char startTmpr[] = "<tmpr>";
char endChar = '<';

char readChar = 0xFF;

int sizePwr = sizeof(startPwr)-1;
int sizePwr2 = sizeof(startPwr2)-1;
int sizePwr3 = sizeof(startPwr3)-1;
int sizeTmpr = sizeof(startTmpr)-1;

int statePwr = sizePwr;
int statePwr2 = sizePwr2;
int statePwr3 = sizePwr3;
int stateTmpr = sizeTmpr;

int newstate = 0;
int newstate2 = 0;
int newstate3 = 0;

long PwrNum = 0;
long PwrNum2 = 0;
long PwrNum3 = 0;

double TmprDouble = 0;

char Pwr[16] = "";
char Pwr2[16] = "";
char Pwr3[16] = "";
char Tmpr[16] = "";

// Packet buffer, must be big enough to packet and payload
#define BUFFER_SIZE 150
static uint8_t buf[BUFFER_SIZE+1];

// define LCD pins
LiquidCrystal lcd(8, 11, 9, 4, 5, 6, 7);

char ready = 0;

// define the Real Time Clock
RTC_DS1307 RTC;

// The objects that talk to the SD card
Sd2Card card;
SdVolume volume;
SdFile root;
SdFile file;

void error(char *str)
{
  Serial.print("error: ");
  Serial.println(str);
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("error");
  lcd.setCursor(0, 1);
  lcd.print(str);
  while(1); //this prints out "error" to the Serial and LCD, then sits in a while(1); loop forever, ie a halt
}

void setup()  {

  // Display welcome message on LCD screen.
  lcd.begin(16, 2);
  lcd.print("CC128 3CH SD LCD");

  // Define pin modes for tx and rx pins:
  pinMode(rxPin, INPUT);
  pinMode(txPin, OUTPUT);

  // Set the data rate for the NewSoftSerial port.
  currentcost.begin(57600);

  // Set the data rate for the hardware serial port
  Serial.begin(115200);

  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, LOW);

  Serial.println(); // drops down 1 line on serial port

#if WAIT_TO_START
  Serial.println("Type any character to start");
  while (!Serial.available());
#endif  //If we set WAIT_TO_START to anything but 0, the Arduino will wait until the user types something in.

  // initialize the SD card
  if (!card.init()) error("card.init");

  // initialize a FAT volume
  if (!volume.init(card)) error("volume.init");

  // open root directory
  if (!root.openRoot(volume)) error("openRoot");

  // create a new file
  char name[] = "LOGGER00.CSV";
  for (uint8_t i = 0; i < 100; i++) {
    name[6] = i/10 + '0';
    name[7] = i%10 + '0';
    if (file.open(root, name, O_CREAT | O_EXCL | O_WRITE)) break;
  }
  if (!file.isOpen()) error ("file.create");
  Serial.print("Logging to: ");
  Serial.println(name);

  // write header
  file.writeError = 0; //

  Wire.begin();
  if (!RTC.begin()) {
    file.println("RTC failed");
#if ECHO_TO_SERIAL
    Serial.println("RTC failed");
#endif  //ECHO_TO_SERIAL
   }

  file.println("millis,stamp,datetime,CH1,CH2,CH3");    // print headers once to file
#if ECHO_TO_SERIAL
  Serial.println("millis,stamp,datetime,CH1,CH2,CH3"); // echo headers to serial
#endif //ECHO_TO_SERIAL

  // attempt to write out the header to the file
  if (file.writeError || !file.sync()) {
    error("write header");
  }

  pinMode(redLEDpin, OUTPUT); // pin 6
  pinMode(greenLEDpin, OUTPUT); // pin 7 was pin 4
  pinMode(yellowLEDpin, OUTPUT); // pin 5
 }
void loop(void) {

  readChar = currentcost.read(); // get data from cc128

  if (readChar > 31) {
    stateTmpr = parseDataTmpr(stateTmpr, readChar);
    if (stateTmpr < 0) {
      Tmpr[abs(stateTmpr)-1] = readChar;
    }
    statePwr = parseDataPwr(statePwr, readChar);
    if (statePwr < 0) {
      Pwr[abs(statePwr)-1] = readChar;
    }
    statePwr2 = parseDataPwr2(statePwr2, readChar);
    if (statePwr2 < 0) {
      Pwr2[abs(statePwr2)-1] = readChar;
    }
    statePwr3 = parseDataPwr3(statePwr3, readChar);
    if (statePwr3 < 0) {
      Pwr3[abs(statePwr3)-1] = readChar;
    }
  }
  else if (readChar == 13) {
  
    PwrNum = atol(Pwr);
    PwrNum2 = atol(Pwr2);
    PwrNum3 = atol(Pwr3);
  
    TmprDouble = strtod(Tmpr,NULL);

  DateTime now;

  // clear print error
  file.writeError = 0;

  // delay for the amount of time we want between readings
  delay((LOG_INTERVAL -1) - (millis() % LOG_INTERVAL));

  digitalWrite(redLEDpin, HIGH);

  // log milliseconds since starting
  uint32_t m = millis();
  file.print(m);           // milliseconds since start
  file.print(", ");  
#if ECHO_TO_SERIAL
  Serial.print(m);         // milliseconds since start
  Serial.print(", ");
#endif
  now = RTC.now();
  // log time
  file.print(now.unixtime()); // seconds since 1/1/1970
  file.print(", ");
  file.print('"');
  file.print(now.year(), DEC);
  file.print("/");
  file.print(now.month(), DEC);
  file.print("/");
  file.print(now.day(), DEC);
  file.print(" ");
  file.print(now.hour(), DEC);
  file.print(":");
  file.print(now.minute(), DEC);
  file.print(":");
  file.print(now.second(), DEC);
  file.print('"');
  file.print(", ");
  // log data
  file.print(PwrNum);
  file.print(", ");  
  file.print(PwrNum2);
  file.print(", ");  
  file.println(PwrNum3);
#if ECHO_TO_SERIAL
  // send time serial
  Serial.print(now.unixtime()); // seconds since 1/1/1970
  Serial.print(", ");
  Serial.print('"');
  Serial.print(now.year(), DEC);
  Serial.print("/");
  Serial.print(now.month(), DEC);
  Serial.print("/");
  Serial.print(now.day(), DEC);
  Serial.print(" ");
  Serial.print(now.hour(), DEC);
  Serial.print(":");
  Serial.print(now.minute(), DEC);
  Serial.print(":");
  Serial.print(now.second(), DEC);
  Serial.print('"'); // aded ln to test
  Serial.print(", ");
  //send data to serial
  Serial.print(PwrNum);
  Serial.print(", ");  
  Serial.print(PwrNum2);
  Serial.print(", ");  
  Serial.println(PwrNum3);
  Serial.flush();
  #endif //ECHO_TO_SERIAL

      // Write to the LCD screen.
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Ch1");
    lcd.setCursor(6, 0);
    lcd.print("Ch2");
    lcd.setCursor(12, 0);
    lcd.print("Ch3");
    lcd.setCursor(0, 1);
    lcd.print(PwrNum);
    lcd.print("W");
    lcd.setCursor(6, 1);
    lcd.print(PwrNum2);
    lcd.print("W");
    lcd.setCursor(12, 1);
    lcd.print(PwrNum3);
    lcd.print("W");
    // if you have larger display than 16x2 you can include more info, ie temp
    //lcd.print("Temp: ");
    //lcd.print(TmprDouble);
    //lcd.print("C");

                   //Sync data to SD card
                     if (file.writeError) error("write data");
                   digitalWrite(redLEDpin, LOW);

                   //don't sync too often - requires 2048 bytes of I/O to SD card
                   if ((millis() - syncTime) <  SYNC_INTERVAL) return;
                    syncTime = millis();

                   // blink LED to show we are syncing data to the card & updating FAT!
                   digitalWrite(greenLEDpin, HIGH);
                   if (!file.sync()) error("sync");
                   digitalWrite(greenLEDpin, LOW);

    //digitalWrite(ledPin, LOW); //Flashes the on board LED

    // Indicate that data is ready to be sent.
    ready = 1;
  }

  if (ready == 1) {
    ready = 0;

    digitalWrite(ledPin, HIGH); //Flashes the on board LED

    sprintf(stringData, " %lu, %lf", PwrNum, TmprDouble);
    content_length = strlen(stringData);

   // Serial.flush();
    //DEBUG_PRINTLN(stringData);
   // Serial.flush();

}
}

 //TEMP
int parseDataTmpr(int oldstate, char chr) {
  newstate = oldstate;
  if (newstate > 0) {
    if (chr == startTmpr[sizeTmpr-newstate]) {
      newstate--;
    }
    else {
      newstate = sizeTmpr;
    }
  }
  else if (newstate <= 0) {
    newstate--;
    if (chr == endChar) {
      newstate = sizeTmpr;
    }
  }
  return newstate;
}
 //CH1
int parseDataPwr(int oldstate, char chr) {
  newstate = oldstate;
  if (newstate > 0) {
    if (chr == startPwr[sizePwr-newstate]) {
      newstate--;
    }
    else {
      newstate = sizePwr;
    }
  }
  else if (newstate <= 0) {
    newstate--;
    if (chr == endChar) {
      newstate = sizePwr;
    }
  }
  return newstate;
}

//CH2
int parseDataPwr2(int oldstate2, char chr) {
  newstate2 = oldstate2;
  if (newstate2 > 0) {
    if (chr == startPwr2[sizePwr2-newstate2]) {
      newstate2--;
    }
    else {
      newstate2 = sizePwr2;
    }
  }
  else if (newstate2 <= 0) {
    newstate2--;
    if (chr == endChar) {
      newstate2 = sizePwr2;
    }
  }
  return newstate2;
}
//CH3
int parseDataPwr3(int oldstate3, char chr) {
  newstate3 = oldstate3;
  if (newstate3 > 0) {
    if (chr == startPwr3[sizePwr3-newstate3]) {
      newstate3--;
    }
    else {
      newstate3 = sizePwr3;
    }
  }
  else if (newstate3 <= 0) {
    newstate3--;
    if (chr == endChar) {
      newstate3 = sizePwr3;
    }
  }
  return newstate3;
}

Sunday, March 20, 2011

A project to log 3 phase load to an SD card using a Current Cost meter

Hello
Ive just complete a project to log 3 phase current loads to an SD card, using a Current Cost meter and an arduino mini pro. I initially looked at posting the XML data to pachube but soon realized that all the fine detail would be lost with the low update rate. I decided to store the data on an SD card inside the current cost meter. I now have 6 sec data resolution -  low cost - 3phase data logger - and with the addition of an optional LCD showing the real time load on each phase.





Ive posted the code on the next entry.

The logger consist of a current cost meter with 3 clamps, mini pro arduino board,
an SD card, a DS1307 real time clock card, and an optional 16x2 LCD module.  The small size of the parts means it can all fit inside the existing current cost meter case (apart from the 16x2 LCD), and if you choice to go without the LCD it can all be powered from the existing Current Cost power supply*.

In the processes Ive learned how to do some basic C programming, and experimented with various arduino boards.... all good fun.

*see note on DS1307 low voltage shut down





data from Current Cost via RJ45 is the "brown" wire, ground in on the "blue" wire, the orange wire at the bottom is heading to the Arduino pin 3.