diff --git a/tools/rpi/ahoy.conf.example b/tools/rpi/ahoy.conf.example new file mode 100644 index 00000000..77338d00 --- /dev/null +++ b/tools/rpi/ahoy.conf.example @@ -0,0 +1,9 @@ +[mqtt] +host = 192.168.84.2 +port = 1883 + +[dtu] +serial = 99978563412 + +[inverter] +serial = 444473104619 diff --git a/tools/rpi/ahoy.py b/tools/rpi/ahoy.py index 74f4b23b..51210c5b 100644 --- a/tools/rpi/ahoy.py +++ b/tools/rpi/ahoy.py @@ -24,11 +24,10 @@ mqtt_client = paho.mqtt.client.Client() mqtt_client.connect(mqtt_host, mqtt_port) # Master Address ('DTU') -dtu_ser = 99978563412 # identical to fc22's +dtu_ser = cfg.get('dtu', 'serial', fallback='99978563412') # identical to fc22's # inverter serial numbers -#inv_ser = 444473104619 # identical to fc22's #99972220200 -inv_ser = 114174608145 # my inverter +inv_ser = cfg.get('inverter', 'serial', fallback='444473104619') # my inverter # all inverters #... @@ -163,6 +162,34 @@ def on_receive(p): d['wday2_Wh'] = uk5 d['uk2'] = uk2 + elif cmd==129: + name = 'error' + print('Command error') + + elif cmd==131: + name = 'statedata' + uk1, l, uk3, t, uk5, uk6 = struct.unpack('>HHHHHH', p[10:22]) + print(f'l={l}%, t={t/10:.2f}C, ', end='') + print(f'uk1={uk1}, ', end='') + print(f'uk3={uk3}, ', end='') + print(f'uk5={uk5}, ', end='') + print(f'uk6={uk6}') + d['l_Pct'] = l + d['t_C'] = t/10 + + elif cmd==132: + name = 'unknown' + uk1, uk2, uk3, uk4, uk5, uk6, uk7, uk8 = struct.unpack( + '>HHHHHHHH', p[10:26]) + print(f'uk1={uk1}, ', end='') + print(f'uk2={uk2}, ', end='') + print(f'uk3={uk3}, ', end='') + print(f'uk4={uk4}, ', end='') + print(f'uk5={uk5}, ', end='') + print(f'uk6={uk6}, ', end='') + print(f'uk7={uk7}, ', end='') + print(f'uk8={uk8}') + else: print(f'unknown cmd {cmd}') else: @@ -173,11 +200,20 @@ def on_receive(p): j = json.dumps(d) mqtt_client.publish(f'ahoy/{src}/{name}', j) if d['cmd']==2: - mqtt_client.publish(f'ahoy/{src}/{name}/p_W', d['p_W']) + mqtt_client.publish(f'ahoy/{src}/emeter/0/voltage', d['u_V']) + mqtt_client.publish(f'ahoy/{src}/emeter/0/power', d['p_W']) + mqtt_client.publish(f'ahoy/{src}/emeter/0/total', d['wtot1_Wh']) + mqtt_client.publish(f'ahoy/{src}/frequency', d['f_Hz']) if d['cmd']==1: - mqtt_client.publish(f'ahoy/{src}/{name}/p1_W', d['p1_W']) - mqtt_client.publish(f'ahoy/{src}/{name}/p2_W', d['p2_W']) - mqtt_client.publish(f'ahoy/{src}/{name}/p_W', d['p1_W']+d['p2_W']) + mqtt_client.publish(f'ahoy/{src}/emeter-dc/0/power', d['p1_W']) + mqtt_client.publish(f'ahoy/{src}/emeter-dc/0/voltage', d['u1_V']) + mqtt_client.publish(f'ahoy/{src}/emeter-dc/0/current', d['i1_A']) + mqtt_client.publish(f'ahoy/{src}/emeter-dc/1/power', d['p2_W']) + mqtt_client.publish(f'ahoy/{src}/emeter-dc/1/voltage', d['u2_V']) + mqtt_client.publish(f'ahoy/{src}/emeter-dc/1/current', d['i2_A']) + if d['cmd']==131: + mqtt_client.publish(f'ahoy/{src}/temperature', d['t_C']) + def main_loop(): @@ -190,53 +226,73 @@ def main_loop(): print_addr(dtu_ser) ctr = 1 + last_tx_message = '' - ts = int(time.time()) # see what happens if we always send one and the same (constant) time! + rx_channels = [3,23,61,75] + rx_channel_id = 0 + rx_channel = rx_channels[rx_channel_id] + + tx_channels = [40] + tx_channel_id = 0 + tx_channel = tx_channels[tx_channel_id] while True: - radio.setChannel(3) + # Sweep receive start channel + rx_channel_id = ctr % len(rx_channels) + rx_channel = rx_channels[rx_channel_id] + + radio.setChannel(rx_channel) radio.enableDynamicPayloads() - radio.setAutoAck(False) + radio.setAutoAck(True) radio.setPALevel(RF24_PA_MAX) radio.setDataRate(RF24_250KBPS) radio.openWritingPipe(ser_to_esb_addr(inv_ser)) radio.flush_rx() radio.flush_tx() radio.openReadingPipe(1,ser_to_esb_addr(dtu_ser)) - #radio.openReadingPipe(1,ser_to_esb_addr(inv_ser)) radio.startListening() - if ctr<3: - pass - # radio.printPrettyDetails() - t_end = time.monotonic_ns()+1e9 while time.monotonic_ns() < t_end: has_payload, pipe_number = radio.available_pipe() if has_payload: size = radio.getDynamicPayloadSize() payload = radio.read(size) - print(f"Received {size} bytes on pipe {pipe_number}: " + + print(last_tx_message, end='') + last_tx_message = '' + dt = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f") + print(f"{dt} Received {size} bytes on channel {rx_channel} pipe {pipe_number}: " + " ".join([f"{b:02x}" for b in payload])) on_receive(payload) else: + radio.stopListening() + radio.setChannel(rx_channel) + radio.startListening() + rx_channel_id = rx_channel_id + 1 + if rx_channel_id >= len(rx_channels): + rx_channel_id = 0 + rx_channel = rx_channels[rx_channel_id] time.sleep(0.01) + tx_channel_id = tx_channel_id + 1 + if tx_channel_id >= len(tx_channels): + tx_channel_id = 0 + tx_channel = tx_channels[tx_channel_id] + radio.stopListening() # put radio in TX mode - radio.setChannel(40) + radio.setChannel(tx_channel) radio.openWritingPipe(ser_to_esb_addr(inv_ser)) - if ctr<3: - pass - # radio.printPrettyDetails() - - # ts = int(time.time()) + ts = int(time.time()) payload = compose_0x80_msg(src_ser_no=dtu_ser, dst_ser_no=inv_ser, ts=ts) - print(f"{ctr:5d}: len={len(payload)} | " + " ".join([f"{b:02x}" for b in payload]), - flush=True) + dt = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f") + last_tx_message = f"{dt} Transmit {ctr:5d}: channel={tx_channel} len={len(payload)} | " + \ + " ".join([f"{b:02x}" for b in payload]) + "\n" radio.write(payload) # will always yield 'True' because auto-ack is disabled ctr = ctr + 1 + print(flush=True, end='') + diff --git a/tools/rpi/requirements.txt b/tools/rpi/requirements.txt index 8579e8b2..a26b1266 100644 --- a/tools/rpi/requirements.txt +++ b/tools/rpi/requirements.txt @@ -1 +1,2 @@ paho-mqtt +crcmod