Tuesday, February 15, 2011

Power Monitoring Using Arduino









Since everyone and their brother that knows how to use an Arduino seems to have done a project to monitor their household power consumption, I figured that I would do one too.  


My situation is slightly different than some, as a photovoltaic solar installation from a previous owner had already provided current taps and a LCD rate meter near the power inverters.  That interface provided pulse frequency modulated signals for both power generated and consumed.  I built a little circuit to buffer and level shift the differential signals and pump them into the Arduino interrupt lines.


As for the software stack, I did a not too dissimilar implementation from what others seem to have done, with the following design choices

1) I used the ethernet shield instead of WiFi for reliability and because I'd like to migrate the solution to be powered over POE over time

2) I used the recently contributed time utility library to have the Arduino connect to an NTP server to set its clock correctly and keep itself in synch with the rest of the world.

3) I collect data once per second, and then do a rolling archive of the last x seconds, the last y minutes and the last z hours of data in the Arduino RAM in case something happens to the client pulling the data.  I then implemented a function to dump both historical and real time data to the client via HTTP.  Different HTTP query strings will return different types (hours, minutes, seconds) of samples.

4) I implemented delta pulse code modulation to compress the storage requirement in RAM on the Arduino.  I needed more than 8 bits of resolution, but really only wanted to use 8 bits per sample.  The DPCM algorithm implemented works very nicely for low frequency signals.  Basically you get to within 64 Watts of accuracy in 1 second, and to within 1 Watt by the second second.

5) Like others that have done similar projects, I have a client program that polls the Arduino and logs the data in a database as well as doing graphing. Since I had a goal of learning Cocoa, as well, I wrote that client for the Mac, using an SQLLite Data Store under a CoreData framework, and implemented historical and realtime graphs as well.

For those who are interested, the Arduino source code is below.  By defining "DPCM", delta pulse code modulation is enabled, which approximately doubles the amount of history that can be stored on the Arduino's onboard RAM.  Sorry that it's a little ugly.


The Mac OS/X source code is even more disheveled.  It may be posted once it embarrasses me a little less.







#include <UdpBytewise.h>
#include <Time.h>


//*******************************
#include <WString.h>
#include <SPI.h>
#include <Ethernet.h>


#define SCALEFACTORA 1280000
#define SCALEFACTORB 640000
#define RAMSIZE 1024  //must be 960 or larger
#define COUNTSIZE (RAMSIZE/8)  //use half the ram, for 2 arrays of 2 byte ints
#define SECONDSIZE 64//(COUNTSIZE/2)
#define MINUTESIZE 64
#define HOURSIZE 32//((COUNTSIZE/2)-MINUTESIZE) 
#define MAXCMDLEN 16
#define DEBOUNCEA (SCALEFACTORA/10000)  // assume max power consumption of 10K watts
#define DEBOUNCEB (SCALEFACTORB/5000)   // assume max power generation of 5K watts
#define HomePin  2
#define SolarPin  3
#define ledPin  4 // LED pin
#define MIN(a,b)  ((a<b) ? a:b)


byte mac[] = { 
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; //physical mac address
byte ip[] = { 
  192, 168, 168, 110 }; // ip in lan
byte gateway[] = { 
  192, 168, 168, 168 }; // internet access via router
byte subnet[] = { 
  255, 255, 255, 0 }; //subnet mask
byte SNTP_server_IP[]    = { 
  192, 43, 244, 18}; // time.nist.gov
//byte SNTP_server_IP[] = { 130,149,17,21};    // ntps1-0.cs.tu-berlin.de
//byte SNTP_server_IP[] = { 192,53,103,108};   // ptbtime1.ptb.de
const  long timeZoneOffset = 0L; // set this to the offset in seconds to your local time;


Server server(80); //server port


String readString = String(MAXCMDLEN); //string for fetching data from address
boolean LEDON = false; //LED status flag
volatile long channelAval = 0;
volatile long channelBval = 0;
volatile unsigned long lastmillisA =0;
volatile unsigned long lastmillisB =0;
unsigned long  myMillis = 0;
unsigned long lastMyMillis=0;
unsigned long safeAval;
unsigned long safeBval;
#define USEBYTES 1
#define DPCM 1


#ifdef USEBYTES
unsigned char aSecArray[SECONDSIZE];
unsigned char bSecArray[SECONDSIZE];
unsigned char aMinArray[MINUTESIZE];
unsigned char bMinArray[MINUTESIZE];
unsigned char aHourArray[HOURSIZE];
unsigned char bHourArray[HOURSIZE];
#if DPCM
#define PRINTSCALE 1
unsigned int startSecsBaseA=0;
unsigned int startSecsBaseB=0;
unsigned int startMinsBaseA=0;
unsigned int startMinsBaseB=0;
unsigned int startHoursBaseA=0;
unsigned int startHoursBaseB=0;
#else
#define PRINTSCALE 32
#endif
#else
unsigned int aSecArray[SECONDSIZE];
unsigned int bSecArray[SECONDSIZE];
unsigned int aMinArray[MINUTESIZE];
unsigned int bMinArray[MINUTESIZE];
unsigned int aHourArray[HOURSIZE];
unsigned int bHourArray[HOURSIZE];


#define PRINTSCALE 1
#endif
unsigned int writecursor = 0;
unsigned int readcursor =0;
unsigned int secondCount=0;
unsigned int minuteCount=0;
unsigned int hourCount=0;
unsigned int secondSamples=0;
unsigned int minuteSamples=0;
unsigned int hourSamples=0;
time_t curSampleTime = 0;
boolean closeConnection=false;
volatile int  ic=0;


void myInterruptHandlerA()
{
  unsigned long nowsnap = millis();
  ic++;


  if (lastmillisA==0) {
    lastmillisA=nowsnap;
    channelAval=0;
    ic++;
    return;
  }
  if ((nowsnap-lastmillisA)>DEBOUNCEA)
  {
    channelAval= nowsnap - lastmillisA;
    lastmillisA=nowsnap;
    ic++;
  }
}


void myInterruptHandlerB()
{
  unsigned long nowsnap = millis();
  if (lastmillisB==0) {
    lastmillisB=nowsnap;
    channelBval=0;
    ic++;
    return;
  }
  if ((nowsnap-lastmillisB)>DEBOUNCEB)
  {
    channelBval= nowsnap - lastmillisB;
    lastmillisB=nowsnap;
    ic++;
  }


}




//coding is as follows
// bit 7: 0 if absolute sample, or 1 if a delta
// bit 0-6: Two's complement of the delta if delta coded (-64 to +63)
// bit 0-6: value of sample divided by 64


unsigned char encodeSample( int base,unsigned int sample)
{
  int delta = sample-base;
  byte retval;
  if (base == -1) 
  {
    retval = (delta) | 0b10000000;
    return((unsigned char) retval);
  }    
  if ((delta > 63) || (delta < -64))
  {
    retval = (sample >> 6) & 0b01111111;
  }
  else 
  {
    retval = (delta) | 0b10000000;
  }
  //  Serial.print("Encoded ");
  //  Serial.print(base);
  //  Serial.print(" and ");
  //  Serial.print(sample); 
  //  Serial.print(" and got ");
  //  Serial.print((int) retval);
  //  Serial.print(" which decodes as ");
  //  Serial.print(decodeSample(base,retval));
  //  Serial.print("\n"); 
  return((unsigned char) retval);



unsigned int decodeSample(unsigned int base, unsigned char code)
{
  unsigned int retval;
  if (code & 0b10000000)   //dealing with a delta
  {
    retval = base + (char) ((code & 0b01111111) | ((code & 0b01000000) << 1));  //extends the sign byte
  }
  else
  {
    retval = code << 6;
  }
  return(retval);
}






void cleanSeconds()
{
  int i;
  unsigned long aAcc= 0;
  unsigned long bAcc =0;
  unsigned long aTot=0;
  unsigned long bTot=0;
  unsigned int xVal=0;
  unsigned int yVal=0;
  unsigned int zVal=0;
  unsigned long aLow,bLow,aHigh,bHigh;
  int j;




#if DPCM  
  aAcc = startSecsBaseA;
  bAcc = startSecsBaseB;
#endif  
  for (i=SECONDSIZE-1;i>=0;i--)
  {
#if DPCM
    aAcc = decodeSample(aAcc,aSecArray[i]);
    bAcc = decodeSample(bAcc,bSecArray[i]);
    aTot +=aAcc;
    bTot +=bAcc;
#else
    aTot += aSecArray[i];
    bTot += bSecArray[i];
#endif
  }
  aLow = aTot/(4*SECONDSIZE);
  aHigh =aTot/(SECONDSIZE/4);
  bLow = bTot/(4*SECONDSIZE);
  bHigh = bTot/(SECONDSIZE/4);


#if DPCM  
  aAcc = startSecsBaseA;
  bAcc = startSecsBaseB;
#endif     


  for (i=SECONDSIZE-1;i>=0;i--)  
  {
#if DPCM   
    aAcc = decodeSample(aAcc,aSecArray[i]);
    bAcc = decodeSample(bAcc,bSecArray[i]);
    if ((aAcc > aHigh) || (aAcc <aLow)) 
    {
      aSecArray[i] = encodeSample(-1, aTot/SECONDSIZE);  //set to an absolute sample
      xVal = aAcc;
      yVal=decodeSample(0,aSecArray[i]);  //note that the zero doesn't matter because we have an 'absolute sample
      for (j=i-1;j>=0;j--)
      {
        zVal=decodeSample(xVal,aSecArray[j]);
        aSecArray[j] = encodeSample(yVal,zVal);
        yVal= decodeSample(yVal,aSecArray[j]);
        xVal=zVal;
      }
    }
    if ((bAcc > bHigh) || (bAcc < bLow)) 
    {
      bSecArray[i] = encodeSample(-1, bTot/SECONDSIZE);  //set to an absolute sample
      xVal = bAcc;
      yVal=decodeSample(0,bSecArray[i]);  //note that the zero doesn't matter because we have an 'absolute sample
      for (j=i-1;j>=0;j--)
      {
        zVal=decodeSample(xVal,bSecArray[j]);
        bSecArray[j] = encodeSample(yVal,zVal);
        yVal= decodeSample(yVal,bSecArray[j]);
        xVal=zVal;
      }
    }
#else
    if (aSecArray[i] > aHigh || aSecArray[i] <aLow) { 
      aSecArray[i] = aTot/SECONDSIZE;
    }
    if (bSecArray[i] > bHigh || bSecArray[i] <bLow) { 
      bSecArray[i] = bTot/SECONDSIZE;
    }
#endif


  }  


}


void printTwoDigits(Client C, byte digits)
{
  if(digits < 10)
    C.print('0');
  C.print(digits,DEC);



void printOffsetTime(Client client, time_t basetime,long int offset)
{
  client.print(year(basetime));
  client.print("-");
  printTwoDigits(client,month(basetime+offset));
  client.print("-");
  printTwoDigits(client,day(basetime+offset));
  client.print(" ");
  printTwoDigits(client,hour(basetime+offset));
  client.print(":");
  printTwoDigits(client,minute(basetime+offset));
  client.print(":");
  printTwoDigits(client,second(basetime+offset));
}


void printHeader(Client client)
{
  // now output HTML data starting with standart header
  client.println("HTTP/1.1 200 OK");
  client.println("Content-Type: text/html");
  client.println();
  //set background color
  client.print("<body style=background-color:white>");
  //send first heading
  client.println("<font color='black'><h1>Power Used and Produced</font></h1>");
  client.println("<hr />");
  client.println("<hr />");
}


void printSeconds(Client client)
{
  int i;
  //output seconds data to browser
  client.print("<font color='blue' size='5'> Last ");
  client.print(SECONDSIZE);
  client.println(" Seconds");
  client.println("<br />");//some space between lines
  for (i=0;i<SECONDSIZE;i++)
  {
    client.print(i);
    client.print(",  ");
    client.print(PRINTSCALE*aSecArray[i]);//lets output some data
    client.print(", ");
    client.print(PRINTSCALE*bSecArray[i]);
    client.println("<br />");//some space between lines
  }
}


void printBCurrent(Client client)
{  
  int i;


  client.print("1\n");
  printOffsetTime(client, curSampleTime,0);
  client.print(" 1, ");
#if DPCM
  int aAcc = startSecsBaseA;
  int bAcc = startSecsBaseB;
  for (i=SECONDSIZE-1;i>=0;i--)  //note that this is in "rolling forward" order
  {
    aAcc = decodeSample(aAcc,aSecArray[i]);
    bAcc = decodeSample(bAcc,bSecArray[i]);
  }
  client.print(PRINTSCALE*aAcc);
#else
  client.print(PRINTSCALE*aSecArray[0]);
#endif
  client.print(", ");
#if DPCM
  client.print(PRINTSCALE*bAcc);
#else
  client.print(PRINTSCALE*bSecArray[0]);
#endif
  client.print("\n");
}


void printBHistSecs(Client client)
{
  int i;
  //output seconds data as text
  client.print(MIN(secondSamples,SECONDSIZE));
  client.print("\n");
#if DPCM
  int aAcc = startSecsBaseA;
  int bAcc = startSecsBaseB;
#endif


  for (i=SECONDSIZE-1;i>=0;i--)  //note that this is in "rolling forward" order
  {
    if (i<MIN(secondSamples,SECONDSIZE))    //must do this for hours and mins 
    {
      printOffsetTime(client, curSampleTime,-1*i);
      client.print(" 1, ");
#if DPCM
      aAcc = decodeSample(aAcc,aSecArray[i]);
      client.print(PRINTSCALE*aAcc);  
#else
      client.print(PRINTSCALE*aSecArray[i]);
#endif
      client.print(", ");
#if DPCM
     bAcc = decodeSample(bAcc,bSecArray[i]);
     client.print(PRINTSCALE*bAcc);
#else
      client.print(PRINTSCALE*bSecArray[0]);
#endif
      client.print("\n");
    }
    else
#if DPCM
    {
      aAcc = decodeSample(aAcc,aSecArray[i]);
      bAcc = decodeSample(bAcc,bSecArray[i]);
    }
#endif
  }
}


void printBHistMins(Client client)
{
   long int i;
  //output seconds data as text
  client.print(MIN(minuteSamples,MINUTESIZE));
  client.print("\n");


#if DPCM
  int aAcc = startMinsBaseA;
  int bAcc = startMinsBaseB;
#endif


  for (i=MINUTESIZE-1;i>=0;i--)  //note that this is in "rolling forward" order
  {
    if (i<MIN(minuteSamples,MINUTESIZE))    
    {
      printOffsetTime(client,curSampleTime,(long) (-second(curSampleTime)-60*i));
      client.print(" 60, ");
#if DPCM
      aAcc = decodeSample(aAcc,aMinArray[i]);
      client.print(PRINTSCALE*aAcc);
#else
      client.print(PRINTSCALE*aMinArray[i]);
#endif
      client.print(", ");
#if DPCM
      bAcc = decodeSample(bAcc,bMinArray[i]);
      client.print(PRINTSCALE*bAcc);
#else
      client.print(PRINTSCALE*bMinArray[0]);
#endif
      client.print("\n");
    }
    else
#if DPCM
    {
      aAcc = decodeSample(aAcc,aMinArray[i]);
      bAcc = decodeSample(bAcc,bMinArray[i]);
    }
#endif


  }


}


void printBHistHours(Client client)
{
   long int i;


  client.print(MIN(hourSamples,HOURSIZE));
  client.print("\n");
#if DPCM
  int aAcc = startHoursBaseA;
  int bAcc = startHoursBaseB;
#endif


  for (i=HOURSIZE-1;i>=0;i--)  //note that this is in "rolling forward" order
  {


    if (i<MIN(hourSamples,HOURSIZE))
    {


      printOffsetTime(client, curSampleTime,(long) (-60*minute(curSampleTime)-second(curSampleTime)-3600*i));      
      client.print(" 3600, ");
#if DPCM
      aAcc = decodeSample(aAcc,aHourArray[i]);
      client.print(PRINTSCALE*aAcc);
#else
      client.print(PRINTSCALE*aHourArray[i]);
#endif
      client.print(", ");
#if DPCM
      bAcc = decodeSample(bAcc,bHourArray[i]);
      client.print(PRINTSCALE*bAcc);
#else
      client.print(PRINTSCALE*bHourArray[i]);
#endif
      client.print("\n");
    }   
    else
#if DPCM
    {
      aAcc = decodeSample(aAcc,aHourArray[i]);
      bAcc = decodeSample(bAcc,bHourArray[i]);
    }
#endif
  }


}


void printMinutes(Client client)
{
  int i;
  //output minutes data to browser
  client.println("<hr />");
  client.println("<hr />");
  client.print("<font color='blue' size='5'>Last ");
  client.print(MINUTESIZE);
  client.println(" Minutes");
  client.println("<br />");//some space between lines
  for (i=0;i<MINUTESIZE;i++)
  {
    client.print(i);
    client.print(",  ");
    client.print(PRINTSCALE*aMinArray[i]);//lets output some data
    client.print(", ");
    client.print(PRINTSCALE*bMinArray[i]);
    client.println("<br />");//some space between lines
  }
}


void printCheckBox(Client client)
{
  //controlling led via checkbox
  client.println("<h1>LED control</h1>");
  //address will look like http://192.168.168.110/?L=1 when submited
  client.println("<form method=get name=LED><input type=checkbox name=L value=1>LED<br><input type=submit value=submit></form>");
  client.println("<br />");
}


void printFooter(Client client)
{
  client.println("</body></html>\n");
}




void printHours(Client client)
{
  int i;
  //output hours data to browser
  client.println("<hr />");
  client.println("<hr />");
  client.print("<font color='blue' size='5'>Last ");
  client.print(HOURSIZE);
  client.println(" Hours");
  client.println("<br />");//some space between lines
  for (i=0;i<HOURSIZE;i++)
  {
    client.print(i);
    client.print(",  ");
    client.print(PRINTSCALE*aHourArray[i]);//lets output some data
    client.print(", ");
    client.print(PRINTSCALE*bHourArray[i]);
    client.println("<br />");//some space between lines
  }
}


/*-------- NTP code ----------*/


unsigned long getNtpTime()
{
  sendNTPpacket(SNTP_server_IP);
  delay(1000);
  if ( UdpBytewise.available() ) {
    for(int i=0; i < 40; i++)
      UdpBytewise.read(); // ignore every field except the time
    const unsigned long seventy_years = 2208988800UL + timeZoneOffset;        
    return getUlong() -  seventy_years;      
  }
  return 0; // return 0 if unable to get the time
}


unsigned long sendNTPpacket(byte *address)
{
  UdpBytewise.begin(123);
  UdpBytewise.beginPacket(address, 123);
  UdpBytewise.write(B11100011);   // LI, Version, Mode
  UdpBytewise.write(0);    // Stratum
  UdpBytewise.write(6);  // Polling Interval
  UdpBytewise.write(0xEC); // Peer Clock Precision
  write_n(0, 8);    // Root Delay & Root Dispersion
  UdpBytewise.write(49); 
  UdpBytewise.write(0x4E);
  UdpBytewise.write(49);
  UdpBytewise.write(52);
  write_n(0, 32); //Reference and time stamps  
  UdpBytewise.endPacket();   
}


unsigned long getUlong()
{
  unsigned long ulong = (unsigned long)UdpBytewise.read() << 24;
  ulong |= (unsigned long)UdpBytewise.read() << 16;
  ulong |= (unsigned long)UdpBytewise.read() << 8;
  ulong |= (unsigned long)UdpBytewise.read();
  return ulong;
}


void write_n(int what, int how_many)
{
  for( int i = 0; i < how_many; i++ )
    UdpBytewise.write(what);
}
#if 0
void printFreeMem()
{
  int i;
  char *test;
  for (i=500;i>0;i--)
  {
    test = (char *) malloc(i);
    if (test)
    {
      Serial.print("Free mem is: ");
      Serial.print(i);
      Serial.print("\n");
      free(test);
      break;
    }
  }
}
#endif


/* main loop */
void setup()
{
  int i;
  int lastic = 0;
  int iccount;
  char *test;


  //Zero Array
  for (i=0;i<SECONDSIZE;i++)
  {
    aSecArray[i]=0;
    bSecArray[i]=0;
  }
  for (i=0;i<MINUTESIZE;i++)
  {
    aMinArray[i]=0;
    bMinArray[i]=0;
  }  
  for (i=0;i<HOURSIZE;i++)
  {
    aHourArray[i]=0;
    bHourArray[i]=0;
  }  


  //start Ethernet
  Ethernet.begin(mac, ip, gateway, subnet);
  pinMode(HomePin,INPUT);
  pinMode(SolarPin,INPUT);
  digitalWrite(HomePin,HIGH);
  digitalWrite(SolarPin,HIGH);


  pinMode(ledPin, OUTPUT);
  //enable serial datada print
  Serial.begin(115200); 
  Serial.print("Booted...\n");




  setSyncProvider(getNtpTime);
  setSyncInterval(3001); 
  while(timeStatus() == timeNotSet)   
  {
    Serial.print("Synching Clock\n"); // wait until the time is set by the sync provider


  }
  Serial.print("Time is: ");
  Serial.print(now());
  Serial.print("\n");
  
  attachInterrupt(0,myInterruptHandlerA,FALLING);  //interrupt zero is pin 2
  attachInterrupt(1, myInterruptHandlerB,FALLING); //interrupt one is pin 3
  iccount = ic;
  while (ic < iccount+5)
  { // just wait!
    }
      
}


void parseInput(Client client)
{
  //lets check if LED should be lighted
  if(readString.indexOf("L=1") != -1) 
  {
    //led has to be turned ON
    digitalWrite(ledPin, HIGH); // set the LED on
    LEDON = true;
    closeConnection =true;
  }
  else
  {
    //led has to be turned OFF
    digitalWrite(ledPin, LOW); // set the LED OFF
    LEDON = false; 
    closeConnection =true;
  }     
  if (readString.indexOf("BCurrent") != -1)
  {
    printBCurrent(client);
    closeConnection = false;
  }
  if (readString.indexOf("BHistSecs") != -1)
  {
    printBHistSecs(client);
    closeConnection = false;
  }
  if (readString.indexOf("BHistMins") != -1)
  {
    printBHistMins(client);
    closeConnection = false;
  }
  if (readString.indexOf("BHistHrs") != -1)
  {
    printBHistHours(client);
    closeConnection = false;
  }
  if(readString.indexOf("Seconds") != -1)
  {
    printHeader(client);
    printSeconds(client);
    printFooter(client);
    closeConnection =true;
  }
  if(readString.indexOf("Minutes") != -1)
  {
    printHeader(client);
    printMinutes(client);
    printFooter(client);
    closeConnection =true;
  }


  if(readString.indexOf("Hours") != -1)
  {            
    printHeader(client);
    printHours(client);
    printFooter(client);
    closeConnection =true;
  }
  if(readString.indexOf("All") != -1)
  {
    printHeader(client);
    printSeconds(client);
    printMinutes(client);
    printHours(client);   
    printCheckBox(client);        
    printFooter(client);   
    closeConnection =true;
  }     
}


void loop()
{
  int i;
  unsigned int aAcc;
  unsigned int bAcc;
  unsigned int a2Acc;
  unsigned int b2Acc;
  unsigned long aTot,bTot;


  myMillis = millis();
  if (myMillis < 1000+lastMyMillis) 
  {
    //could delay here or do other work
  }
  else
  {
    lastMyMillis=lastMyMillis+1000;


    secondCount++;
    secondSamples++;
    secondSamples = MIN(secondSamples,SECONDSIZE);
    if (myMillis < 5000+lastmillisA) {
      safeAval=channelAval;
    } 
    else {
      safeAval=0;
    }  // no data for 5 seconds means 0
    if (myMillis < 5000+lastmillisB) {
      safeBval=channelBval;
    } 
    else {
      safeBval=0;
    }  //no data for 5 seconds means 0
    if (safeAval && (safeAval < 150)) {
      Serial.print("Error Channel A: ");
      Serial.print(safeAval); 
      Serial.print(" \n");
    }
    if (safeBval && (safeBval < 150)) {
      Serial.print("Error Channel B: ");
      Serial.print(safeBval); 
      Serial.print(" \n");
    }
    curSampleTime =now();


    // scrolls the seconds along the array - trading off  speed for less code complexity
#if DPCM
    startSecsBaseA = decodeSample(startSecsBaseA,aSecArray[SECONDSIZE-1]);
    startSecsBaseB = decodeSample(startSecsBaseB,bSecArray[SECONDSIZE-1]);
    aAcc = startSecsBaseA;
    bAcc = startSecsBaseB;
#endif


    for (i=SECONDSIZE-1;i>0;i--)
    {
#if DPCM
      aAcc = decodeSample(aAcc,aSecArray[i-1]);
      bAcc = decodeSample(bAcc,bSecArray[i-1]);
#endif
      aSecArray[i] = aSecArray[i-1];
      bSecArray[i]=  bSecArray[i-1];
    }






#if USEBYTES    
#if DPCM


    if (safeAval) {
      aSecArray[0]=encodeSample(aAcc,SCALEFACTORA/(PRINTSCALE*safeAval));
    } 
    else  aSecArray[0]=encodeSample(aAcc,0);
    if (safeBval) {
      bSecArray[0]=encodeSample(bAcc,SCALEFACTORB/(PRINTSCALE*safeBval));
    } 
    else bSecArray[0]=encodeSample(bAcc,0);
#else
    if (safeAval) {
      aSecArray[0]=SCALEFACTORA/(PRINTSCALE*safeAval);
    } 
    else aSecArray[0]=0;
    if (safeBval) {
      bSecArray[0]=SCALEFACTORB/(PRINTSCALE*safeBval);
    } 
    else bSecArray[0]=0;
#endif
#else
    if (safeAval) {
      aSecArray[0]=SCALEFACTORA/safeAval;
    } 
    else aSecArray[0]=0;
    if (safeBval) {
      bSecArray[0]=SCALEFACTORB/safeBval;
    } 
    else bSecArray[0]=0;
#endif






    //This section handles minutes
    // every minute, average the last 60 seconds, and scroll the minutes along  


    if (second(curSampleTime) == 0)  // do this at the top of every minute
    {
      //first clean out any egregious data points
      //      cleanSeconds();
      //scroll the minutes


#if DPCM


      startMinsBaseA = decodeSample(startMinsBaseA,aMinArray[MINUTESIZE-1]);
      startMinsBaseB = decodeSample(startMinsBaseB,bMinArray[MINUTESIZE-1]);
      a2Acc = startMinsBaseA;
      b2Acc = startMinsBaseB;
#endif
      for (i=MINUTESIZE-1;i>0;i--)    
      {
#if DPCM
        a2Acc = decodeSample(a2Acc,aMinArray[i-1]);  
        b2Acc = decodeSample(b2Acc,bMinArray[i-1]);
#endif   
        aMinArray[i]= aMinArray[i-1];
        bMinArray[i]=  bMinArray[i-1];    
      }
      // Do the averaging   
      //although this second pass seems redundant with "cleanSeconds", it actually helps to have the data cleaned in the first pass and the
      //re-averaged.  
      //We could do this with more code complexity in a single pass
      aTot=0;
      bTot=0;
#if DPCM      
      aAcc=startSecsBaseA;
      bAcc=startSecsBaseB;
#endif
      for (i=SECONDSIZE-1; i>=0; i--) 
      {
#if DPCM
        aAcc = decodeSample(aAcc,aSecArray[i]);
        bAcc = decodeSample(bAcc,bSecArray[i]);
#endif 
        if (i<MIN(60,secondCount))
        {
#if DPCM
          aTot = aTot+aAcc;
          bTot = bTot+bAcc;       
#else
          aTot = aTot+aSecArray[i];
          bTot = bTot+bSecArray[i];
#endif
        }


      }


#ifdef DPCM
      aMinArray[0]= encodeSample(a2Acc,(aTot/MIN(60,secondCount)));      // add dpcm here
      bMinArray[0]= encodeSample(b2Acc,(bTot/MIN(60,secondCount)));
#else
      aMinArray[0]= (unsigned int) (aTot/MIN(60,secondCount));
      bMinArray[0]= (unsigned int) (bTot/MIN(60,secondCount));
#endif
      minuteCount++;
      minuteSamples++;
      minuteSamples = MIN(minuteSamples,MINUTESIZE);
      secondCount=0;
      //      Serial.print("Done with minutes\n"); 
    }  //closes the once a minute loop




    //This section handles hours    


    // every hour, average the last 60 minutes, and scroll the hours along  
    if ((minute(curSampleTime) == 0)  && (second(curSampleTime) == 0))
    {


#if DPCM
      startHoursBaseA = decodeSample(startHoursBaseA,aHourArray[SECONDSIZE-1]);
      startHoursBaseB = decodeSample(startHoursBaseB,bHourArray[SECONDSIZE-1]);
      a2Acc = startHoursBaseA;
      b2Acc = startHoursBaseB;
#endif      




      // do the scrolling
      for (i=HOURSIZE-1;i>0;i--)
      {


#if DPCM
        a2Acc = decodeSample(a2Acc,aHourArray[i-1]);
        b2Acc = decodeSample(b2Acc,bHourArray[i-1]);
#endif
        aHourArray[i] = aHourArray[i-1];
        bHourArray[i] = bHourArray[i-1];
      }


      aTot=0;
      bTot=0;
#if DPCM      
      aAcc=startMinsBaseA;
      bAcc=startMinsBaseB;
#endif      
      //find the average
      for (i=MINUTESIZE-1;i>=0;i--)
      {
#if DPCM  
        aAcc = decodeSample(aAcc,aMinArray[i]);
        bAcc = decodeSample(bAcc,bMinArray[i]);
#endif        
        if (i<MIN(60,minuteCount))
        {
#if DPCM      
          aTot += aAcc;
          bTot += bAcc;
#else
          aTot +=aMinArray[i];
          bTot +=bMinArray[i];
#endif          
        }


      }
#if DPCM  
      aHourArray[0]=  encodeSample(a2Acc,(aTot/MIN(60,minuteCount))); 
      bHourArray[0]= encodeSample(b2Acc,(bTot/MIN(60,minuteCount)));
#else      
      aHourArray[0]= (unsigned int) (aAcc/MIN(60,minuteCount));
      bHourArray[0]= (unsigned int) (bAcc/MIN(60,minuteCount));
#endif
      minuteCount=0;
      hourCount++;
      hourSamples++;
      hourSamples = MIN(hourSamples,HOURSIZE);
    }//closes the "once per hour loop


  }//closes the "once per second loop"




  // Create a client connection
  Client client = server.available();


  readString = "";
  if (client) {
    //Serial.print("Client connecting\n");
    while (client.connected()) 
    {
      if (client.available()) 
      {
        char c = client.read();
        //read char by char HTTP request
        if (readString.length() < MAXCMDLEN) 
        {
          //store characters to string
          readString +=c;
        }
        //output chars to serial port
        Serial.print(c);
        //if HTTP request has ended
        if (c == '\n') 
        {
          if(readString.indexOf("GET") != -1)
          {
            Serial.print(readString);
            //printFreeMem();
            parseInput(client);
            //if (closeConnection) client.stop();
            client.stop();
          }
          //clearing string for next read
          readString="";
        } //closes the branch where we've found a new line
      } //closes the if client available
    } // closes while client connected
    if (client) client.stop();
  }  //closes if client 
} //closes loop()
//*******************************











1 comment:

  1. can you please provide me the following things regarding this project please
    1) block diagram or circuit diagram
    2) components list

    ReplyDelete