





/* This program written by Tim Robinson
(c) Tim Robinson 2008



To read my gas and electric meters based on pulse inputs from the parallel port and updates a set of rrdtool databases.
This is my first ever C program so all recommendations for improvement welcome.

meter reading project web site at http:// www.txrx.org.uk/energy


Acknowledgements to Sean Adams - I took the initial code and inspiration from him, and am tweaking the project and returning it to the community from whence it came!

 http://www.seanadams.com/pge    

You must compile this with gcc -O 

The system was designed to measure three meters:

gas - whole house gas meter.
elec_main - main electricity meter to the house
elec_txrx - sub electricity meter feeding my servers, UPS, phones etc for my business.


Setting up of rrdtool is done as follows:

To create the rrdtool databases execute the following command lines as root:

	rrdtool create /var/spool/rrdtool/elec_main.rrd -s 60 DS:elec_main:COUNTER:60:U:U RRA:AVERAGE:0.5:1:86400
	rrdtool create /var/spool/rrdtool/elec_txrx.rrd -s 60 DS:elec_txrx:COUNTER:60:U:U RRA:AVERAGE:0.5:1:86400
	rrdtool create /var/spool/rrdtool/gas.rrd -s 60 DS:gas:COUNTER:60:U:U RRA:AVERAGE:0.5:1:86400


To plot the graphs, I have the following shell script which dumps the graph into a directory visible to Apache2.


#!/bin/bash
rrdtool graph /home/www/html/meters/elec.png --start end-6h  --lower-limit 0 --title "Electrical Power Usage" DEF:power=/var/spool/rrdtool/elec_main.rrd:elec_main:AVERAGE --vertical-label Watts CDEF:watts=power,3600,\* LINE1:watts#ff0000:Power

# CDEF converts pulses per second into watts.  My meter has 1000 pulses per kWh 

rrdtool graph /home/www/html/meters/gas.png --start end-3h --lower-limit 0 --title "Gas Power Usage"  DEF:unit=/var/spool/rrdtool/gas.rrd:gas:AVERAGE:step=1  --vertical-label Watts CDEF:watts=unit,396180,\* LINE1:watts#ff0000:Power

Gas meter reads in cubic metres (m^3)
From gas bill, applying Calorific value of gas and pressure/temperature contstants for the period, 1 cubic metre gives me 11.05 kWh (we are billed in kWh for gas here in UK)

Natively rrdtool will plot pulses per secondn which we want to display in Watts.


	1 pulse = 110 Wh =  396000 watt-seconds

as the graph is displaying watt-seconds per second, which is watts.

If my maths is wrong please shout - but the graphs look right based on the power of my boiler (15 Kw)

Comments, feedback or errors please to tim@txrx.org.uk


*/


#include <sys/types.h>
#include <sys/time.h>
#include <stdio.h>
#include <unistd.h>
#include <asm/io.h>
#include <stdint.h>
#include <stdlib.h>


/* IO address of the parallel port*/

#define LP_BASE    0x378	
#define LP_DATA    LP_BASE
#define LP_STATUS  LP_BASE + 1
#define LP_CONTROL LP_BASE + 2


/* file settings - hard coded - sorry ! */

#define LOGFILE  	"/var/log/meters.log" 		/* Save watt-seconds to this file		*/
#define METER_INDEXES 	"/var/log/meterstatus.log"	/* Save a snapshot of meter values to this file	*/


#define MAX_CHECKS 1 // Debounce max number of checks
#define SIZE 256

// Set port values for reading inputs from parallel port

#define spare3	4	/* pin 15 ERROR bit 3 */
#define elec2	8	/* pin 13 SLCT  bit 4 */
#define elec1	64	/* pin 12 PE    bit 5 */
#define gas	16	/* pin 10 ACK   bit 6 */
#define spare7	128	/* pin 11 BUSY  bit 7 */


// these are the bit numbers of the parallel port
// should probably combine both definitions, or calculate one from the other.
#define elec2_total	4
#define gas_total	5
#define elec1_total	6



uint8_t currState = 0;
uint8_t prevState = 0;

// Not sure this is strictly necessary, but is a legacy from Sean's code and is a nice timer
void sleep_us(unsigned int usecs) {
  struct timeval sleeptime;
  sleeptime.tv_sec=usecs / 1000000;
  sleeptime.tv_usec=usecs % 1000000;
  select(0,NULL,NULL,NULL,&sleeptime);
}


// this function reads the parallel port until it has been stable for MAX_CHECKS 
void debounce_port() 
{
  
  static uint8_t State[MAX_CHECKS];
  static uint8_t Index = 0;
  uint8_t i,j;
  
  State[Index++]=inb(LP_STATUS);
  if (Index >= MAX_CHECKS) 
    {
      Index = 0;
    }
  
  j=0xff;
  for(i=0; i<MAX_CHECKS;i++)
    {
      j=j & State[i];
    }
  currState= j;
}

// Main routine starts here and runs for ever.

int main (int argc, char *argv[]) 
{
  unsigned long t;
  time_t calendar_time;
  struct tm *local_time;
  char buffer[SIZE];
  unsigned char cur,last;
  struct timeval starttime, stoptime;
  FILE *logfile_ptr;
  FILE *meter_ptr;

//let's see if we can talk to the port
  if (ioperm(LP_BASE, 3, 1) < 0) 
    {
      printf ("Can't get permission to access IO port 0x%x\n",LP_BASE);
      exit(-1);
    }

  // woot! port ok..set the output ports to 0xff to provide power and pullups
  outb(0xff, LP_BASE);

  // grab saved meter meter reading values from file

  char text[16];
  int counters[8];
  int flags[8];
  int i;
  char systembuffer[128];

//meter indexes (8 of them) stored in seperate lines in the file defined as METER_INDEXES

// if the file exists read in the values into an array called counters[]
  if      ((meter_ptr=fopen(METER_INDEXES,"r")) !=NULL)
    {  
      for (i = 0; i < 8;i++)
	{
	  fgets(text,16,meter_ptr);
	  counters[i]=atoi(text);
	}
      fclose(meter_ptr);
    }
  else
    	{

// so the file does not exist, and we assume meter readings start at zero, so initialise the array
      	for (i = 0; i < 8;i++)
		{
	  counters[i]=0;
		}
    	}


// to start with we read in the port until it has stabilised

  for (i=0; i < MAX_CHECKS; i++)
    	{
      		debounce_port();
    	}

// we now have an initial state from which to start looking for changes to the bits on the port
prevState = currState;

// here we go loopy loo - start polling the port for ch ch changes....
  while (1)
    {
      sleep_us(10000);		// wait 10 ms
      debounce_port();		// read port til stable
	//we need some variables

	int bit ;	//a bit counter
      	int changeflag;	// flag which is zero when no changes, 1 when changes discovered
      uint8_t mask;	// an 8 bit mask

//no changes yet, so we set changeflag to zero	
      changeflag=0;

// initialise the flag[] array to zero - this is a bit mask which indicates which bit has changed
      for (i = 0; i < 8;i++)
	{
	  flags[i]=0;
	}


// let's test each bit for changes.....

      for (bit = 0; bit < 8; bit++)
	{
	  mask = 1 << bit;	//set the mask to the bit we are looking at

	  if ((currState & mask) != (prevState & mask))  //we have a bit which changed to 1 from 0
	    {
	      if (currState & mask)
		{	// at this point, we know we have a valid pulse (down--> up --down  on this bit so we increment the counter
		  counters[bit]++;	//increment bit counter
		  changeflag++;		//set the change flag
		  flags[bit]++;		// set the flag for bit [] to indicate which bit has changed.
		  
		}
	    }
	}
      prevState = currState;		//update prevState to the current state now we have done the test


//	now let's look and take action if we have a valid pulse on an input
      if(changeflag !=0) 
	{
	//	Write everything to a log file for debug and history.

	  calendar_time = time(NULL);
	  local_time = localtime( &calendar_time);	
	  strftime(buffer,SIZE,"%Y-%m-%d %H:%M:%S",local_time);	// format date string for mysql-friendly 
	  if ((logfile_ptr=fopen(LOGFILE,"a")) != NULL)
	    {
	      fprintf (logfile_ptr,"%s, Gas %d, Elec1 %d, Elec2 %d \n",buffer,counters[gas_total],counters[elec1_total],counters[elec2_total]);
	      fclose(logfile_ptr);
	    }

	// write the current meter index readings to the index file 

	  if ((meter_ptr=fopen(METER_INDEXES,"w")) !=NULL) 
		{
	    for (i = 0; i < 8;i++)
	      		{
			//write counters to file     
			fprintf(meter_ptr,"%d \n",counters[i]);
	      		}
	    	fclose(meter_ptr);

	//  Now we check which bit has changed, and poke a new reading into rrdtool.

 
	    	if (flags[elec1_total]!=0) 
	      		{
			sprintf(systembuffer,"/usr/bin/rrdtool update /var/spool/rrdtool/elec_main.rrd -t elec_main N:%d",counters[elec1_total]);
			printf("/usr/bin/rrdtool update /var/spool/rrdtool/elec_main.rrd -t elec_main N:%d\n",counters[elec1_total]);
			}
	    	if (flags[elec2_total]!=0)
	      		{
			sprintf(systembuffer,"/usr/bin/rrdtool update /var/spool/rrdtool/elec_txrx.rrd -t elec_txrx N:%d",counters[elec2_total]);
	      		printf("/usr/bin/rrdtool update /var/spool/rrdtool/elec_txrx.rrd -t elec_txrx N:%d\n",counters[elec2_total]);
	      		}
	    	if(flags[gas_total]!=0) 
	      		{
			sprintf(systembuffer,"/usr/bin/rrdtool update /var/spool/rrdtool/gas.rrd -t gas N:%d",counters[gas_total]);
                	printf("/usr/bin/rrdtool update /var/spool/rrdtool/gas.rrd -t gas N:%d\n",counters[gas_total]);
			}
	    system(systembuffer);
		}

	}
    }
}
// The End.


