|  |  | @ -127,46 +127,37 @@ def on_receive(p, ch_rx=None, ch_tx=None): | 
			
		
	
		
			
				
					|  |  |  |     # interpret content | 
			
		
	
		
			
				
					|  |  |  |     mid = p[0] | 
			
		
	
		
			
				
					|  |  |  |     d['mid'] = mid | 
			
		
	
		
			
				
					|  |  |  |     name = 'unknowndata' | 
			
		
	
		
			
				
					|  |  |  |     d['response_time_ns'] = t_now_ns-t_last_tx | 
			
		
	
		
			
				
					|  |  |  |     d['ch_rx'] = ch_rx | 
			
		
	
		
			
				
					|  |  |  |     d['ch_tx'] = ch_tx | 
			
		
	
		
			
				
					|  |  |  |     d['src'] = 'src_unkn' | 
			
		
	
		
			
				
					|  |  |  |     d['name'] = 'name_unkn' | 
			
		
	
		
			
				
					|  |  |  |   | 
			
		
	
		
			
				
					|  |  |  |     if mid == 0x95: | 
			
		
	
		
			
				
					|  |  |  |         src, dst, cmd = struct.unpack('>LLB', p[1:10]) | 
			
		
	
		
			
				
					|  |  |  |         src_s = f'{src:08x}' | 
			
		
	
		
			
				
					|  |  |  |         dst_s = f'{dst:08x}' | 
			
		
	
		
			
				
					|  |  |  |         d['src'] = src_s | 
			
		
	
		
			
				
					|  |  |  |         d['dst'] = dst_s | 
			
		
	
		
			
				
					|  |  |  |         d['src'] = f'{src:08x}' | 
			
		
	
		
			
				
					|  |  |  |         d['dst'] = f'{dst:08x}' | 
			
		
	
		
			
				
					|  |  |  |         d['cmd'] = cmd | 
			
		
	
		
			
				
					|  |  |  |         print(f'MSG src={src_s}, dst={dst_s}, cmd={cmd},  ', end=' ') | 
			
		
	
		
			
				
					|  |  |  |         print(f'MSG src={d["src"]}, dst={d["dst"]}, cmd={d["cmd"]}:') | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |         if cmd==1: | 
			
		
	
		
			
				
					|  |  |  |             name = 'dcdata' | 
			
		
	
		
			
				
					|  |  |  |             d['name'] = 'dcdata' | 
			
		
	
		
			
				
					|  |  |  |             unknown1, u1, i1, p1, u2, i2, p2, unknown2 = struct.unpack( | 
			
		
	
		
			
				
					|  |  |  |                 '>HHHHHHHH', p[10:26]) | 
			
		
	
		
			
				
					|  |  |  |             print(f'u1={u1/10}V, i1={i1/100}A, p1={p1/10}W,  ', end='') | 
			
		
	
		
			
				
					|  |  |  |             print(f'u2={u2/10}V, i2={i2/100}A, p2={p2/10}W,  ', end='') | 
			
		
	
		
			
				
					|  |  |  |             print(f'unknown1={unknown1}, unknown2={unknown2}') | 
			
		
	
		
			
				
					|  |  |  |             d['u1_V'] = u1/10 | 
			
		
	
		
			
				
					|  |  |  |             d['i1_A'] = i1/100 | 
			
		
	
		
			
				
					|  |  |  |             d['p1_W'] = p1/10 | 
			
		
	
		
			
				
					|  |  |  |             d['u2_V'] = u2/10 | 
			
		
	
		
			
				
					|  |  |  |             d['i2_A'] = i2/100 | 
			
		
	
		
			
				
					|  |  |  |             d['p2_W'] = p2/10 | 
			
		
	
		
			
				
					|  |  |  |             d['p_W'] = d['p1_W']+d['p2_W'] | 
			
		
	
		
			
				
					|  |  |  |             d['unknown1'] = unknown1 | 
			
		
	
		
			
				
					|  |  |  |             d['unknown2'] = unknown2 | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |         elif cmd==2: | 
			
		
	
		
			
				
					|  |  |  |             name = 'acdata' | 
			
		
	
		
			
				
					|  |  |  |             d['name'] = 'acdata' | 
			
		
	
		
			
				
					|  |  |  |             uk1, uk2, uk3, uk4, uk5, u, f, p = struct.unpack( | 
			
		
	
		
			
				
					|  |  |  |                 '>HHHHHHHH', p[10:26]) | 
			
		
	
		
			
				
					|  |  |  |             print(f'u={u/10:.1f}V, f={f/100:.2f}Hz, p={p/10:.1f}W,  ', end='') | 
			
		
	
		
			
				
					|  |  |  |             print(f'uk1={uk1}, ', end='') | 
			
		
	
		
			
				
					|  |  |  |             print(f'uk2={uk2}, ', end='') | 
			
		
	
		
			
				
					|  |  |  |             print(f'uk3={uk3}, ', end='') | 
			
		
	
		
			
				
					|  |  |  |             print(f'uk4={uk4}, ', end='') | 
			
		
	
		
			
				
					|  |  |  |             print(f'uk5={uk5}') | 
			
		
	
		
			
				
					|  |  |  |             d['u_V'] = u/10 | 
			
		
	
		
			
				
					|  |  |  |             d['f_Hz'] = f/100 | 
			
		
	
		
			
				
					|  |  |  |             d['p_W'] = p/10 | 
			
		
	
	
		
			
				
					|  |  | @ -177,17 +168,11 @@ def on_receive(p, ch_rx=None, ch_tx=None): | 
			
		
	
		
			
				
					|  |  |  |             d['uk2'] = uk2 | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |         elif cmd==129: | 
			
		
	
		
			
				
					|  |  |  |             name = 'error' | 
			
		
	
		
			
				
					|  |  |  |             print('Command error') | 
			
		
	
		
			
				
					|  |  |  |             d['name'] = 'error' | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |         elif cmd==131:  # 0x83 | 
			
		
	
		
			
				
					|  |  |  |             name = 'statedata' | 
			
		
	
		
			
				
					|  |  |  |             d['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 | 
			
		
	
		
			
				
					|  |  |  |             d['uk1'] = uk1 | 
			
		
	
	
		
			
				
					|  |  | @ -196,17 +181,17 @@ def on_receive(p, ch_rx=None, ch_tx=None): | 
			
		
	
		
			
				
					|  |  |  |             d['uk6'] = uk6 | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |         elif cmd==132:  # 0x84 | 
			
		
	
		
			
				
					|  |  |  |             name = 'unknown0x84' | 
			
		
	
		
			
				
					|  |  |  |             d['name'] = 'unknown0x84' | 
			
		
	
		
			
				
					|  |  |  |             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}') | 
			
		
	
		
			
				
					|  |  |  |             d['uk1'] = uk1 | 
			
		
	
		
			
				
					|  |  |  |             d['uk2'] = uk2 | 
			
		
	
		
			
				
					|  |  |  |             d['uk3'] = uk3 | 
			
		
	
		
			
				
					|  |  |  |             d['uk4'] = uk4 | 
			
		
	
		
			
				
					|  |  |  |             d['uk5'] = uk5 | 
			
		
	
		
			
				
					|  |  |  |             d['uk6'] = uk6 | 
			
		
	
		
			
				
					|  |  |  |             d['uk7'] = uk7 | 
			
		
	
		
			
				
					|  |  |  |             d['uk8'] = uk8 | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |         else: | 
			
		
	
		
			
				
					|  |  |  |             print(f'unknown cmd {cmd}') | 
			
		
	
	
		
			
				
					|  |  | @ -220,21 +205,21 @@ def on_receive(p, ch_rx=None, ch_tx=None): | 
			
		
	
		
			
				
					|  |  |  |     # output to MQTT | 
			
		
	
		
			
				
					|  |  |  |     if d: | 
			
		
	
		
			
				
					|  |  |  |         j = json.dumps(d) | 
			
		
	
		
			
				
					|  |  |  |         mqtt_client.publish(f'ahoy/{src}/{name}', j) | 
			
		
	
		
			
				
					|  |  |  |         mqtt_client.publish(f"ahoy/{d['src']}/{d['name']}", j) | 
			
		
	
		
			
				
					|  |  |  |         if d['cmd']==2: | 
			
		
	
		
			
				
					|  |  |  |             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']) | 
			
		
	
		
			
				
					|  |  |  |             mqtt_client.publish(f'ahoy/{d["src"]}/emeter/0/voltage', d['u_V']) | 
			
		
	
		
			
				
					|  |  |  |             mqtt_client.publish(f'ahoy/{d["src"]}/emeter/0/power', d['p_W']) | 
			
		
	
		
			
				
					|  |  |  |             mqtt_client.publish(f'ahoy/{d["src"]}/emeter/0/total', d['wtot1_Wh']) | 
			
		
	
		
			
				
					|  |  |  |             mqtt_client.publish(f'ahoy/{d["src"]}/frequency', d['f_Hz']) | 
			
		
	
		
			
				
					|  |  |  |         if d['cmd']==1: | 
			
		
	
		
			
				
					|  |  |  |             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']) | 
			
		
	
		
			
				
					|  |  |  |             mqtt_client.publish(f'ahoy/{d["src"]}/emeter-dc/0/power', d['p1_W']) | 
			
		
	
		
			
				
					|  |  |  |             mqtt_client.publish(f'ahoy/{d["src"]}/emeter-dc/0/voltage', d['u1_V']) | 
			
		
	
		
			
				
					|  |  |  |             mqtt_client.publish(f'ahoy/{d["src"]}/emeter-dc/0/current', d['i1_A']) | 
			
		
	
		
			
				
					|  |  |  |             mqtt_client.publish(f'ahoy/{d["src"]}/emeter-dc/1/power', d['p2_W']) | 
			
		
	
		
			
				
					|  |  |  |             mqtt_client.publish(f'ahoy/{d["src"]}/emeter-dc/1/voltage', d['u2_V']) | 
			
		
	
		
			
				
					|  |  |  |             mqtt_client.publish(f'ahoy/{d["src"]}/emeter-dc/1/current', d['i2_A']) | 
			
		
	
		
			
				
					|  |  |  |         if d['cmd']==131: | 
			
		
	
		
			
				
					|  |  |  |             mqtt_client.publish(f'ahoy/{src}/temperature', d['t_C']) | 
			
		
	
		
			
				
					|  |  |  |             mqtt_client.publish(f'ahoy/{d["src"]}/temperature', d['t_C']) | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
	
		
			
				
					|  |  | @ -269,7 +254,7 @@ def main_loop(): | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |         radio.setChannel(rx_channel) | 
			
		
	
		
			
				
					|  |  |  |         radio.enableDynamicPayloads() | 
			
		
	
		
			
				
					|  |  |  |         radio.setAutoAck(True) | 
			
		
	
		
			
				
					|  |  |  |         radio.setAutoAck(False) | 
			
		
	
		
			
				
					|  |  |  |         radio.setPALevel(RF24_PA_MAX) | 
			
		
	
		
			
				
					|  |  |  |         radio.setDataRate(RF24_250KBPS) | 
			
		
	
		
			
				
					|  |  |  |         radio.openWritingPipe(ser_to_esb_addr(inv_ser)) | 
			
		
	
	
		
			
				
					|  |  | @ -278,35 +263,14 @@ def main_loop(): | 
			
		
	
		
			
				
					|  |  |  |         radio.openReadingPipe(1,ser_to_esb_addr(dtu_ser)) | 
			
		
	
		
			
				
					|  |  |  |         radio.startListening() | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |         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(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, ch_rx=rx_channel, ch_tx=tx_channel) | 
			
		
	
		
			
				
					|  |  |  |             else: | 
			
		
	
		
			
				
					|  |  |  |                 # pass | 
			
		
	
		
			
				
					|  |  |  |                 # time.sleep(0.01) | 
			
		
	
		
			
				
					|  |  |  |                 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] | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |         # | 
			
		
	
		
			
				
					|  |  |  |         # TX | 
			
		
	
		
			
				
					|  |  |  |         # | 
			
		
	
		
			
				
					|  |  |  |         radio.stopListening()  # put radio in TX mode | 
			
		
	
		
			
				
					|  |  |  |         radio.setChannel(tx_channel) | 
			
		
	
		
			
				
					|  |  |  |         radio.openWritingPipe(ser_to_esb_addr(inv_ser)) | 
			
		
	
	
		
			
				
					|  |  | @ -315,13 +279,47 @@ def main_loop(): | 
			
		
	
		
			
				
					|  |  |  |         payload = compose_0x80_msg(src_ser_no=dtu_ser, dst_ser_no=inv_ser, ts=ts) | 
			
		
	
		
			
				
					|  |  |  |         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 | 
			
		
	
		
			
				
					|  |  |  |             " ".join([f"{b:02x}" for b in payload]) + f" rx_ch: {rx_channel}" | 
			
		
	
		
			
				
					|  |  |  |         print(last_tx_message) | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |         # for i in range(0,3): | 
			
		
	
		
			
				
					|  |  |  |         result = radio.write(payload)  # will always yield 'True' because auto-ack is disabled | 
			
		
	
		
			
				
					|  |  |  |         #    time.sleep(.05) | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |         t_last_tx = time.monotonic_ns() | 
			
		
	
		
			
				
					|  |  |  |         ctr = ctr + 1 | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |         print(flush=True, end='') | 
			
		
	
		
			
				
					|  |  |  |         t_end = time.monotonic_ns()+5e9 | 
			
		
	
		
			
				
					|  |  |  |         tslots = [1000]  #, 40, 50, 60, 70]  # switch channel at these ms times since transmission | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |         for tslot in tslots: | 
			
		
	
		
			
				
					|  |  |  |             t_end = t_last_tx + tslot*1e6  # ms to ns | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |             radio.stopListening() | 
			
		
	
		
			
				
					|  |  |  |             radio.setChannel(rx_channel) | 
			
		
	
		
			
				
					|  |  |  |             radio.startListening() | 
			
		
	
		
			
				
					|  |  |  |             while time.monotonic_ns() < t_end: | 
			
		
	
		
			
				
					|  |  |  |                 has_payload, pipe_number = radio.available_pipe() | 
			
		
	
		
			
				
					|  |  |  |                 if has_payload: | 
			
		
	
		
			
				
					|  |  |  |                     size = radio.getDynamicPayloadSize() | 
			
		
	
		
			
				
					|  |  |  |                     payload = radio.read(size) | 
			
		
	
		
			
				
					|  |  |  |                     # 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, ch_rx=rx_channel, ch_tx=tx_channel) | 
			
		
	
		
			
				
					|  |  |  |                 else: | 
			
		
	
		
			
				
					|  |  |  |                     pass | 
			
		
	
		
			
				
					|  |  |  |                     # time.sleep(0.001) | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |             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] | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |         print(flush=True, end='') | 
			
		
	
		
			
				
					|  |  |  |         # time.sleep(2) | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
	
		
			
				
					|  |  | 
 |