print 'Importing libraries... ', import urllib2, urllib from urllib2 import URLError from urllib2 import HTTPError import socket from socket import error as SocketError import errno import time from time import strftime from ABE_ADCPi import ADCPi from ABE_helpers import ABEHelpers from pymodbus.client.sync import ModbusSerialClient as ModbusClient startTime = time.time() fails = 0 counter = 0 loadAvg = 0.0 # Variables submitURL = 'http://www.solarpoweredhome.co.uk/submit.php' failFile = 'fails.log' inMin = 2.485 inMax = 2.7 outMin = 0.26 outMax = 3.63 intervals = ( ('weeks', 604800), # 60 * 60 * 24 * 7 ('days', 86400), # 60 * 60 * 24 ('hours', 3600), # 60 * 60 ('minutes', 60), ('seconds', 1), ) def display_time(seconds, granularity=4): result = [] for name, count in intervals: value = int(seconds // count) if value: seconds -= value * count if value == 1: name = name.rstrip('s') result.append("{} {}".format(value, name)) return ', '.join(result[:granularity]) def map(x, in_min, in_max, out_min, out_max): return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min def log(line): f = open(failFile,'a') f.write(line + '\n') f.close() print 'Done' # Initialise ADC print 'Initialising ADC... ', i2c_helper = ABEHelpers() bus = i2c_helper.get_smbus() adc = ADCPi(bus, 0x68, 0x69, 14) adc.set_conversion_mode(1) # Continuous conversion print ('Done') # Initialise the RS845 connection print 'Initialising RS485... ', client = ModbusClient(method = 'rtu', port = '/dev/ttyUSB0', baudrate = 115200) connection = client.connect() print ('Done') print ('Starting datalogger...') while 1: success = False processStart = time.time() lI = round(map(adc.read_voltage(6), inMin, inMax, outMin, outMax), 1) # Get the first load current reading loadAvg = loadAvg + lI result = client.read_input_registers(0x3100,6,unit=1) # Request the range of registers that hold the solar/battery realtime data (3100 - 3105) sV = float(result.registers[0] / 100.0) # Solar voltage is register 3100, divide by 100 sI = float(result.registers[1] / 100.0) # Solar current is register 3101, divide by 100 bV = float(result.registers[4] / 100.0) # Battery voltage is register 3104, divide by 100 bI = float(result.registers[5] / 100.0) # Charging current is register 3105, divide by 100 lI = round(map(adc.read_voltage(6), inMin, inMax, outMin, outMax), 1) # Get a second load current reading loadAvg = loadAvg + lI result = client.read_input_registers(0x311A,1,unit=1) # Request the register that holds the battery state of charge (311A) bS = result.registers[0] / 100 # Battery state of charge is register 311A, divide by 100 lI = round(map(adc.read_voltage(6), inMin, inMax, outMin, outMax), 1) # Get a final load current reading loadAvg = loadAvg + lI loadAvg = loadAvg/3 # Take a load current average # Ignore negative reading from current sensor if loadAvg < 0.0: loadAvg = 0.0 print 'sV: ' + str(sV) + ' | ' + 'sI: ' + str(sI) + ' | ' + 'bV: ' + str(bV) + ' | ' + 'bI: ' + str(bI) + ' | ' + 'bS: ' + str(bS) + '% | ' + 'lI: ' + format(loadAvg, '.2f') # Raspberry Pi uptime with open('/proc/uptime', 'r') as f: uptime_seconds = float(f.readline().split()[0]) systemUptime = display_time(uptime_seconds) # Program uptime upTime = time.time() - startTime upTime = display_time(upTime) # Submit the data with POST try: data = urllib.urlencode({'time' : strftime("%Y-%m-%d_%H:%M:%S"), 'sv' : str(sV), 'si' : str(sI), 'bv' : str(bV), 'bi' : str(bI), 'li' : format(loadAvg, '.2f'), 'bs' : str(bS), 'uptime' : upTime, 'piuptime' : systemUptime}) req = urllib2.Request(submitURL, data) response = urllib2.urlopen(req) submit = response.read() print submit, if 'Database updated' not in submit: log('Failed to submit values at ' + strftime("%Y-%m-%d %H:%M:%S") + ' due to PHP/MySQL error \n' + data) fails += 1 else: success = True except URLError as ue: print ue.reason, log('Failed to submit values at ' + strftime("%Y-%m-%d %H:%M:%S") + ' due to URL error: ' + str(ue.reason) + '\n' + data) fails += 1 except urllib2.HTTPError as he: print he.code, log('Failed to submit values at ' + strftime("%Y-%m-%d %H:%M:%S") + ' due to HTTP error: ' + str(he.code) + '\n' + data) fails += 1 except SocketError as se: print se.errno, log('Failed to submit values at ' + strftime("%Y-%m-%d %H:%M:%S") + ' due to SOCKET error: ' + str(se.errno) + '\n' + data) fails += 1 except socket.timeout as te: print te, log('Failed to submit values at ' + strftime("%Y-%m-%d %H:%M:%S") + ' due to SOCKET TIMEOUT: ' + str(te) + '\n' + data) fails += 1 loadAvg = 0.0 # Show pending fails, if any if (fails > 0): print ('Pending fails: ' + str(fails)) # Check fail log every minute and retry posting them counter += 1 if (counter > 7): counter = 0 if (success == True and fails > 0): # There are pending fails but the last post was a success, retrying failed posts print ('Checking fail log...') f = open('fails.log',"r+") d = f.readlines() f.seek(0) for i in d: if "si=" in i: # There is a line containing pending post data req = urllib2.Request(submitURL, i) response = urllib2.urlopen(req) submit = response.read() print submit if 'Database updated' not in submit: # Post failed, keep the line f.write(i) else: # Post successful, forget the line and decrement pending fails f.write('Done \n') fails -= 1 else: # There is a line with no post data (error info), leave it in f.write(i) f.truncate() f.close() # Slow things down when inverter is off if (loadAvg < 0.2): print '' print 'Sleeping...', time.sleep(50) # Calculate how long this cycle took processFinish = time.time(); processDuration = processFinish - processStart print ('(' + format(processDuration, '.2f') + ' seconds)') client.close()