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()