Reading a CurrentCost CC128 ENVI Power Monitor using Debian Linux

Updated: 4 Dec 2013

Home... Help... Search... Computers... Science...

Reading a CurrentCost CC128 ENVI Power Monitor using Debian Linux

The CC128 ENVI Power Monitor

The CC128 ENVI Power Monitor is cheap and does work pretty well, with its wireless data transmitter connected to the house's main power switchboard. To get data out of it, I use the optional serial cable with its built-in USB convertor, plugged into my Debian Linux server.

Gathering the Data

I adapted the following PERL script from one I found on the Net. It just sits passively waiting for serial data to arrive, ie, it sends nothing to the CC128. I expect the CC128 can receive signals, but my scheme does not use any. At intervals the CC128 dumps out historic data it has stored, and the script filters this into a separate history file, in case I ever have a need for it.

  #!/usr/bin/perl -w
  #Original Perl code by Paul Mutton.
  #Updated for CC128 by Mark E Taylor. April 2009.
  # Reads data from a Current Cost device via USB/serial port.
  # modified 2011 by Tardus, to parse the data stream into "current" value and "history" value files.
  # modified 2013 by Tardus to pass the serial port as an argument. With 
  # multiple USB serial devices, eg, an Arduino as well as the PL2303 used by
  # the CC128, we cannot be sure which port gets assigned to the PL2303,
  # hence cannot hard code it in this script.
  use strict;
  use Device::SerialPort qw( :PARAM :STAT 0.07 );
  #my $PORT = "/dev/ttyUSB0";
  # port now passed via argument to
  print $ARGV[0];
  my $PORT = "$ARGV[0]";
  my $ob = Device::SerialPort->new($PORT);
  $ob->baudrate(57600); #For CC Classic use 9600 for CC128 use 56700.
  open(SERIAL, "+>$PORT");
  while (my $line = <SERIAL>) {
      if ( length($line) > 240 ) {
          print ("HISTORY: $line" );
          open (HISTFILE, '>>powerhist.txt');
          print HISTFILE  "$line\n" ;
          close (HISTFILE);
      } else {
          print ("CURRENT: $line" );
          open (CURRFILE, '>>powercurr.txt');
          print CURRFILE  "$line\n" ;
          close (CURRFILE);
      print "\n";

Note: you will need to install the PERL serial module apt-get install libdevice-serialport-perl (as root)

The script logic is to write any files longer than 240 characters to a history file, and shorter ones to a current power file. The history lines the CC128 pumps out at intervals are very long. For now I only use the current power values.

Also, the calling script has to work out what the actual "ttyUSBx" port is and pass it to

At least with my CC128 the data output is quite dirty, with items dropping out of the xml text stream, and with frequent null characters (ASCII 00) being inserted. The calling script,, filters the rubbish out.

Here is a sample:-

  powercurr.txt (single line records, wrapped for easier display)

The "^@" characters are nulls that don't belong here. Note to that the third line has lost the closing "</dsb>". The second line is OK.

Processing the data

The following bash shell script runs every 10 minutes, activated by an entry in the crontab scheduler. You might find my brief guide to BASH helpful, if you are not familiar with BASH scripts

  # get the latest house power Kw figures and use it to plot with mrtg
  # also add to a history file for any desired later processing (just in case!)
  # Tardus 27 Jul 2011
  # altered 6 March 2012 to use direct ENVI pickup from this Linux machine
  # data is now in the powercurr.txt file
  # Revised November 2013 to better handle the dirty data from the CC128
  cd ~/monitor
  #check to see if the CurrentCost ENVI data receiving script is running
  if [ ! $(pidof -x ]; then
      echo "cc6 is NOT running"
      echo "starting"
      # find what port the CC128's pl2303 usb serial convertor is on
      PORT=`dmesg|grep ttyUSB|awk '/pl2303/ {print $NF}'`
      echo "PORT=$PORT"
      # run in the background, passing it the port to use
      nohup ./ "/dev/"$PORT > ./cc6.log 2>&1 &
      echo "cc6 is running"
      cp powercurr.txt powercurr.tmp
      rm powercurr.dat
          # parse the xml data and clean it up as much as possible
          # also turning each multi-line record (output by xml2)
          # into a single line record without the "=" signs
          # this is done by the tr command, which translates = and newlines
          # into spaces
      while read LINE
          echo $LINE | xml2 |tr '\n=' ' ' >> powercurr.dat
          echo " "						>> powercurr.dat
      done < powercurr.tmp
          # now we use awk to test for uncorrupted records and average the 
          # data over the approx. 10 minute collection period
      awk -f getAverage.awk powercurr.dat > latestHousePower.txt
          # and get the last line into a separate file
      tail -1 latestHousePower.txt > latestHousePower.dat
      echo `date` "latestHousePower.dat file just written"
          # and add its contents to a history file, in case I ever find a use
          # for it.
      awk '{print strftime(systime()),$0}' latestHousePower.dat >> \
          # reset the input file from the ENVI CC128
      echo "lines in powercurr.txt: " `wc -l powercurr.txt`
      cat powercurr.txt >> powercurr.hist
      cat /dev/null >  powercurr.txt
      cat latestHousePower.txt >> housePower.hist
      echo `date` "===========================" >> housePower.hist
          # plot using MRTG
      env LANG=C mrtg housepower.cfg
      # now send to
      # get date from Linux
      DATE=`date +"%Y%m%d"`
      TIME=`awk '{print $2}'     		latestHousePower.dat | cut -c1-5`
      TEMPERATURE=`awk '{print $3}'     	latestHousePower.dat`
      CONSUMPTION=`awk '{print $4+$5}'     latestHousePower.dat` 
      curl -d "d=$DATE" -d "t=$TIME" -d "v4=$CONSUMPTION" -d "v5=$TEMPERATURE" \
          	-H "X-Pvoutput-Apikey: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
          	-H "X-Pvoutput-SystemId: NNNN" \

Here is an example of a cleaned up data file from the CC128:-

  powercurr.dat (single line records, wrapped for easier display)
  /msg/src CC128-v1.18 /msg/dsb 01157 /msg/time 17:15:17 /msg/tmpr 23.1 
  /msg/sensor 0 /msg/id 02882 /msg/type 1 /msg/ch1/watts 00419 
  /msg/ch2/watts 00000  
  /msg/src CC128-v1.18 /msg/dsb 01157 /msg/time 17:16:37 /msg/tmpr 23.1 
  /msg/sensor 0 /msg/id 02882  
  /msg/src CC128-v1.18 /msg/dsb 01157 /msg/time 17:17:26 /msg/tmpr 23.1  
  /msg/src CC128-v1.18 /msg/dsb 01157 /msg/time 17:18:39 /msg/tmpr 23.1  
  /msg/src CC128-v1.18 /msg/dsb 01157 /msg/time 17:18:51 /msg/tmpr 23.1 
  /msg/sensor 0 /msg/id 02882 /msg/type 1 /msg/ch1/watts 02130 
  /msg/ch2/watt /msg/ch2 watts>  
  /msg/src CC128-v1.18  
  /msg/src CC128-v1.18 /msg/dsb 01157 /msg/time 17:19:17 /msg/tmpr 23.1 
  /msg/sensor 0 /msg/id 02882 /msg/typy pe> /msg/typy/ch1/watts 03252 
  /msg/typy/ch2/watts 00144  
  /msg/src CC128-v1.18 /msg/dsb 01157 /msg/time 17:20:38 /msg/tmpr 23.1 
  /msg/sensor 0 /msg/id 02882 /msg/type 1 /msg/ch1/watts 01716 
  /msg/ch1/hc2/watts 00008  
  /msg/src CC128-v1.18 /msg/dsb 01157 /msg/time 17:21:47 /msg/tmpr 23.1
   /msg/sensor 0 /msg/id 02882 /msg/type 1 /msg/ch1/watts 03217  
  /msg/src CC128-v1.18 /msg/dsb 01157 /msg/time 17:22:12 /msg/tmpr 23.1 
  /msg/sensor 0 /msg/id 02882 /msg/type 1 /msg/ch1/watts 03185 
  /msg/ch2/watts 00144  

What we want is exactly 18 white-space delimited fields, and exactly 153 characters. Anything else will be rejected by the getAverage.awk script.

  # getAverage.awk
  BEGIN	{	totalStdWatts=0; totalOffPeakWatts=0; totaltemp=0; count=0
      # 	corr is a correction factor passed by the calling script
      #	if corr is not specified it is forced to "1"
      	if ( corr == "" ) corr=1		}
  # find only lines with 18 fields
  NF==18	{ 	if ( length ($0) == 153 ) #process only if line has 153 characters
          	{	dsb=$4
          		totalStdWatts += stdWatts
          		totalOffPeakWatts += offPeakWatts
          		totaltemp += temperature
          		count += 1
          		print dsb,stamp,temperature,stdWatts,offPeakWatts,count,   \
  END	{	avgStdWatts = int (totalStdWatts * corr / count + 0.5 )
          avgOffPeakWatts = int (totalOffPeakWatts / count + 0.5 )
          avgtemp = int (totaltemp / count * 10 + 0.5 ) / 10
          print dsb,stamp,avgtemp,avgStdWatts,avgOffPeakWatts

Here is the latestHousePower.txt file as output by getAverage.awk

  01158 15:47 23.2 00769 00002 1 23.2 769 2
  01158 15:49 23.1 00760 00003 2 46.3 1529 5
  01158 15:49 23.1 00755 00003 3 69.4 2284 8
  01158 15:49 23.1 761 3

and the last line which is written to latestHousPower.dat

  01158 15:49 23.1 761 3

The 3rd field is the room temperature in degrees Celsius, the 4th is the standard rate power consumption in watts and the 5th is the off-peak power in watts.

Here is the MRTG configuration file I use:-

  Interval: 10
  WorkDir: /home/steve/monitor/mrtg/
  Target[single]: `~/monitor/`
  Options[single]: growright,integer,gauge,noo
  Colours[single]: orange#ffa500,Color2#ff6600,Color3#121212,Color4#191970
  MaxBytes[single]: 10000
  AbsMax[single]: 15000
  WithPeak[single]: ymw
  YLegend[single]: Watt
  ShortLegend[single]: w
  Legend1[single]: w
  LegendI[single]: Standard Rate
  LegendO[single]: w
  Title[single]: Single (Standard) Rate Power Consumed by House 
  PageTop[single]: <h1>Single (Standard) Rate Power Consumed by House</h1> 
      Note: time is Australian Eastern Standard Time<br> 
  Target[offpeak]: `~/monitor/`
  Options[offpeak]: growright,integer,gauge,noo
  Colours[offpeak]: black#000000,Color2#ff6600,Color3#001200,Color4#191970
  MaxBytes[offpeak]: 10000
  AbsMax[offpeak]: 15000
  WithPeak[offpeak]: ymw
  YLegend[offpeak]: Watt
  ShortLegend[offpeak]: w
  Legend1[offpeak]: w
  LegendI[offpeak]: Offpeak
  LegendO[offpeak]: w
  Title[offpeak]: Off Peak Rate Power Consumed by House (Water Heating)
  PageTop[offpeak]: <h1>Off Peak Rate Power Consumed by House (Water Heating)</h1>
       Note: time is Australian Eastern Standard Time<br> 
  Target[housepower]: `~/monitor/`
  Options[housepower]: growright,integer,gauge
  Colours[housepower]: orange#ffa500,black#000000,Color3#121212,Color4#191970
  MaxBytes[housepower]: 10000
  AbsMax[housepower]: 15000
  YLegend[housepower]: Watt
  ShortLegend[housepower]: w
  Legend1[housepower]: w
  LegendI[housepower]: Standard
  LegendO[housepower]: Offpeak
  Title[housepower]: Total Power Consumed by House 
  PageTop[housepower]: <h1>Total Power Consumed by House</h1> 
      Note: time is Australian Eastern Standard Time<br> 
  Target[envi-t]: `~/monitor/`
  Options[envi-t]: growright,integer,gauge
  Colours[envi-t]: springgreen#00ee76,color2#000000,Color3#121212,Color4#191970
  MaxBytes[envi-t]: 30
  AbsMax[envi-t]: 40
  WithPeak[envi-t]: ymw
  UnScaled[envi-t]: ymwd
  YLegend[envi-t]: DegC
  ShortLegend[envi-t]: C
  Legend1[envi-t]: T 
  LegendI[envi-t]: Indoor Temperature
  LegendO[envi-t]: Subfloor Temperature
  Title[envi-t]: Room Temperature at the CurrentCost ENVI CC128 Monitor Station
  PageTop[envi-t]: <h1>Room Temperature at the CurrentCost ENVI CC128  Monitor 
      Station</h1> Note: time is Australian Eastern Standard Time<br>  

And the simple shell scripts that gather the data for mrtg to process:-

  # extracts data from single rate tariff file and prepares for MRTG
  W=`awk '{print int($4)}' ~/monitor/latestHousePower.dat`
  LABEL="Single (Standard) Rate Power Consumption - Watts"
  echo $W
  echo $W # MRTG needs 2 variables to plot - we suppress 2nd later
  echo $UPTIME
  echo $LABEL
  # extracts data from offpeak rate tariff file and prepares for MRTG
  W=`awk '{print int($5)}' ~/monitor/latestHousePower.dat`
  LABEL="Off Peak Rate Power Consumption (Water Heating) - Watts"
  echo $W
  echo $W # MRTG needs 2 variables to plot - we suppress 2nd later
  echo $UPTIME
  echo $LABEL
  # extracts data from single and offpeak rate tariff files and prepares for MRTG
  SW=`awk '{print int($4)}' ~/monitor/latestHousePower.dat`
  OW=`awk '{print int($5)}' ~/monitor/latestHousePower.dat`
  LABEL="Total Power Consumption - Watts"
  echo $SW
  echo $OW
  echo $UPTIME
  echo $LABEL
  # extracts temperature at the ENVI monitor from single rate tariff file 
  # and prepares for MRTG
  # added experimental temperature collection of subfloor via digitemp - 10/11/12
  TEMP=`awk '{print int($3+0.5)}' ~/monitor/latestHousePower.dat`
  LABEL="Room Temperature - Degrees C"
  SUBTEMP=`fgrep "Sensor 1" ~/monitor/digitemp.log |tail -1 \
          	| awk '{print int($7+0.5)}'` 
  echo $TEMP
  echo $SUBTEMP
  echo $UPTIME
  echo $LABEL

Live data display

You can see live data plotted here

Home... Help... Search... Computers... Science...

This page Last refreshed: 04 Oct 2023

About Tardus

Contact me, "Tardus" Copyright powered by txt2tags