commit 8108b800da820bf6fdb3a2ee9d2032b812ddf3dc Author: Willem Oldemans Date: Mon Jun 23 11:51:54 2025 +0200 initial diff --git a/Metro/LICENSE b/Metro/LICENSE new file mode 100644 index 0000000..f945b53 --- /dev/null +++ b/Metro/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2013 thomasfredericks + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/Metro/Metro.cpp b/Metro/Metro.cpp new file mode 100644 index 0000000..104739f --- /dev/null +++ b/Metro/Metro.cpp @@ -0,0 +1,61 @@ + + +#if defined(ARDUINO) && ARDUINO >= 100 +#include "Arduino.h" +#else +#include "WProgram.h" +#endif +#include "Metro.h" + +Metro::Metro() +{ + + this->interval_millis = 1000; + +} + + +Metro::Metro(unsigned long interval_millis) +{ + + this->interval_millis = interval_millis; + +} + + +void Metro::interval(unsigned long interval_millis) +{ + this->interval_millis = interval_millis; +} + +uint8_t Metro::check() +{ + + unsigned long now = millis(); + + if ( interval_millis == 0 ){ + previous_millis = now; + return 1; + } + + if ( (now - previous_millis) >= interval_millis) { + #ifdef NOCATCH-UP + previous_millis = now ; + #else + previous_millis += interval_millis ; + #endif + return 1; + } + + return 0; + +} + +void Metro::reset() +{ + + this->previous_millis = millis(); + +} + + diff --git a/Metro/Metro.h b/Metro/Metro.h new file mode 100644 index 0000000..ec09aad --- /dev/null +++ b/Metro/Metro.h @@ -0,0 +1,49 @@ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * + Main code by Thomas O Fredericks (tof@t-o-f.info) + Contributions by Paul Bouchier and Benjamin.soelberg +* * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef Metro_h +#define Metro_h + +#include + + +class Metro +{ + +public: + Metro(); + Metro(unsigned long interval_millis); + void interval(unsigned long interval_millis); + uint8_t check(); + void reset(); + +private: + unsigned long previous_millis, interval_millis; + +}; + +#endif + + diff --git a/Metro/README.md b/Metro/README.md new file mode 100644 index 0000000..2d680d0 --- /dev/null +++ b/Metro/README.md @@ -0,0 +1 @@ +Please visit https://github.com/thomasfredericks/Metro-Arduino-Wiring for latest code and documentation. \ No newline at end of file diff --git a/Metro/examples/blinking/blinking.ino b/Metro/examples/blinking/blinking.ino new file mode 100644 index 0000000..72d0d99 --- /dev/null +++ b/Metro/examples/blinking/blinking.ino @@ -0,0 +1,30 @@ +/* + This code will blink an LED attached to pin 13 on and off. + It will stay on for 0.25 seconds. + It will stay off for 1 second. + */ +#include //Include Metro library +#define LED 13 // Define the led's pin + +//Create a variable to hold theled's current state +int state = HIGH; + +// Instanciate a metro object and set the interval to 250 milliseconds (0.25 seconds). +Metro ledMetro = Metro(250); + +void setup() +{ + pinMode(LED,OUTPUT); + digitalWrite(LED,state); +} + +void loop() +{ + + if (ledMetro.check() == 1) { // check if the metro has passed its interval . + if (state==HIGH) state=LOW; + else state=HIGH; + + digitalWrite(LED,state); + } +} diff --git a/Metro/examples/blinking_2_instances/blinking_2_instances.ino b/Metro/examples/blinking_2_instances/blinking_2_instances.ino new file mode 100644 index 0000000..4bdd974 --- /dev/null +++ b/Metro/examples/blinking_2_instances/blinking_2_instances.ino @@ -0,0 +1,51 @@ +// This code will blink output 13 every 250 ms +// abd will blink output 9 every 125 ms + + +#include // Include Metro library +#define LED0 13 // Define a LED pin +#define LED1 9 // Define another LED pin + +// Create variables to hold the LED states +int state0 = HIGH; +int state1 = HIGH; + +// Instantiate a metro object and set the interval to 250 milliseconds (0.25 seconds). +Metro metro0 = Metro(250); + +// Instantiate another metro object and set the interval to 125 milliseconds (0.125 seconds). +Metro metro1 = Metro(125); + +void setup() +{ + pinMode(LED0,OUTPUT); + digitalWrite(LED0,state0); + + pinMode(LED1,OUTPUT); + digitalWrite(LED1,state1); + +} + +void loop() +{ + + if (metro0.check() == 1) { // check if the metro has passed its interval . + if (state0==HIGH) { + state0=LOW; + } else { + state0=HIGH; + } + digitalWrite(LED0,state0); + } + + if (metro1.check() == 1) { // check if the metro has passed its interval . + if (state1==HIGH) { + state1=LOW; + } else { + state1=HIGH; + } + digitalWrite(LED1,state1); + } + + +} diff --git a/Metro/examples/blinking_2_intervals/blinking_2_intervals.ino b/Metro/examples/blinking_2_intervals/blinking_2_intervals.ino new file mode 100644 index 0000000..d6f9a70 --- /dev/null +++ b/Metro/examples/blinking_2_intervals/blinking_2_intervals.ino @@ -0,0 +1,36 @@ +/* + This code will blink an LED attached to pin 13 on and off. + It will stay on for 0.25 seconds. + It will stay off for 1 second. + */ +#include //Include Metro library +#define LED 13 // Define the led's pin + +//Create a variable to hold theled's current state +int state = HIGH; + +// Instanciate a metro object and set the interval to 250 milliseconds (0.25 seconds). +Metro ledMetro = Metro(250); + +void setup() +{ + pinMode(LED,OUTPUT); + digitalWrite(LED,state); +} + +void loop() +{ + + if (ledMetro.check() == 1) { // check if the metro has passed its interval . + if (state==HIGH) { + state=LOW; + ledMetro.interval(250); // if the pin is HIGH, set the interval to 0.25 seconds. + } + else { + ledMetro.interval(1000); // if the pin is LOW, set the interval to 1 second. + state=HIGH; + } + digitalWrite(LED,state); + } +} + diff --git a/Metro/examples/serialInterval/serialInterval.ino b/Metro/examples/serialInterval/serialInterval.ino new file mode 100644 index 0000000..68a9d41 --- /dev/null +++ b/Metro/examples/serialInterval/serialInterval.ino @@ -0,0 +1,24 @@ +// This example sends a Serial message every 250 milliseconds + +#include // Include the Metro library + +Metro serialMetro = Metro(250); // Instantiate an instance + +void setup() { + Serial.begin(115200); // Start the Serial communication +} + +void loop() { + + if (serialMetro.check() == 1) { // check if the metro has passed it's interval . + // Output all the analog readings seperated by a space character + for (int i = 0; i < 6; i++ ) { + Serial.print (analogRead( i) ); + Serial.print(32,BYTE); + } + // Terminate message with a linefeed and a carriage return + Serial.print(13,BYTE); + Serial.print(10,BYTE); + } +} + diff --git a/Metro/keywords.txt b/Metro/keywords.txt new file mode 100644 index 0000000..62ca8a2 --- /dev/null +++ b/Metro/keywords.txt @@ -0,0 +1,25 @@ +####################################### +# Syntax Coloring Map For Test +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +Metro KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +check KEYWORD2 +interval KEYWORD2 + +####################################### +# Instances (KEYWORD2) +####################################### + +####################################### +# Constants (LITERAL1) +####################################### + diff --git a/ferrarismonitor.zip b/ferrarismonitor.zip new file mode 100644 index 0000000..e81d56e Binary files /dev/null and b/ferrarismonitor.zip differ diff --git a/ferrarismonitor/FerrarisMonitor/FerrarisMonitor.ino b/ferrarismonitor/FerrarisMonitor/FerrarisMonitor.ino new file mode 100644 index 0000000..f2fd3cd --- /dev/null +++ b/ferrarismonitor/FerrarisMonitor/FerrarisMonitor.ino @@ -0,0 +1,141 @@ +int portLeft = 4; +int portRight = 3; + +long sampleMetro = 10; +long printMetro = 100; + +int16_t stateLeft = 0; +int16_t stateRight = 0; + +int16_t minLeft = 1024; +int16_t maxLeft = 0; +int16_t nextMinLeft = 1024; +int16_t nextMaxLeft = 0; +int16_t minRight = 1024; +int16_t maxRight = 0; +int16_t nextMinRight = 1024; +int16_t nextMaxRight = 0; + +int16_t margin = 5; +int16_t rotation = 1; // right = 1, normal, -1 is reverse + +int16_t lightLeft = 0; // latest reading +int16_t lightRight = 0; + +double prevMs = 0; // timestamp of last peak +long previousSample = 0; +long previousPrint = 0; +double len = 0; // delay between the last two peaks + +void setup() { + Serial.begin(57600); + Serial.println("\n[Monitoring a Ferraris Meter]"); + + //portLeft.mode(INPUT); + //portRight.mode(INPUT); +} + +void loop() { + unsigned long currentMillis = millis(); + // take a reading from both sensors + if(currentMillis - previousSample > sampleMetro) { + previousSample = currentMillis; + // read sensors + lightLeft = analogRead(portLeft); + lightRight = analogRead(portRight); + + // to take into account changing peak sizes, we follow the peak size continuously + // the found peak size serves as the target (minus a small margin) for the next peak detection + // update minimum and maximum for the left sensor + if ( lightLeft > nextMaxLeft ) { + nextMaxLeft = lightLeft; + } + // we also do this for the valley + if ( lightLeft < nextMinLeft ) { + nextMinLeft = lightLeft; + } + + // and the same for the right sensor + // update minimum and maximum for the right sensor + if ( lightRight > nextMaxRight ) { + nextMaxRight = lightRight; + } + if ( lightRight < nextMinRight ) { + nextMinRight = lightRight; + } + + if (( stateLeft == 0 ) && (lightLeft > maxLeft - margin )) { + // make state = 1 when light above threshold and state == 0 + stateLeft = 1; + + // we are now going to a minimum, reset the minimum value of the left sensor + minLeft = nextMinLeft; + nextMinLeft = 1024; + + // calculate rotation direction + if (( stateRight == 0 ) && (rotation < 1)) { + rotation++; + } else if ((stateRight == 1 ) && (rotation > -1)) { + rotation--; + } + + // calculate delay between the last two peaks, now and prevMs + double ms = millis(); + len = ms - prevMs; + prevMs = ms; + + } else if ((stateLeft == 1 ) && (lightLeft < minLeft + margin)) { + // make state = 0 when light below threshold and state == 1 + stateLeft = 0; + + // we are now going to a maximum, reset the maximum value of the left sensor + maxLeft = nextMaxLeft; + nextMaxLeft = 0; + + } + + if (( stateRight == 0 ) && (lightRight > maxRight - margin )) { + // make state = 1 when light above threshold and state == 0 + stateRight = 1; + + // we are now going to a minimum, reset the minimum value of the right sensor + minRight = nextMinRight; + nextMinRight = 1024; + + } else if ((stateRight == 1 ) && (lightRight < minRight + margin)) { + // make state = 0 when light below threshold and state == 1 + stateRight = 0; + + // we are now going to a maximum, reset the maximum value of the right sensor + maxRight = nextMaxRight; + nextMaxRight = 0; + + } + } + + + // print current status + if(currentMillis - previousPrint > printMetro) { + previousPrint = currentMillis; + // calculate new delay between now and the previous peak (prevMs) + double ms = millis(); + double tempDelay = ms - prevMs; + + // if 'tempDelay' is larger than the delay between the last two peaks 'len' it means that the disc is spinning slower and power usage is decreasing + // so max power consumption at this moment is not len, but tempDelay. + if ( len > tempDelay ) { + tempDelay = len; + } + + int watt = rotation * 4800000 / tempDelay; // my kwh meter says 600 rotations per kwh + + Serial.print(watt); + Serial.print(" | "); + Serial.print(rotation); + Serial.print(" | "); + Serial.print(lightLeft); + Serial.print(" | "); + Serial.println(lightRight); + } +} + diff --git a/kwhmeter.conf b/kwhmeter.conf new file mode 100644 index 0000000..037d3c1 --- /dev/null +++ b/kwhmeter.conf @@ -0,0 +1,38 @@ +[kWh Settings] +leftchannelid = 0 +rightchannelid = 2 + +[kWh] +importcounter = 1359430 +exportcounter = 1190820 +rotationsperkwh = 480 +minleft = 76 +maxleft = 722 +minmarginleft = 396 +maxmarginleft = 594 +minright = 92 +maxright = 807 +minmarginright = 447 +maxmarginright = 665 +cumuliday = 20150331 +importday = 2229 +exportday = 3077 + +[MySQL] +host = localhost +user = root +passwd = root +db = kwhRijnsraat214 + +[pvout] +pvout_enabled = true +pvout_apikey = b8795509823e9439f9d107c8f608c0dd7a96239f +pvout_sysid = 28910 + +[EmonCMS] +emoncmspath = emoncms +domain = 192.168.2.2 +apikey = 1e01f7ef0c6c83f4c2fe0bced49374ad +nodeid = 10 +emon_enable = true + diff --git a/kwhmeter_20131014.py b/kwhmeter_20131014.py new file mode 100644 index 0000000..fb12084 --- /dev/null +++ b/kwhmeter_20131014.py @@ -0,0 +1,554 @@ +# This Python script needs a SPI module. +# For more info, please see: +# http://www.100randomtasks.com/simple-spi-on-raspberry-pi +# +# This script also uses a "LoadAverage" module. This module can be downloaded from: +# http://patrick.i234.me/kwhmeter/LoadAverage.py +# +# This script is written for use on a Raspberri Pi, in combination with some +# custom-made hardware. +# The schematic of the hardware can be found here: +# https://www.circuitlab.com/circuit/eae95u/raspberry-pi-kwh-meter/ +# +# or an PDF can be found on my own site: +# http://patrick.i234.me/kwhmeter/raspberry-pi-kwh-meter.pdf +# +# +# + +# Unused modules, that I might incorporate later +#import rrdtool +#import os + +import MySQLdb + + +import spidev +import time +import RPi.GPIO as GPIO +import ConfigParser +# for the module LoadAverage, see: http://patrick.i234.me/kwhmeter/LoadAverage.py +import LoadAverage +import datetime +from datetime import date +# For PVoutput +import urllib, urllib2 + + +DEBUG = False + +# GPIO Pins for the different components: +switch = 23 +redLED = 17 +greenLED = 18 +boardLED = 22 + + +# Initialise SPI +spi = spidev.SpiDev() +spi.open(0, 0) + + +# read SPI data from MCP3008 chip, 8 possible adc's (0 thru 7) +def readadc(adcnum): + if ((adcnum > 7) or (adcnum < 0)): + return -1 + r = spi.xfer2([1, (8 + adcnum) << 4, 0]) + adcout = ((r[1] & 3) << 8) + r[2] + return adcout + + +def turnonLED(LED): + GPIO.output(LED, True) + + +def turnoffLED(LED): + GPIO.output(LED, False) + + +def toggleLED(LED): + GPIO.output(LED, GPIO.input(LED) ^ 1) + + +def writeLog(watt, total, importCount, exportCount, rotTime, loadaverage): + localtime = time.localtime(time.time()) + filename = '%s_kWh-meter.log' % time.strftime("%Y-%m-%d", localtime) + line = "%s\t%s\t%s\t%s\t%.3f\t%.3f\t%.3f\t%.0f\t%.3f\t%.3f\t%.3f\n" % ( + time.strftime("%Y-%m-%d %H:%M:00", localtime), time.strftime("%Y-%m-%d", localtime), + time.strftime("%H:%M:00", localtime), watt, total, importCount, exportCount, rotTime, loadaverage[0], + loadaverage[1], loadaverage[2]) + + logfile = open(filename, 'a') + try: + logfile.write(line) + finally: + logfile.close() + + logfile.close() + + +def writesettings(config): + with open('kwhmeter.conf', 'wb') as configfile: + config.write(configfile) + return True + + +def readsettings(): + config = ConfigParser.SafeConfigParser() + config.read('kwhmeter.conf') + + if not config.has_section('kWh Settings'): + config.add_section('kWh Settings') + if not config.has_option('kWh Settings', 'LeftChannelID'): + config.set('kWh Settings', 'LeftChannelID', '2') + if not config.has_option('kWh Settings', 'RightChannelID'): + config.set('kWh Settings', 'RightChannelID', '0') + + + # Section kWh data + if not config.has_section('kWh'): + config.add_section('kWh') + if not config.has_option('kWh', 'importCounter'): + config.set('kWh', 'importCounter', '0') + if not config.has_option('kWh', 'exportCounter'): + config.set('kWh', 'exportCounter', '0') + if not config.has_option('kWh', 'rotationsPerKWh'): + config.set('kWh', 'rotationsPerKWh', '600') + if not config.has_option('kWh', 'cumuliday'): + config.set('kWh', 'cumuliday', date.today().strftime("%Y%m%d")) + + # Add Pvoutput stuff + if not config.has_section('pvout'): + config.add_section('pvout') + if not config.has_option('pvout','pvout_enabled'): + config.set('pvout','pvout_enabled','false') + if not config.has_option('pvout','pvout_apikey'): + config.set('pvout','pvout_apikey','0') + if not config.has_option('pvout','pvout_sysid'): + config.set('pvout','pvout_sysid','0') + + # If you want to save in a MySQL DB, uncomment below: + # Section MySQL + #if not config.has_section('MySQL'): + # config.add_section('MySQL') + #if not config.has_option('MySQL', 'host'): + # config.set('MySQL', 'host', 'localhost') + #if not config.has_option('MySQL', 'user'): + # config.set('MySQL', 'user', 'root') + #if not config.has_option('MySQL', 'passwd'): + # config.set('MySQL', 'passwd', 'root') + #if not config.has_option('MySQL', 'db'): + # config.set('MySQL', 'db', 'kwhRijnsraat214') + + + return config + + +def savekwhdata(config, importCounter, exportCounter): + config.set('kWh', 'importCounter', "%.0f" % importCounter) + config.set('kWh', 'exportCounter', "%.0f" % exportCounter) + writesettings(config) + + +# If you want to save in a MySQL DB, uncomment below: +#def savetodb(config, watt, total, imported, exported): +# section = 'MySQL' +# localtime = time.localtime(time.time()) +# +# conn = MySQLdb.connect(host = config.get(section, 'host'), +# user = config.get(section, 'user'), +# passwd = config.get(section, 'passwd'), +# db = config.get(section, 'db')) +# cursor = conn.cursor() +# +# try: +# cursor.execute( +# "INSERT INTO `DayDatakWh` (`DateTime`, `CurrentPower`, `ETotalToday`, `EImportToday`, `EExportToday`, `CurrentUsage`, `EUsageToday`, `PVOutput`, `CHANGETIME`) VALUES ('%s', %s, %.3f, %.3f, %.3f, NULL, NULL, NULL, '0000-00-00 00:00:00');" % ( +# time.strftime("%Y-%m-%d %H:%M:00", localtime), watt, total, imported, exported)) +# conn.commit() +# except: +# conn.rollback() +# conn.close() +# +# return True + +def printinfo(line): + timeStr = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time())) + print "%s\t%s" % (timeStr, line) + #serialDev.writeLine("%s\t%s" % (timeStr, line)) + +def exportPvoutput(config, gentotal, genpower, contotal, conpower): + + pvout_enabled = config.getboolean('pvout','pvout_enabled') + pvout_apikey = config.get('pvout','pvout_apikey') + pvout_sysid = config.get('pvout','pvout_sysid') + + url = "http://pvoutput.org/service/r2/addstatus.jsp" + now = datetime.datetime.now() + + get_data = { + 'key': pvout_apikey, + 'sid': pvout_sysid, + 'd': now.strftime('%Y%m%d'), + 't': now.strftime('%H:%M'), + 'v1': gentotal * 1000, + 'v2': genpower, + 'v3': contotal * 1000, + 'v4': conpower, + 'c1': "1" + } + + + get_data_encoded = urllib.urlencode(get_data) # UrlEncode the parameters + printinfo(get_data_encoded) + request_object = urllib2.Request(url + '?' + get_data_encoded) # Create request object + try: + response = urllib2.urlopen(request_object) #make the request and store the response + printinfo("Pvoutput Exported") + return True + except urllib.error.HTTPError, e: + printinfo('HTTPError = ' + str(e.code)) + return False + except Exception: + import traceback + checksLogger.error('generic exception: ' + traceback.format_exc()) + + #http://pvoutput.org/service/r2/addstatus.jsp? + +def calibratesensors(config): + printinfo("Do calibration of sensors . . .") + sensorleftLValue = 9999 + sensorleftHValue = -1 + sensorrightLValue = 9999 + sensorrightHValue = -1 + + turnoffLED(boardLED) + turnoffLED(greenLED) + turnonLED(redLED) + + startTime = millis() + elapsedTime = millis() - startTime + startBlink = millis() + startInfo = millis() + + sensorleftDiff = 0 + sensorrightDiff = 0 + + while (elapsedTime < 30000) or (sensorleftDiff < 40) or (sensorrightDiff < 40): #or not GPIO.input(switch): + # run calibration for min. 10 seconds, + # and distance between low and high value must be at least 40 ticks + # or while the switch on the board is hold down (pressing the switch = 0, not pressing the switch = 1) + + sensordata = samplesensors(config) + sensorleftCurrent = sensordata['left'] + sensorrightCurrent = sensordata['right'] + + if sensorleftLValue > sensorleftCurrent: + sensorleftLValue = sensorleftCurrent + if sensorleftHValue < sensorleftCurrent: + sensorleftHValue = sensorleftCurrent + + if sensorrightLValue > sensorrightCurrent: + sensorrightLValue = sensorrightCurrent + if sensorrightHValue < sensorrightCurrent: + sensorrightHValue = sensorrightCurrent + + if DEBUG: + printinfo("Sensor left: %s\tSensor right: %s" % (sensorleftCurrent, sensorrightCurrent)) + + if millis() - startBlink > 100: + toggleLED(boardLED) + toggleLED(greenLED) + toggleLED(redLED) + startBlink = millis() + + + sensorleftDiff = sensorleftHValue - sensorleftLValue + sensorrightDiff = sensorrightHValue - sensorrightLValue + + if millis() - startInfo > 1000: + printinfo("Time %s: \tSL: %s (diff: %s) \t\tSR: %s (diff: %s)" % (elapsedTime, sensorleftCurrent, sensorleftDiff, sensorrightCurrent, sensorrightDiff)) + startInfo = millis() + + + time.sleep(0.1) + elapsedTime = millis() - startTime + + + sensorleftLTh = sensorleftLValue + ((sensorleftDiff / 10) * 5) # 50% above min + sensorleftHTh = sensorleftHValue - ((sensorleftDiff / 10) * 2) # 20% below max + sensorrightLTh = sensorrightLValue + ((sensorrightDiff / 10) * 5) # 50% above min + sensorrightHTh = sensorrightHValue - ((sensorrightDiff / 10) * 2) # 20% below max + + turnoffLED(boardLED) + turnoffLED(greenLED) + turnoffLED(redLED) + + printinfo("Calibration done!") + printinfo("Left low: %s" % sensorleftLValue) + printinfo(" high: %s" % sensorleftHValue) + printinfo(" diff: %s" % sensorleftDiff) + printinfo(" lthld.%s" % sensorleftLTh) + printinfo(" hthld.%s" % sensorleftHTh) + printinfo("Right low: %s" % sensorrightLValue) + printinfo(" high: %s" % sensorrightHValue) + printinfo(" diff: %s" % sensorrightDiff) + printinfo(" lthld.%s" % sensorrightLTh) + printinfo(" hthld.%s" % sensorrightHTh) + printinfo("") + + config.set('kWh', 'minleft', "%.0f" % sensorleftLValue) + config.set('kWh', 'maxleft', "%.0f" % sensorleftHValue) + config.set('kWh', 'minmarginleft', "%.0f" % sensorleftLTh) + config.set('kWh', 'maxmarginleft', "%.0f" % sensorleftHTh) + + config.set('kWh', 'minright', "%.0f" % sensorrightLValue) + config.set('kWh', 'maxright', "%.0f" % sensorrightHValue) + config.set('kWh', 'minmarginright', "%.0f" % sensorrightLTh) + config.set('kWh', 'maxmarginright', "%.0f" % sensorrightHTh) + + return config + + +def millis(): + return int(round(time.time() * 1000)) + + +def samplesensors(config): + # Sample the left and right sensor for maxsamples times (in order to get an average value) + leftid = config.getint('kWh Settings', 'LeftChannelID') + rightid = config.getint('kWh Settings', 'RightChannelID') + maxsamples = 20 + sensorleft = 0 + sensorright = 0 + + for loopCount in range(0, maxsamples): + sensorleft += readadc(leftid) + sensorright += readadc(rightid) + #print "%s | %s" % (sensorleft, sensorright) + sensorleft /= maxsamples + sensorright /= maxsamples + + return {'left': sensorleft, 'right': sensorright} + + +def dotFollower(config): + # This is the main part of the code, which will start to minitor the black dot on the disc + + # Do some initialisations + rotationsperkwh = config.getfloat("kWh", "rotationsPerKWh") * 1.00 + counterimport = config.getint("kWh", "importCounter") + counterexport = config.getint("kWh", "exportCounter") + countertotal = counterimport + counterexport + currentpower = 0 + timeperrotation = 0 + + left = False + right = False + leftdetected = False + rightdetected = False + lefttimestamp = time.time() + leftprevtimestamp = time.time() + righttimestamp = time.time() + rightprevtimestamp = time.time() + sensorinnertimestamp = time.time() + sensorinnerlength = 0 + sensoroutertimestamp = time.time() + sensoroutherlength = 0 + exportpower = 0 + importpower = 0 + + thisday = config.get("kWh", "cumuliday") + printinfo("Date: %s" % thisday) + + + leftmin = config.getint("kWh", "minmarginleft") + leftmax = config.getint("kWh", "maxmarginleft") + rightmin = config.getint("kWh", "minmarginright") + rightmax = config.getint("kWh", "maxmarginright") + + logexported = False + loadaverage = LoadAverage.Loadaverage() + loadaverageadded = False + + # Keep on running while the switch is NOT pressed + while 1: #GPIO.input(switch): + sensordata = samplesensors(config) + + # left sensor + if sensordata['left'] >= leftmax and not left: + # turn left to False + left = True + turnonLED(greenLED) + leftdetected = True + leftprevtimestamp = lefttimestamp + lefttimestamp = time.time() + #printinfo("Left sensor: . %.5f secs" % (lefttimestamp - leftprevtimestamp)) + if not rightdetected: + sensorinnertimestamp = time.time() + sensoroutherlength = time.time() - sensoroutertimestamp + else: + sensoroutertimestamp = time.time() + sensorinnerlength = time.time() - sensorinnertimestamp + + if sensordata['left'] <= leftmin and left: + # turn left to False + left = False + turnoffLED(greenLED) + #printinfo("Left off") + + + + # right sensor + if sensordata['right'] >= rightmax and not right: + # turn right to False + right = True + turnonLED(redLED) + rightdetected = True + rightprevtimestamp = righttimestamp + righttimestamp = time.time() + #printinfo("Right sensor:. %.5f secs" % (righttimestamp - rightprevtimestamp)) + if not leftdetected: + sensorinnertimestamp = time.time() + sensoroutherlength = time.time() - sensoroutertimestamp + else: + sensoroutertimestamp = time.time() + sensorinnerlength = time.time() - sensorinnertimestamp + + if sensordata['right'] <= rightmin and right: + # turn right to False + right = False + turnoffLED(redLED) + #printinfo("Right off") + + if not left and not right and leftdetected and rightdetected: + #rotation detected! + leftdetected = False + rightdetected = False + if sensorinnerlength < sensoroutherlength: # detection of left and right was correct + turnonLED(boardLED) + # Determine direction + if lefttimestamp < righttimestamp: + # The left sensor was seen before the right + # S1 --> S2 + # Rotation is "importing from the net" + rotation = 1 + counterimport += 1 + else: + # The right sensor was seen before the left + # S1 <-- S2 + # Rotation is "exporting to the net" + rotation = -1 + counterexport += 1 + countertotal = counterimport - counterexport + + # I'll average the left and right rotation time to get a "smoother" value (in principle I calculate the point between the 2 sensors) + timeperrotation = (((lefttimestamp - leftprevtimestamp) + (righttimestamp - rightprevtimestamp)) / 2) + currentpower = int(rotation * rotationsperkwh * 10 / (timeperrotation)) + + if rotation == 1: + printinfo("-> importing from net") + importpower = currentpower + exportpower = 0 + else: + printinfo("<- exporting to net") + exportpower = currentpower * -1 + importpower = 0 + #printinfo("Rotation time: . %.5f secs" % timeperrotation) + #printinfo("-- -- -- -- -- -- -- -- -- -- --") + #printinfo("Inner gab: . . . %.5f secs" % sensorinnerlength) + #printinfo("Outer: . . . . . %.5f secs" % sensoroutherlength) + #printinfo("Total: . . . . . %.5f secs" % (sensoroutherlength + sensorinnerlength)) + printinfo("Rotation time: . %.5f secs" % timeperrotation) + printinfo("Current power: . %.0f Watt" % currentpower) + #printinfo("ImportCount: . . %.0f" % counterimport) + #printinfo("ExportCount: . . %.0f" % counterexport) + printinfo("Import:. . . . . %.2f kWh" % (counterimport / rotationsperkwh)) + printinfo("Export:. . . . . %.2f kWh" % (counterexport / rotationsperkwh)) + printinfo("Total: . . . . . %.2f kWh" % (countertotal / rotationsperkwh)) + printinfo("Load average:. . %.0f W, %.0f W, %.0f W" % loadaverage.loadaverage()) + printinfo("--------------------------------------------------------------------------------") + + # do a tiny nap + time.sleep(0.1) + turnoffLED(boardLED) + else: + # Ooops... inner gab is bigger then the outer route??? We are detecting the wrong way! + # sleep a bit and try again + printinfo("Skipping rotation . . .") + if (sensoroutherlength / 4) < 10: + time.sleep(sensoroutherlength / 4) + else: + time.sleep(10) + else: + time.sleep(0.001) + + if time.gmtime()[5] % 10 == 0: + # Every 10 seconds print the current values + if not loadaverageadded: + loadaverage.addvalue(currentpower) + loadaverageadded = True + + if time.gmtime()[4] % 5 == 0: + # Every 5 minutes, write a log entry & DB record + if not logexported: + writeLog(currentpower, (countertotal / rotationsperkwh), (counterimport / rotationsperkwh), + (counterexport / rotationsperkwh), (timeperrotation * 1000), loadaverage.loadaverage()) + savekwhdata(config, counterimport, counterexport) + # If you want to save to pvoutput, uncomment the line below: + exportPvoutput(config, (counterexport / rotationsperkwh), exportpower, (counterimport / rotationsperkwh), importpower) + logexported = True + else: + logexported = False + else: + loadaverageadded = False + + if thisday != date.today().strftime("%Y%m%d"): + if logexported == True: + exportpower = 0 + importpower = 0 + thisday = date.today().strftime("%Y%m%d") + config.set('kWh', 'cumuliday',thisday) + + writesettings(config) + + +# Main program routine begins here: +#---------------------------------- + +config = readsettings() +writesettings(config) + +#init raspberry pi gpio pins +GPIO.setwarnings(False) +GPIO.setmode(GPIO.BCM) + +GPIO.setup(switch, GPIO.IN) +GPIO.setup(boardLED, GPIO.OUT, initial = GPIO.LOW) +GPIO.setup(greenLED, GPIO.OUT, initial = GPIO.LOW) +GPIO.setup(redLED, GPIO.OUT, initial = GPIO.LOW) + +#set LEDs default off +turnoffLED(boardLED) +turnoffLED(greenLED) +turnoffLED(redLED) + +writesettings(config) + +# Infinite loop +while True: + config = calibratesensors(config) + dotFollower(config) + +# we will never get here... +GPIO.remove_event_detect(switch) +GPIO.cleanup() + + + + + + + + +