summaryrefslogtreecommitdiff
path: root/inverter0/main.py
diff options
context:
space:
mode:
Diffstat (limited to 'inverter0/main.py')
-rw-r--r--inverter0/main.py243
1 files changed, 243 insertions, 0 deletions
diff --git a/inverter0/main.py b/inverter0/main.py
new file mode 100644
index 0000000..3dfe996
--- /dev/null
+++ b/inverter0/main.py
@@ -0,0 +1,243 @@
+import machine
+
+# RS-485 / comlynx
+u1 = machine.UART(1)
+u1.init(19200)
+
+# RS-485 Driver Enable flag
+de = machine.Pin(32, machine.Pin.OUT)
+de.value(0) # disable driver
+
+# Neoway M590
+u2 = machine.UART(2)
+u2.init(115200)
+
+
+import comlynx
+import time
+
+SOURCE_ADDRESS = comlynx.Address(0x01, 0x01, 0x02)
+INVERTER_ADDRESS = comlynx.Address(0x01, 0x01, 0x3b) #3d
+
+def write(data):
+ try:
+ de.value(1) # enable write
+ u1.write(data)
+ time.sleep_ms(int(len(data))) # very rough estimation
+ finally:
+ de.value(0) # disable write
+
+
+ping = comlynx.PingRequest(comlynx.Address(0,0,2), comlynx.Address.broadcast())
+
+def communicate(message):
+ data = bytes(tuple(message))
+ print("Send: {} => {}".format(message, data))
+ u1.read() # empty buf
+ write(data)
+ request = u1.read()
+ if request != data:
+ raise Exception("Unexpected comm error")
+ time.sleep_ms(100)
+ response = u1.read()
+ print("Received: {} => ".format(response), end='')
+ msg = comlynx.Message.unpack(response)
+ print(str(msg))
+ return msg
+
+class Point:
+ def __init__(self, name):
+ self.name = name
+ self.min = None
+ self.max = None
+ self.last = None
+ def update(self, value):
+ self.min = min(self.min or value, value)
+ self.max = max(self.max or value, value)
+ self.last = value
+ def clear(self):
+ self.min = self.max = self.last = None
+
+
+values = list(map(Point, [
+ 'PVVoltage1', 'PVVoltage2', 'PVVoltage3',
+ 'PVCurrent1', 'PVCurrent2', 'PVCurrent3',
+ 'GridVoltage', 'GridFrequency', 'LatestEvent',
+ 'LatestEventModule', 'SmoothedInstantEnergyProduction']))
+error = None
+
+def update():
+ global error
+ try:
+ for point in values:
+ print("Update: {}".format(point.name))
+ attrs = getattr(comlynx.Parameters.ULX, point.name)
+ message = comlynx.CanRequest(SOURCE_ADDRESS, INVERTER_ADDRESS, *attrs)
+ reply = communicate(message)
+ point.update(reply.value())
+ error = None
+ except Exception as e:
+ print("Error: {}".format(e))
+ error = str(e)
+
+def reset():
+ print("Error detected, reset in 30s")
+ time.sleep(30)
+ print("Reset SIM now")
+ u2.write("AT+CFUN=15\r\n"); time.sleep(1); print(u2.read())
+ machine.reset()
+
+
+def sim_command(command, expect):
+ while u2.any():
+ data = u2.readline().strip()
+ print(data)
+
+ print("> {}".format(command))
+ u2.write(command + b"\r\n")
+
+ data = u2.readline()
+ start = time.time()
+ while time.time() - start < 5:
+ if data is not None:
+ data = data.strip()
+ print("< {}".format(data))
+ if data.startswith(expect):
+ break
+ elif data == b"ERROR":
+ reset()
+ data = u2.readline()
+
+ip_address = None
+
+def wait_connection():
+ is_connected = False
+ start = time.time()
+ while not is_connected:
+ if time.time() - start > 60:
+ reset()
+ u2.write(b"AT+XIIC?\r\n")
+ time.sleep_ms(500)
+ while u2.any() > 0:
+ line = u2.readline()
+ print("< {}".format(line))
+ if line.startswith("+XIIC: 1"):
+ print("PPP connection established")
+ is_connected = True
+
+def sim_connect():
+ global ip_address
+ # Connect to cellular network
+ sim_command(b"AT+CREG=1", b"OK")
+ is_registered = False
+ start = time.time()
+ while not is_registered:
+ if time.time() - start > 120:
+ reset()
+ u2.write(b"AT+CREG?\r\n")
+ time.sleep_ms(500)
+ while u2.any() > 0:
+ line = u2.readline()
+ print("< {}".format(line))
+ if line.startswith("+CREG: 1,1") or line.startswith("+CREG: 1,5") or \
+ line.startswith("+CREG: 0,1") or line.startswith("+CREG: 0,5"):
+ print("Cell network status: registered")
+ is_registered = True
+
+ sim_command(b"AT+XISP=0", b"OK")
+ sim_command(b"AT+CGDCONT=1,\"IP\",\"TM\"", b"OK")
+ sim_command(b"AT+XIIC=1", b"OK")
+
+ wait_connection()
+
+ sim_command(b"AT", b"OK")
+ time.sleep(1)
+
+ start = time.time()
+ while ip_address is None:
+ if time.time() - start > 60:
+ reset()
+ u2.write(b"AT+DNS=\"www.localnet.cc\"\r\n")
+ time.sleep(1)
+ while u2.any() > 0:
+ line = u2.readline().strip()
+ if line.startswith(b"+DNS:") and len(line) > 8:
+ ip_address = line[5:]
+
+ print("Connect done")
+
+def tcp_send(data):
+ CHUNK_SIZE = 512
+ wait_connection()
+ sim_command(b"AT+TCPSETUP=0," + ip_address + b",80", b"+TCPSETUP")
+ try:
+ chunks = [data[i:i+CHUNK_SIZE] for i in range(0, len(data), CHUNK_SIZE)]
+ for chunk in chunks:
+ u2.write(b"AT+TCPSEND=0," + str(len(chunk)).encode('ascii') + b"\r\n")
+ start = time.time()
+ while True:
+ if time.time() - start > 10:
+ print("Timeout waiting for tcpsend")
+ reset()
+ line = u2.readline()
+ print("< {}".format(line))
+ if line is not None and line.startswith('>'):
+ break
+ elif line is not None and line.startswith('+TCPSEND:Error'):
+ raise Exception("TCPSEND failed: {}".format(line))
+ else:
+ time.sleep_ms(200)
+
+ u2.write(chunk)
+ u2.write(b"\r")
+ time.sleep(1)
+ print("< {}".format(u2.read()))
+ finally:
+ sim_command(b"AT+TCPCLOSE=0", b"+TCPCLOSE")
+
+def post(data):
+ global config
+ secret = config['CONFIG_GUARD_SECRET']
+ req = 'POST /guard/write/{}?db=data HTTP/1.1\r\nContent-Type: application/x-www-urlencoded\r\nContent-Length: {}\r\nHost: localnet.cc\r\n\r\n{}'.format(
+ secret, len(data), data)
+ tcp_send(req.encode('utf-8'))
+
+def send_data(post_func):
+ global error
+ data = ""
+ if error is not None:
+ data += "inverter0.error value=\"{}\"".format(error.replace('"', '_'))
+ else:
+ for point in values:
+ data += "inverter0.{} min={},max={},last={}\n".format(
+ point.name, point.min, point.max, point.last)
+ point.clear()
+ post_func(data)
+ error = None
+
+def loop():
+ last_post = time.time() - 590
+ try:
+ while True:
+ update()
+ for point in values:
+ print("{}: last={} min={} max={}".format(point.name, point.last, point.min, point.max))
+
+ if time.time() - last_post > 600:
+ try:
+ send_data(post)
+ last_post = time.time()
+ except Exception as e:
+ print(e)
+
+ print("Time to next send: {}".format(time.time() - last_post * -1))
+ time.sleep(30)
+
+ except Exception as e:
+ print(str(e))
+ reset()
+
+print("main.py: Start loop in 5s")
+time.sleep(5)
+sim_connect()
+loop()