diff --git a/software/archive/backup nonworkign code/am1.py b/software/archive/backup nonworkign code/am1.py new file mode 100644 index 0000000..afe0668 --- /dev/null +++ b/software/archive/backup nonworkign code/am1.py @@ -0,0 +1,46 @@ +import time +import gc +import machine +from machine import Pin, Timer + +print("Running am1.py") + +try: + networking +except NameError: + print("Trying to load networking from config.py") + from config import networking + if networking is None: + print("Error loading networking, reseting machine") + machine.reset() + +peer_mac = b'\xff\xff\xff\xff\xff\xff' # Replace with actual MAC +lastPressed = 0 +message = "Boop!" + +networking.ping(peer_mac) + +def boop(pin): + global lastPressed + if (time.ticks_ms() - lastPressed > 1000): + lastPressed = time.ticks_ms() + networking.ping(peer_mac) + # networking.echo(peer_mac, message) + # networking.send(peer_mac, time.ticks_ms()) + print(f"{(time.ticks_ms() - networking.inittime) / 1000:.3f} Networking Tool: Sent {message} to {peer_mac}") + # print(f"{(time.ticks_ms() - networking.inittime) / 1000:.3f} Networking Tool: RSSI table: {networking.rssi()}") + +# Button interrupt +switch_select = Pin(9, Pin.IN, Pin.PULL_UP) +switch_select.irq(trigger=Pin.IRQ_FALLING, handler=boop) + +def heartbeat(timer): + print("") + boop(None) + print(f"{(time.ticks_ms() - networking.inittime) / 1000:.3f} Networking Tool Heartbeat: {gc.mem_free()} bytes") + print("") + gc.collect() + +# Periodic heartbeat timer (every 10 s) +timer = Timer(0) +timer.init(period=10000, mode=Timer.PERIODIC, callback=heartbeat) diff --git a/software/archive/backup nonworkign code/boot.py b/software/archive/backup nonworkign code/boot.py new file mode 100644 index 0000000..5dd8fd0 --- /dev/null +++ b/software/archive/backup nonworkign code/boot.py @@ -0,0 +1 @@ +#empty \ No newline at end of file diff --git a/software/archive/backup nonworkign code/config.py b/software/archive/backup nonworkign code/config.py new file mode 100644 index 0000000..8c209e0 --- /dev/null +++ b/software/archive/backup nonworkign code/config.py @@ -0,0 +1,83 @@ +config = {'name': 'Nick', 'ap_channel': 1, 'sta_mac': '64:e8:33:85:d3:bc', 'ap_mac': '64:e8:33:85:d3:bd', 'version': '2025-10-12-175607', 'id': '64e83385d3bc', 'configuration': 'AM1', 'sta_channel': 1, 'type': 'ESP32C3 module with ESP32C3'} +version = { + 'adxl345.py': 3, + 'am1.py': 1, + 'hm1.py': 1, + 'ssp_networking.py': 4, + 'files.py': 2, + 'icons.py': 2, + 'prefs.py': 2, + 'sensors.py': 4, + 'servo.py': 2, + 'ssd1306.py': 2, + 'sm3.py': 1, + 'sl1.py': 1, + 'smartlight.py': 1, + 'networking.py': 6, + 'main.py': 2, + 'boot.py': 0 +} +mysecrets = {"SSID": "Tufts_Robot", "key": ""} +msg_codes = {"cmd": 0x01, "inf": 0x02, "ack": 0x03} +msg_subcodes = { + "cmd": { + "Reboot": 0x00, + "Firmware-Update": 0x01, + "File-Update": 0x02, + "File-Download": 0x03, + "File-Run": 0x05, + "Set-Admin": 0x06, + "Whitelist-Add": 0x07, + "Config-Change": 0x08, + "Name-Change": 0x09, + "Ping": 0x10, + "Pair": 0x11, + "Set-Pair": 0x12, + "RSSI/Status/Config-Boop": 0x13, + "Directory-Get": 0x14, + "Echo": 0x15, + "Resend": 0x16, + "Hive-Set": 0x17, + "Hive-Configure": 0x18, + "Web-Repl": 0x20, + "WiFi-Connect": 0x21, + "WiFi-Disconnect": 0x22, + "AP-Enable": 0x23, + "AP-Disable": 0x24, + "Pause": 0x25, + "Resume": 0x26, + }, + "inf": { + "RSSI/Status/Config-Boop": 0x20, + "Data": 0x21, + "Message": 0x22, + "Directory": 0x23, + }, + "ack": { + "Pong": 0x10, + "Success": 0x11, + "Fail": 0x12, + "Confirm": 0x13, + "Echo": 0x15, + }, +} +networking_keys = { + "default_handshake_key": "handshake", + "handshake_key1": "handshake1", + "handshake_key2": "handshake2", + "handshake_key3": "handshake3", + "handshake_key4": "handshake4", + "default_ap_key": "password", + "default_wifi_key": "password" +} +whitelist = [b'd\xe83\x84\xd8\x18', b'd\xe83\x84\xd8\x19', + b'd\xe83\x85\xd3\xbc', b'd\xe83\x85\xd3\xbd', + b'd\xe83\x84\xd8\x18', b'd\xe83\x84\xd8\x19'] #each ESP32 has two MAC addresses +i2c_dict = { + "0x3C": ["pca9685", 0, "screen"], + "0x53": ["ACCEL", 1, "accelerometer"] +} #key is i2c address: ["device name", Output (0) or Input (1), "Description"] +sensor_dict = {"sensor": [0,4095], "potentiometer": [0,180], "select": [0,1], "up": [0,1], "down": [0,1], "button": [0,1], "sw1": [0,1], "sw2": [0,1], "sw3": [0,1], "sw4": [0,1]} +hive_config = {'hive': True, 'recipients': [], 'sender_sensor_list': [[b'd\xe83\x84\xd8\x18', 'None']], 'refreshrate': 200, 'mode': 'None'} + +networking = None diff --git a/software/archive/backup nonworkign code/example.py b/software/archive/backup nonworkign code/example.py new file mode 100644 index 0000000..957452b --- /dev/null +++ b/software/archive/backup nonworkign code/example.py @@ -0,0 +1,91 @@ +import time + + +# Network +from ssp_networking import SSP_Networking + +# Initialise +infmsg = True +dbgmsg = False +errmsg = True +networking = SSP_Networking(infmsg, dbgmsg, errmsg) + +print(f"{(time.ticks_ms() - networking.inittime) / 1000:.3f} Networking Example: Running example.py") +print() + + +###Example code### + +recipient_mac = b'\xff\xff\xff\xff\xff\xff' # This mac sends to all +message = b'Boop' + +# Print device networking configuration +print(f"{(time.ticks_ms() - networking.inittime) / 1000:.3f} Networking Example: Config: {networking.config}") +print() + + +#Command Types: +# Ping, calculates the time until you receive a response from the peer and adds their infor to the networking address book +networking.ping(recipient_mac) +print() + +# Echo, sends a message that will be repeated back by the recipient +networking.echo(recipient_mac, message) +print() + +# There are a bunch of more command types, the complete list can be found in the config.py or ssp_networking.py + + +#Message Type +# Message, sends the specified message to the recipient, supported formats are bytearrays, bytes, int, float, string, bool, list and dict, if above 241 bytes, it will send in multiple packages: max 60928 bytes +networking.send(recipient_mac, message) +print() + +# Check if there are any messages in the message buffer, returns True or False +print(f"{(time.ticks_ms() - networking.inittime) / 1000:.3f} Networking Example: {networking.check_messages()}") +print() + +# Get Last Received Message +print(f"{(time.ticks_ms() - networking.inittime) / 1000:.3f} Networking Example: {networking.return_message()}") # Returns none, none, none if there are no messages +print() + + +#Other +# Get the RSSI table +print(f"{(time.ticks_ms() - networking.inittime) / 1000:.3f} Networking Example: {networking.rssi()}") +print() + + +#IRQ +# Set up an interrupt which runs a function as soon as possible after receiving a new message +def receive(): + for mac, message, rtime in networking.return_messages(): # You can directly iterate over the function + print(f"{(time.ticks_ms() - networking.inittime) / 1000:.3f} Networking Example: Received {message} from {peer_mac} at {rtime}") + +networking.irq(receive) # interrupt handler + + +#Send trigger +#You could set up a function that sends a message to all and is called when the select button on the dahal board is pressed +from machine import Pin +import machine + +lastPressed = 0 + +def boop(pin): + global lastPressed + if (time.ticks_ms() - lastPressed > 1000): #prevents button to be called multiple times within 1 second + lastPressed = time.ticks_ms() + networking.send(peer_mac, "Hewwo!") + print(f"{(time.ticks_ms() - networking.inittime) / 1000:.3f} Networking Example: Sent {message} to {peer_mac}") + +switch_select = Pin(9, Pin.IN, Pin.PULL_UP) +switch_select.irq(trigger=Pin.IRQ_FALLING, handler=boop) + + +#print something on the screen + +#set up servo + +#set up sensor + diff --git a/software/archive/backup nonworkign code/main.py b/software/archive/backup nonworkign code/main.py new file mode 100644 index 0000000..d6f98d4 --- /dev/null +++ b/software/archive/backup nonworkign code/main.py @@ -0,0 +1,109 @@ +import config +import utime as time +from machine import Pin, Timer +import machine + +print("Running main.py") + +try: + time.sleep(3) +except Exception as e: + print(f"Error {e}") + +module_name = config.config["configuration"].lower() + +import gc + +gc.collect() + +import network + +# just to be safe +sta = network.WLAN(network.STA_IF) +ap = network.WLAN(network.AP_IF) +sta.active(True) +ap.active(True) +sta.active(False) +ap.active(False) + +from ssp_networking import SSP_Networking +import ssp_networking + +# Network +infmsg = True +dbgmsg = False +errmsg = True +configuration = config.config["configuration"] + +config.networking = SSP_Networking(infmsg, dbgmsg, errmsg) +networking = config.networking + +peer_mac = b'\xff\xff\xff\xff\xff\xff' + +print("{:.3f} Name: {}, ID: {}, Configuration: {}, Type: {}, Sta mac: {}, Sta channel: {}, Ap mac: {}, Ap channel: {}, Version: {}".format( + (time.ticks_ms() - networking.inittime) / 1000, + networking.config["name"], + networking.config["id"], + networking.config["configuration"], + networking.config["type"], + networking.config["sta_mac"], + networking.config["sta_channel"], + networking.config["ap_mac"], + networking.config["ap_channel"], + networking.config["version"] + )) + +def am1(): + print("Running am1") + + peer_mac = b'\xff\xff\xff\xff\xff\xff' # Replace with actual MAC + lastPressed = 0 + message = "Boop!" + + networking.ping(peer_mac) + + def boop(pin): + global lastPressed + if (time.ticks_ms() - lastPressed > 1000): + lastPressed = time.ticks_ms() + networking.ping(peer_mac) + # networking.echo(peer_mac, message) + # networking.send(peer_mac, time.ticks_ms()) + print(f"{(time.ticks_ms() - networking.inittime) / 1000:.3f} Networking Tool: Sent {message} to {peer_mac}") + # print(f"{(time.ticks_ms() - networking.inittime) / 1000:.3f} Networking Tool: RSSI table: {networking.rssi()}") + + # Button interrupt + switch_select = Pin(9, Pin.IN, Pin.PULL_UP) + switch_select.irq(trigger=Pin.IRQ_FALLING, handler=boop) + + def heartbeat(timer): + print("") + boop(None) + print(f"{(time.ticks_ms() - networking.inittime) / 1000:.3f} Networking Tool Heartbeat: {gc.mem_free()} bytes") + print("") + gc.collect() + + # Periodic heartbeat timer (every 10 s) + timer = Timer(0) + timer.init(period=1000, mode=Timer.PERIODIC, callback=heartbeat) + + +def deinit(): + networking.cleanup() + try: + timer.deinit() + except Exception as e: + print(e) + machine.reset() + + +try: + if configuration == "AM1": + am1() + else: + with open(module_name + ".py") as f: + code = f.read() + exec(code, {"networking": networking, "configuration": configuration, "__name__": "__main__"}) +except Exception as e: + print(f"Error running {module_name}: {e}") + diff --git a/software/archive/backup nonworkign code/networking.py b/software/archive/backup nonworkign code/networking.py new file mode 100644 index 0000000..85e555e --- /dev/null +++ b/software/archive/backup nonworkign code/networking.py @@ -0,0 +1,787 @@ +import network +import time +import ubinascii +import espnow +import gc +import struct +import json +import machine + + +class Networking: + def __init__(self, infmsg=False, dbgmsg=False, errmsg=False, admin=False, inittime=0): + gc.collect() + self.inittime = inittime + if infmsg: + print(f"{(time.ticks_ms() - self.inittime) / 1000:.3f} Initialising Networking") + self.master = self + self.infmsg = infmsg + self.dbgmsg = dbgmsg + self.errmsg = errmsg + self.admin = admin + self.config = {"name": None, + "configuration": None, + "id": None, + "version": None, + } #Just as an example, can include more information here, but for interoperability purposes please keep the basic four attributes + + self._staif = network.WLAN(network.STA_IF) + self._apif = network.WLAN(network.AP_IF) + + self.sta = self.Sta(self, self._staif) + self.ap = self.Ap(self, self._apif) + self.aen = self.Aen(self) + + if infmsg: + print(f"{(time.ticks_ms() - self.inittime) / 1000:.3f} Networking initialised and ready") + + def cleanup(self): + self.dprint(".cleanup") + self.aen.cleanup() + self._staif.active(False) + self._apif.active(False) + + def log(self, text): + if self.admin: + with open("log.txt", "a") as f: + f.write(f"{text}\n") + + def iprint(self, message): + if self.infmsg: + try: + text = f"{(time.ticks_ms() - self.inittime) / 1000:.3f} Networking Info: {message}" + print(text) + self.log(text) + except Exception as e: + print(f"Error printing Networking Info: {e}") + return + + def dprint(self, message): + if self.dbgmsg: + try: + text = f"{(time.ticks_ms() - self.inittime) / 1000:.3f} Networking Debug: {message}" + print(text) + self.log(text) + except Exception as e: + print(f"Error printing Networking Debug: {e}") + return + + def eprint(self, message): + if self.errmsg: + try: + text = f"{(time.ticks_ms() - self.inittime) / 1000:.3f} Networking Error: {message}" + print(text) + self.log(text) + except Exception as e: + print(f"Error printing Networking Error: {e}") + return + + class Sta: + def __init__(self, master, _staif): + self.master = master + self._sta = _staif + self._sta.active(True) + self.master.iprint("STA initialised and ready") + + def scan(self): + self.master.dprint("sta.scan") + scan_result = self._sta.scan() + if self.master.infmsg: + for ap in scan_result: + self.master.iprint(f"SSID:%s BSSID:%s Channel:%d Strength:%d RSSI:%d Auth:%d " % ap) + return scan_result + + def connect(self, ssid, key="", timeout=10): + self.master.dprint("sta.connect") + self._sta.connect(ssid, key) + stime = time.time() + while time.time() - stime < timeout: + if self._sta.ifconfig()[0] != '0.0.0.0': + self.master.iprint("Connected to WiFi") + return + time.sleep(0.1) + self.master.iprint(f"Failed to connect to WiFi: {self._sta.status()}") + + def disconnect(self): + self.master.dprint("sta.disconnect") + self._sta.disconnect() + + def ip(self): + self.master.dprint("sta.ip") + return self._sta.ifconfig() + + def mac(self): + self.master.dprint("sta.mac") + return bytes(self._sta.config('mac')) + + def mac_decoded(self): # Necessary? + self.master.dprint("sta.mac_decoded") + return ubinascii.hexlify(self._sta.config('mac'), ':').decode() + + def channel(self): + self.master.dprint("sta.channel") + return self._sta.config('channel') + + def set_channel(self, number): + self.master.dprint("sta.set_channel") + if number > 14 or number < 0: + number = 0 + self._sta.config(channel=number) + self.master.iprint(f"STA channel set to {number}") + + class Ap: + def __init__(self, master, _apif): + self.master = master + self._ap = _apif + self._ap.active(True) + self.master.iprint("AP initialised and ready") + + def set_ap(self, name="", password="", max_clients=10): + self.master.dprint("ap.set_ap") + if name == "": + name = self.master.config["name"] + self._ap.active(True) + self._ap.config(essid=name) + if password: + self._ap.config(authmode=network.AUTH_WPA_WPA2_PSK, password=password) + self._ap.config(max_clients=max_clients) + self.master.iprint(f"Access Point {name} set with max clients {max_clients}") + + def deactivate(self): + self.master.dprint("ap.deactivate") + self._ap.active(False) + self.master.iprint("Access Point deactivated") + + def ip(self): + self.master.dprint("ap.ip") + return self._ap.ifconfig() + + def mac(self): + self.master.dprint("ap.mac") + return bytes(self._ap.config('mac')) + + def mac_decoded(self): + self.master.dprint("ap.mac_decoded") + return ubinascii.hexlify(self._ap.config('mac'), ':').decode() + + def channel(self): + self.master.dprint("ap.channel") + return self._ap.config('channel') + + def set_channel(self, number): + self.master.dprint("ap.set_channel") + if number > 14 or number < 0: + number = 0 + self._ap.config(channel=number) + self.master.iprint(f"AP channel set to {number}") + + class Aen: + def __init__(self, master): + self.master = master + self._aen = espnow.ESPNow() + self._aen.active(True) + + self._peers = {} + self._received_messages = [] + self._received_messages_size = [] + self._long_buffer = {} + self._long_buffer_size = {} + self.received_sensor_data = {} + self.received_rssi_data = {} + self._irq_function = None + self.boop_irq = None + self.data_irq = None + self.msg_irq = None + self.ack_irq = None + self.custom_cmd = None + self.custom_inf = None + self.custom_ack = None + self.boops = 0 + self.ifidx = 0 # 0 sends via sta, 1 via ap + # self.channel = 0 + + self._aen.irq(self._irq) + + self.master.iprint("ESP-NOW initialised and ready") + + def cleanup(self): + self.master.iprint("aen.cleanup") + self.irq(None) + self._aen.active(False) + # add delete buffers and stuff + + def update_peer(self, peer_mac, peer_config=None, channel=None, ifidx=None): + self.master.dprint("aen.update_peer") + if peer_mac == b'\xff\xff\xff\xff\xff\xff': + return + if peer_mac in self._peers: + try: + if peer_config is not None: + self._peers[peer_mac].update(peer_config) + if channel is not None: + self._peers[peer_mac].update({'channel': channel}) + if ifidx is not None: + self._peers[peer_mac].update({'ifidx': ifidx}) + self.master.dprint(f"Peer {peer_mac} updated to channel {channel}, ifidx {ifidx} and name {self.peer_name(peer_mac)}") + except OSError as e: + self.master.eprint(f"Error updating peer {peer_mac}: {e}") + return + self.master.iprint(f"Peer {peer_mac} not found") + + def add_peer(self, peer_mac, peer_config=None, channel=None, ifidx=None): + self.master.dprint("aen.add_peer") + if peer_mac == b'\xff\xff\xff\xff\xff\xff': + return + if peer_mac not in self._peers: + try: + self._peers[peer_mac] = {} + if channel is not None: + self._peers[peer_mac].update({'channel': channel}) + if ifidx is not None: + self._peers[peer_mac].update({'ifidx': ifidx}) + if peer_config is not None: + self._peers[peer_mac].update(peer_config) + if peer_mac in self._aen.peers_table: + _rssi = self._aen.peers_table[peer_mac][0] + _time = self._aen.peers_table[peer_mac][1] - self.master.inittime + else: + self.master.dprint(f"Error getting RSSI and time for {peer_mac}: {e}") + _rssi = None + _time = None + self._peers[peer_mac].update({'rssi': _rssi, 'time': _time, 'last_ping': None, 'ping': None}) + self.master.dprint(f"Peer {peer_mac} added with channel {channel}, ifidx {ifidx} and name {self.peer_name(peer_mac)}") + except OSError as e: + self.master.eprint(f"Error adding peer {peer_mac}: {e}") + else: + self.master.dprint(f"Peer {peer_mac} already exists, updating") + self.update_peer(peer_mac, peer_config, channel, ifidx) + + def remove_peer(self, peer_mac): + self.master.dprint("aen.remove_peers") + if peer_mac in self._peers: + try: + del self._peers[peer_mac] + self.master.iprint(f"Peer {peer_mac} removed") + except OSError as e: + self.master.eprint(f"Error removing peer {peer_mac}: {e}") + + def peers(self): + self.master.dprint("aen.peers") + rssi_table = self._aen.peers_table + for key in self._peers: + self._peers[key].update({'rssi': rssi_table[key][0]}) + self._peers[key].update({'time': rssi_table[key][1]-self.master.inittime}) + return self._peers + + def peer_name(self, key): + self.master.dprint("aen.name") + if isinstance(key, list): + return "..." + if key in self._peers: + if 'name' in self._peers[key]: + return self._peers[key]['name'] + else: + return None + else: + return None + + def rssi(self): + self.master.dprint("aen.rssi") + return self._aen.peers_table + + # Send cmds + def send_custom(self, msg_code, msg_subcode, mac, payload=None, channel=None, ifidx=None): + self.master.dprint("aen.send_custom") + self._compose(mac, payload, msg_code, msg_subcode, channel, ifidx) + gc.collect() + + def ping(self, mac, channel=None, ifidx=None): #Ping + self.master.dprint("aen.ping") + if bool(self.ifidx): + send_channel = self.master.ap.channel() + else: + send_channel = self.master.sta.channel() + self.send_custom(0x01, 0x10, mac, [send_channel, self.ifidx, self.master.config], channel, ifidx) # sends channel, ifidx and name + if isinstance(mac, list): + for key in mac: + self._peers[key].update({'last_ping': time.ticks_ms()}) + elif mac == b'\xff\xff\xff\xff\xff\xff': + for key in self._peers: + self._peers[key].update({'last_ping': time.ticks_ms()}) + else: + self._peers[mac].update({'last_ping': time.ticks_ms()}) + + def boop(self, mac, channel=None, ifidx=None): #"RSSI/Status/Config-Boop" + self.master.dprint("aen.boop") + self.send_custom(0x01, 0x15, mac, None, channel, ifidx) + + def echo(self, mac, message, channel=None, ifidx=None): + self.master.dprint("aen.echo") + if len(str(message)) > 241: + try: + self.master.iprint(f"Sending echo ({message}) to {mac} ({self.peer_name(mac)})") + except Exception as e: + self.master.eprint(f"Sending echo to {mac}, but error printing message content: {e}") + else: + self.master.iprint(f"Sending echo ({message}) to {mac} ({self.peer_name(mac)})") + self.send_custom(0x01, 0x15, mac, message, channel, ifidx) + gc.collect() + + def send_message(self, mac, message, channel=None, ifidx=None): + self.send(mac, message, channel, ifidx) + + def send(self, mac, message, channel=None, ifidx=None): + self.master.dprint("aen.send_message") + if len(str(message)) > 241: + try: + self.master.iprint( + f"Sending message ({str(message)[:50] + '... (truncated)'}) to {mac} ({self.peer_name(mac)})") + except Exception as e: + self.master.eprint(f"Sending message to {mac} ({self.peer_name(mac)}), but error printing message content: {e}") + gc.collect() + else: + self.master.iprint(f"Sending message ({message}) to {mac} ({self.peer_name(mac)})") + self._compose(mac, message, 0x02, 0x22, channel, ifidx) + gc.collect() + + def send_data(self, mac, message, channel=None,ifidx=None): # message is a dict, key is the sensor type and the value is the sensor value, fix name!!!!!!! + self.master.dprint("aen.message") + try: + self.master.iprint(f"Sending sensor data ({message}) to {mac} ({self.peer_name(mac)})") + except Exception as e: + self.master.eprint(f"Sending sensor data to {mac} ({self.peer_name(mac)}), but error printing message content: {e}") + self._compose(mac, message, 0x02, 0x21, channel, ifidx) + + def check_messages(self): + self.master.dprint("aen.check_message") + return len(self._received_messages) > 0 + + def return_message(self): + self.master.dprint("aen.return_message") + if self.check_messages(): + self._received_messages_size.pop() + return self._received_messages.pop() + return None, None, None + + def return_messages(self): + self.master.dprint("aen.return_messages") + if self.check_messages(): + messages = self._received_messages[:] + self._received_messages.clear() + self._received_messages_size.clear() + gc.collect() + return messages + return [(None, None, None)] + + def return_data(self): + self.master.dprint("aen.return_data") + return self.received_sensor_data + + def _irq(self, espnow): + self.master.dprint("aen._irq") + if self.master.admin: + try: + self._receive() + if self._irq_function and self.check_messages(): + self._irq_function() + gc.collect() + return + except KeyboardInterrupt: + self.master.eprint("aen._irq except KeyboardInterrupt") + self.master.cleanup() #network cleanup + raise SystemExit("Stopping networking execution. ctrl-c or ctrl-d again to stop main code") # in thonny stops library code but main code keeps running, same in terminal + else: + self._receive() + if self._irq_function and self.check_messages(): + self._irq_function() + gc.collect() + return + + def irq(self, func): + self.master.dprint("aen.irq") + self._irq_function = func + + def cmd(self, func): + self.master.dprint("aen.cmd") + self.custom_cmd = func + + def inf(self, func): + self.master.dprint("aen.inf") + self.custom_inf = func + + def ack(self, func): + self.master.dprint("aen.ack") + self.custom_ack = func + + def _send(self, peers_mac, messages, channel, ifidx, long_msg=False): + self.master.dprint("aen._send") + if isinstance(peers_mac, bytes): + peers_mac = [peers_mac] + for peer_mac in peers_mac: + try: + if channel is None: + if peer_mac in self._peers: + if 'channel' in self._peers[peer_mac]: + channel=self._peers[peer_mac]['channel'] + else: + channel = 0 + elif ifidx is None: + if peer_mac in self._peers: + if 'ifidx' in self._peers[peer_mac]: + ifidx=self._peers[peer_mac]['ifidx'] + else: + ifidx=self.ifidx + self._aen.add_peer(peer_mac, channel=channel, ifidx=ifidx) + self.master.dprint(f"Added {peer_mac} to espnow buffer with channel {channel} and ifidx {ifidx}") + except Exception as e: + self.master.eprint(f"Error adding {peer_mac} to espnow buffer: {e}") + for m in range(len(messages)): + i = 0 + while i in range(3): + i += i + ack = False + try: + ack = self._aen.send(peer_mac, messages[m], True) + self.master.dprint(f"Receipt confirm: {ack}") + if ack: + break + except Exception as e: + self.master.eprint(f"Error sending to {peer_mac}: {e}") + self.master.dprint(f"Sent {messages[m]} to {peer_mac} ({self.peer_name(peer_mac)}) with {ack}") + gc.collect() + try: + self._aen.del_peer(peer_mac) + self.master.dprint(f"Removed {peer_mac} from espnow buffer") + except Exception as e: + self.master.eprint(f"Error removing {peer_mac} from espnow buffer: {e}") + + def __send_confirmation(self, msg_type, recipient_mac, msg_subkey_type, payload=None, error=None): + if msg_type == "Success": + self._compose(recipient_mac, [msg_subkey_type, payload], 0x03, 0x11) + elif msg_type == "Fail": + self._compose(recipient_mac, [msg_subkey_type, error, payload], 0x03, 0x12) + else: + self._compose(recipient_mac, [msg_subkey_type, payload], 0x03, 0x13) + + def _compose(self, peer_mac, payload=None, msg_type=0x02, subtype=0x22, channel=None, ifidx=None): + self.master.dprint("aen._compose") + + if isinstance(peer_mac, list): + for peer_macs in peer_mac: + if peer_macs not in self._peers: + self.add_peer(peer_macs, None, channel, ifidx) + elif peer_mac not in self._peers: + self.add_peer(peer_mac, None, channel, ifidx) + + payload_type, payload_bytes = None, None + self.master.dprint("aen.__encode_payload") + if payload is None: # No payload type + payload_type, payload_bytes = b'\x00', b'' + elif isinstance(payload, bytearray): # bytearray + payload_type, payload_bytes = b'\x01', bytes(payload) + elif isinstance(payload, bytes): # bytes + payload_type, payload_bytes = b'\x01', payload + elif isinstance(payload, bool): # bool + payload_type, payload_bytes = b'\x02', (b'\x01' if payload else b'\x00') + elif isinstance(payload, int): # int + payload_type, payload_bytes = b'\x03', str(payload).encode('utf-8') + elif isinstance(payload, float): # float + payload_type, payload_bytes = b'\x04', str(payload).encode('utf-8') + elif isinstance(payload, str): # string + payload_type, payload_bytes = b'\x05', payload.encode('utf-8') + elif isinstance(payload, dict) or isinstance(payload, list): # json dict or list + json_payload = json.dumps(payload) + payload_type, payload_bytes = b'\x06', json_payload.encode('utf-8') + else: + raise ValueError("Unsupported payload type") + + messages = [] + identifier = 0x2a + timestamp = time.ticks_ms() + header = bytearray(8) + header[0] = identifier + header[1] = msg_type + header[2] = subtype + header[3:7] = timestamp.to_bytes(4, 'big') + long_msg = False + if len(payload_bytes) < 242: # 250-9=241=max_length + header[7] = payload_type[0] + total_length = 1 + 1 + 1 + 4 + 1 + len(payload_bytes) + 1 + message = bytearray(total_length) + message[:8] = header + message[8:-1] = payload_bytes + message[-1:] = (sum(message) % 256).to_bytes(1, 'big') # Checksum + self.master.dprint(f"Message {1}/{1}; Length: {len(message)}; Free memory: {gc.mem_free()}") + messages.append(message) + else: + self.master.dprint("Long message: Splitting!") + max_size = 238 # 241-3 + total_chunk_number = (-(-len(payload_bytes) // max_size)) # Round up division + lba = b'\x07' + header[7] = lba[0] # Long byte array + if total_chunk_number > 256: + raise ValueError("More than 256 chunks, unsupported") + for chunk_index in range(total_chunk_number): + message = bytearray(9 + 3 + min(max_size, len(payload_bytes) - chunk_index * max_size)) + message[:8] = header + message[8:10] = chunk_index.to_bytes(1, 'big') + total_chunk_number.to_bytes(1, 'big') + message[10] = payload_type[0] + message[11:-1] = payload_bytes[chunk_index * max_size: (chunk_index + 1) * max_size] + message[-1:] = (sum(message) % 256).to_bytes(1, 'big') # Checksum + self.master.dprint(message) + messages.append(bytes(message)) + self.master.dprint( + f"Message {chunk_index + 1}/{total_chunk_number}; length: {len(message)}; Free memory: {gc.mem_free()}") + gc.collect() + long_msg = True + gc.collect() + self._send(peer_mac, messages, channel, ifidx, long_msg) + + + def __remove_long_message_from_buffer(self, timer, key): + self.master.dprint("aen.__remove_long_message_from_buffer") + if key in self._long_buffer: + del self._long_buffer[key] + if key in self._long_buffer_size: + self._long_buffer_size[key] + gc.collect() + + def _receive(self): + self.master.dprint("aen._receive") + + + + def __process_message(self, sender_mac, message, receive_timestamp): + self.master.dprint("aen.__process_message") + if message[0] != 0x2a: # Unique Message Identifier Check + self.master.dprint("Invalid message: Message ID Fail") + return None + if len(message) < 9: # Min size + self.master.dprint("Invalid message: too short") + return None + + msg_type = int.from_bytes(message[1:2], 'big') + subtype = subtype = int.from_bytes(message[2:3], 'big') + send_timestamp = int.from_bytes(message[3:7], 'big') + payload_type = bytes(message[7:8]) + payload_bytes = message[8:-1] + checksum = message[-1] + self.master.dprint(f"{type(msg_type)}: {msg_type}, {type(subtype)}: {subtype}, {type(send_timestamp)}: {send_timestamp}, {type(payload_type)}: {payload_type}, {type(payload_bytes)}: {payload_bytes}, {type(checksum)}: {checksum}") + + # Checksum + if checksum != sum(message[:-1]) % 256: + self.master.dprint("Invalid message: checksum mismatch") + return None + + if sender_mac not in self._peers: + self.add_peer(sender_mac) + + payload = None + + if payload_type == b'\x07': + self.master.dprint("Long message received, processing...") + part_n = int.from_bytes(payload_bytes[0:1], 'big') + total_n = int.from_bytes(payload_bytes[1:2], 'big') + payload_type = bytes(payload_bytes[2:3]) + + # Create a key as a bytearray: (msg_type, subtype, timestamp, payload_type, total_n) + key = bytearray() + key.extend(message[1:2]) + key.extend(message[2:3]) + key.extend(message[3:7]) + key.extend(payload_type) + key.extend(payload_bytes[1:2]) + key = bytes(key) + self.master.dprint(f"Key: {key}") + + payload_bytes = payload_bytes[3:] + + # Check if the key already exists in the long buffer + if key in self._long_buffer: + # If the part is None, add the payload + if self._long_buffer[key][part_n] is None: + self._long_buffer[key][part_n] = payload_bytes + self._long_buffer_size[key] = self._long_buffer_size[key] + len(payload_bytes) + self.master.dprint( + f"Long message: Key found, message added to entry in long_message_buffer, {sum(1 for item in self._long_buffer[key] if item is not None)} out of {total_n} packages received") + # If there are still missing parts, return + if any(value is None for value in self._long_buffer[key]): + gc.collect() + return + else: + return + else: + # Initialize the long message buffer for this key + payloads = [None] * total_n + payloads[part_n] = payload_bytes + self._long_buffer[key] = payloads + self._long_buffer_size[key] = len(payload_bytes) + self.master.dprint( + f"Long message: Key not found and new entry created in long_message_buffer, {sum(1 for item in self._long_buffer[key] if item is not None)} out of {total_n} packages received") + + while len(self._long_buffer) > 8 or sum(self._long_buffer_size.values()) > 75000: + self.master.dprint( + f"Maximum buffer size reached: {len(self._long_buffer)}, {sum(self._long_buffer_size.values())} bytes; Reducing!") + oldest_key = next(iter(self._long_buffer)) + del self._long_buffer[oldest_key] + del self._long_buffer_size[oldest_key] + gc.collect() + timer = machine.Timer(0) + timer.init(period=10000, mode=machine.Timer.ONE_SHOT, callback=lambda t: self.__remove_long_message_from_buffer(t, key)) + return + + # If all parts have been received, reconstruct the message + if not any(value is None for value in self._long_buffer[key]): + payload_bytes = bytearray() + for i in range(0, total_n): + payload_bytes.extend(self._long_buffer[key][i]) + del self._long_buffer[key] + del self._long_buffer_size[key] + self.master.dprint("Long message: All packages received!") + else: + self.master.dprint("Long Message: Safeguard triggered, code should not have gotten here") + gc.collect() + return + + payload_size = len(payload_bytes) + + self.master.dprint("aen.__decode_payload") + if payload_type == b'\x00': # None + payload = None + elif payload_type == b'\x01': # bytearray or bytes + payload = bytes(payload_bytes) + elif payload_type == b'\x02': # bool + payload = payload_bytes[0:1] == b'\x01' + elif payload_type == b'\x03': # int + payload = int(payload_bytes.decode('utf-8')) + elif payload_type == b'\x04': # float + payload = float(payload_bytes.decode('utf-8')) + elif payload_type == b'\x05': # string + payload = payload_bytes.decode('utf-8') + elif payload_type == b'\x06': # json dict or list + payload = json.loads(payload_bytes.decode('utf-8')) #use eval instead? #FIX + elif payload_type == b'\x07': # Long byte array + payload = bytes(payload_bytes) + else: + raise ValueError(f"Unsupported payload type: {payload_type} Message: {payload_bytes}") + + # Handle the message based on type + if msg_type == 0x01: # Command Message + msg_key = "cmd" + __handle_cmd(self, sender_mac, subtype, send_timestamp, receive_timestamp, payload, payload_size, msg_key) + elif msg_type == 0x02: # Informational Message + msg_key = "inf" + __handle_inf(self, sender_mac, subtype, send_timestamp, receive_timestamp, payload, payload_size, msg_key) + elif msg_type == 0x03: # Acknowledgement Message + msg_key = "ack" + __handle_ack(self, sender_mac, subtype, send_timestamp, receive_timestamp, payload, payload_size, msg_key) + else: + self.master.iprint(f"Unknown message type from {sender_mac} ({self.peer_name(sender_mac)}): {message}") + + def __handle_cmd(self, sender_mac, subtype, send_timestamp, receive_timestamp, payload, payload_size, msg_key): + self.master.dprint(f"aen.__handle_cmd") + if (msg_subkey := "Ping") and subtype == 0x01 or subtype == 0x10: # Ping + self.master.iprint(f"{msg_subkey} ({subtype}) command received from {sender_mac} ({self.peer_name(sender_mac)})") + self.add_peer(sender_mac, payload[2], payload[0], payload[1]) + if bool(self.ifidx): + channel = self.master.ap.channel() + else: + channel = self.master.sta.channel() + response = [channel, self.ifidx, self.master.config, send_timestamp] + self._compose(sender_mac, response, 0x03, 0x10) + elif (msg_subkey := "RSSI/Status/Config-Boop") and subtype == 0x03 or subtype == 0x13: # RSSI/Status/Config Boop + self.boops = self.boops + 1 + self.master.iprint(f"{msg_subkey} ({subtype}) command received from {sender_mac} ({self.peer_name(sender_mac)}), Received total of {self.boops} boops!") + try: + self._compose(sender_mac, [self.master.config, self.master.sta.mac, self.master.ap.mac, self.rssi()], 0x02, 0x20) # [ID, Name, Config, Version, sta mac, ap mac, rssi] + except Exception as e: + self.master.aen.__send_confirmation(self, "Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, e) + elif (msg_subkey := "Echo") and subtype == subtype == 0x02 or subtype == 0x15: # Echo + self.master.iprint(f"{msg_subkey} ({subtype}) command received from {sender_mac} ({self.peer_name(sender_mac)}): {payload}") # Check i or d + self._compose(sender_mac, payload, 0x03, 0x15) + else: + if self.custom_cmd: + self.master.iprint(f"Checking custom cmd handler library") + self.custom_cmd([sender_mac, subtype, send_timestamp, receive_timestamp, payload, msg_key]) + self.master.iprint(f"Unknown command subtype from {sender_mac} ({self.peer_name(sender_mac)}): {subtype}") + + def __handle_inf(self, sender_mac, subtype, send_timestamp, receive_timestamp, payload, payload_size, msg_key): + self.master.dprint("aen.__handle_inf") + if (msg_subkey := "RSSI/Status/Config-Boop") and subtype == 0x00 or subtype == 0x20: # RSSI/Status/Config-Boop + self.master.iprint(f"{msg_subkey} ({subtype}) data received from {sender_mac} ({self.peer_name(sender_mac)}): {payload}") + self.received_rssi_data[sender_mac] = payload + # self.master.aen.__send_confirmation(self, "Confirm", sender_mac, f"{msg_subkey} ({subtype})", payload) #confirm message recv + if self.boop_irq: + self.boop_irq() + elif (msg_subkey := "Data") and subtype == 0x01 or subtype == 0x21: # Sensor Data + payload["time_sent"] = send_timestamp + payload["time_recv"] = receive_timestamp + self.master.iprint(f"{msg_subkey} ({subtype}) data received from {sender_mac} ({self.peer_name(sender_mac)}): {payload}") + if sender_mac in self.received_sensor_data: + self.received_sensor_data[b'prev_' + sender_mac] = self.received_sensor_data[sender_mac] + self.received_sensor_data[sender_mac] = payload + # self.master.aen.__send_confirmation(self, "Confirm", sender_mac, f"{msg_subkey} ({subtype})", payload) #confirm message recv + if self.data_irq: + self.data_irq() + elif (msg_subkey := "Message") and subtype == 0x02 or subtype == 0x22: # Message / Other + self.master.iprint(f"{msg_subkey} ({subtype}) received from {sender_mac} ({self.peer_name(sender_mac)}): {payload}") + self._received_messages.append((sender_mac, payload, receive_timestamp)) + self._received_messages_size.append(payload_size) + while len(self._received_messages) > 2048 or sum(self._received_messages_size) > 20000: + self.master.dprint(f"Maximum buffer size reached: {len(self._received_messages)}, {sum(self._received_messages_size)} bytes; Reducing!") + self._received_messages.pop(0) + self._received_messages_size.pop(0) + # self.master.aen.__send_confirmation(self, "Confirm", sender_mac, f"{msg_subkey} ({subtype})", payload) #confirm message recv + if self.msg_irq: + self.msg_irq() + else: + if self.custom_inf: + self.master.iprint(f"Checking custom inf handler library") + self.custom_inf([sender_mac, subtype, send_timestamp, receive_timestamp, payload, msg_key]) + self.master.iprint(f"Unknown info subtype from {sender_mac} ({self.peer_name(sender_mac)}): {subtype}") + + def __handle_ack(self, sender_mac, subtype, send_timestamp, receive_timestamp, payload, payload_size, msg_key): + self.master.dprint("aen.__handle_ack") + if (msg_subkey := "Pong") and subtype == 0x10: # Pong + self.add_peer(sender_mac, payload[2], payload[0], payload[1]) + self.master.iprint(f"{msg_subkey} ({subtype}) received from {sender_mac} ({self.peer_name(sender_mac)}), {receive_timestamp - payload[3]}") + self._peers[sender_mac].update({'ping': receive_timestamp - payload[3]}) + elif (msg_subkey := "Echo") and subtype == 0x15: # Echo + self.master.iprint(f"{msg_subkey} ({subtype}) received from {sender_mac} ({self.peer_name(sender_mac)}), {payload}") + #self.last_echo = payload #Not sure wtf this is + elif (msg_subkey := "Success") and subtype == 0x11: # Success + # payload should return a list with a cmd type and payload + self.master.iprint(f"{msg_subkey} ({subtype}) received from {sender_mac} ({self.peer_name(sender_mac)}) for type {payload[0]} with payload {payload[1]}") + # add to ack buffer + elif (msg_subkey := "Fail") and subtype == 0x12: # Fail + # payload should return a list with a cmd type, error and payload + self.master.iprint(f"{msg_subkey} ({subtype}) received from {sender_mac} ({self.peer_name(sender_mac)}) for type {payload[0]} with error {payload[1]} and payload {payload[2]}") + # add to ack buffer + elif (msg_subkey := "Confirm") and subtype == 0x13: # Confirmation + # payload should return a list with message type and payload + self.master.iprint(f"{msg_subkey} ({subtype}) received from {sender_mac} ({self.peer_name(sender_mac)}) for type {payload[0]} with payload {payload[1]}") + # add to ack buffer + else: + if self.custom_ack: + self.master.iprint(f"Checking custom ack handler library") + self.custom_ack([sender_mac, subtype, send_timestamp, receive_timestamp, payload, msg_key]) + self.master.iprint(f"Unknown ack subtype from {sender_mac} ({self.peer_name(sender_mac)}): {subtype}, Payload: {payload}") + # Insert more acknowledgement logic here and/or add message to acknowledgement buffer + if self.ack_irq: + self.ack_irq() + + if self._aen.any(): + timestamp = time.ticks_ms() + for mac, data in self._aen: + self.master.dprint(f"Received {mac, data}") + if mac is None: # mac, msg will equal (None, None) on timeout + break + if data: + if mac and data is not None: + # self._received_messages.append((sender_mac, data, receive_timestamp))#Messages will be saved here, this is only for debugging purposes + __process_message(self, mac, data, timestamp) + if not self._aen.any(): # this is necessary as the for loop gets stuck and does not exit properly. + break + +# message structure (what kind of message types do I need?: Command which requires me to do something (ping, pair, change state(update, code, mesh mode, run a certain file), Informational Message (Sharing Sensor Data and RSSI Data) +# | Header (1 byte) | Type (1 byte) | Subtype (1 byte) | Timestamp (ms ticks) (4 bytes) | Payload type (1) | Payload (variable) | Checksum (1 byte) | diff --git a/software/archive/backup nonworkign code/ssp_networking.py b/software/archive/backup nonworkign code/ssp_networking.py new file mode 100644 index 0000000..5498298 --- /dev/null +++ b/software/archive/backup nonworkign code/ssp_networking.py @@ -0,0 +1,657 @@ +from config import mysecrets, config, whitelist, i2c_dict, version, msg_codes, msg_subcodes, networking_keys +from networking import Networking +import ubinascii +import machine +import time +import os +import gc +import webrepl +#from sys import implementation + + +class SSP_Networking: + def __init__(self, infmsg=False, dbgmsg=False, errmsg=False, admin=False, inittime=0): + if infmsg: + print(f"{(time.ticks_ms() - inittime) / 1000:.3f} Initialising Smart System Platform Networking") + self.networking = Networking(infmsg, dbgmsg, errmsg, admin, inittime) + config["id"] = ubinascii.hexlify(machine.unique_id()).decode() + #config["type"] = implementation._machine if hasattr(implementation, "_machine") else implementation.name + from sys import implementation as _ + config["type"] = _._machine if hasattr(_, "_machine") else _.name + _=None + del _ + config["ap_mac"] = self.networking.ap.mac_decoded() + config["sta_mac"] = self.networking.sta.mac_decoded() + self.networking.config = config + self.config = self.networking.config + self.version = version + self.orders = self.Orders(self) + self.inittime = self.networking.inittime + try: + if self.config["ap_channel"] is not None: + self.networking.ap.set_channel(self.config["ap_channel"]) + if self.config["sta_channel"] is not None: + self.networking.sta.set_channel(self.config["sta_channel"]) + except Exception as e: + self.networking.eprint(f"Error: {e}") + try: + file_path = "config.py" + with open(file_path, "r") as f: + lines = f.readlines() + with open(file_path, "w") as f: + for line in lines: + if line.startswith(f'config = '): + f.write(f'config = {self.config}\n') + else: + f.write(line) + except Exception as e: + self.networking.eprint(f"Error: {e}") + + def cleanup(self): + self.networking.cleanup() + + def rssi(self): + return self.networking.aen.rssi() + + def peers(self): + return self.networking.aen.peers() + + def wpeers(self): + self.networking.iprint(f"time.ticks_ms(): {time.ticks_ms()}") + original_dict = self.networking.aen.peers() + #decoded_dict = {ubinascii.hexlify(key, ':').decode(): value for key, value in original_dict.items()} + networking_peer_info = f"networking_peers_info_start{original_dict}networking_peers_info_end" + print(networking_peer_info) + + def irq(self, func): + self.networking.aen.irq(func) + + def check_messages(self): + return self.networking.aen.check_messages() + + def return_message(self): + return self.networking.aen.return_message() + + def return_messages(self): + return self.networking.aen.return_messages() + + def return_data(self): + return self.networking.aen.return_data() + + def send_custom(self, msg_subkey, mac, payload=None, channel=None, ifidx=None, sudo=False): + self.networking.dprint("aen.send_custom") + if sudo and isinstance(payload, list): + payload.append("sudo") + elif sudo and payload is None: + payload = ["sudo"] + else: + payload = [payload, "sudo"] + if (msg_key := "cmd") and msg_subkey in msg_subcodes[msg_key]: + if isinstance(mac, list): + self.networking.iprint(f"Sending {msg_subkey} ({bytes([msg_subcodes[msg_key][msg_subkey]])}) command to {mac}") + else: + self.networking.iprint(f"Sending {msg_subkey} ({bytes([msg_subcodes[msg_key][msg_subkey]])}) command to {mac} ({self.networking.aen.peer_name(mac)})") + self.networking.aen.send_custom(msg_codes[msg_key], msg_subcodes[msg_key][msg_subkey], mac, payload, channel, ifidx) + else: + self.networking.iprint(f"Command {msg_subkey} not found") + gc.collect() + + def ping(self, mac, channel=None, ifidx=None): + self.networking.dprint("net.cmd.ping") + self.networking.aen.ping(mac, channel, ifidx) + + def echo(self, mac, message, channel=None, ifidx=None): + self.networking.dprint("net.cmd.echo") + self.networking.aen.echo(mac, message, channel, ifidx) + + def boop(self, mac, channel=None, ifidx=None, sudo=False): + self.networking.dprint("net.cmd.boop") + self.networking.aen.boop(mac, channel, ifidx) + + def send(self, mac, message, channel=None, ifidx=None): + self.networking.dprint("net.cmd.message") + self.networking.aen.send(mac, message, channel, ifidx) + + def broadcast(self, message, channel=None, ifidx=None): + self.networking.dprint("net.cmd.broadcast") + mac = b'\xff\xff\xff\xff\xff\xff' + self.send(mac, message, channel, ifidx) + + def send_data(self, mac, message, channel=None, ifidx=None): # message is a dict, key is the sensor type and the value is the sensor value + self.networking.dprint("net.cmd.message") + self.networking.aen.send_data(mac, message, channel, ifidx) + + def reboot(self, mac, channel=None, ifidx=None, sudo=False): + self.networking.dprint("net.cmd.reboot") + self.send_custom("Reboot", mac, None, channel, ifidx, sudo) + + def hive_set(self, mac, hive_bool, channel=None, ifidx=None, sudo=False): + self.networking.dprint("net.cmd.hive_set") + self.send_custom("Hive-Set", mac, hive_bool, channel, ifidx, sudo) + + def hive_configure(self, mac, hive_config, channel=None, ifidx=None, sudo=False): + self.networking.dprint("net.cmd.hive_configure") + self.send_custom("Hive-Configure", mac, f"{hive_config}", channel, ifidx, sudo) + + def hive_file(self, mac, file_name, hive_file, channel=None, ifidx=None, sudo=False): + self.networking.dprint("net.cmd.hive_file") + self.send_custom("Hive-File", mac, f"{[file_name, hive_file]}", channel, ifidx, sudo) + + def firmware_update(self, mac, channel=None, ifidx=None, sudo=False): + self.networking.dprint("net.cmd.firmware_update") + self.send_custom("Firmware-Update", mac, None, channel, ifidx, sudo) + + def file_update(self, mac, channel=None, ifidx=None, sudo=False): + self.networking.dprint("net.cmd.file_update") + self.send_custom("File-Update", mac, None, channel, ifidx, sudo) + + def file_download(self, mac, link, file_list=None, channel=None, ifidx=None, sudo=False): + self.networking.dprint("net.cmd.file_download") + self.send_custom("File-Download", mac, [link, file_list], channel, ifidx, sudo) + + def web_repl(self, mac, channel=None, ifidx=None, sudo=False): + self.networking.dprint("net.cmd.web_repl") + self.networking.ap.set_ap(ap_name := self.networking.config["name"], password := networking_keys["default_ap_key"]) + self.send_custom("Web-Repl", mac, [ap_name, password], channel, ifidx, sudo) + # await success message and if success False disable AP or try again + + def file_run(self, mac, filename, channel=None, ifidx=None, sudo=False): + self.networking.dprint("net.cmd.file_run") + self.send_custom("File-Run", mac, filename, channel, ifidx, sudo) + + def admin_set(self, mac, new_bool, channel=None, ifidx=None, sudo=False): + self.networking.dprint("net.cmd.admin_set") + self.send_custom("Admin-Set", mac, new_bool, channel, ifidx, sudo) + + def whitelist_add(self, mac, mac_list=None, channel=None, ifidx=None, sudo=False): + self.networking.dprint("net.cmd.whitelist_add") + if mac_list is not None: + mac_list = [self.networking.sta.mac_decoded, self.networking.ap.mac_decoded] + self.send_custom("Whitelist-Add", mac, mac_list, channel, ifidx, sudo) + + def config_change(self, mac, new_config, hardcode=False, channel=None, ifidx=None, sudo=False): + self.networking.dprint("net.cmd.config_change") + self.send_custom("Config-Change", mac, [new_config, hardcode], channel, ifidx, sudo) + + def name_change(self, mac, new_name, hardcode=False, channel=None, ifidx=None, sudo=False): + self.networking.dprint("net.cmd.name_change") + self.send_custom("Name-Change", mac, [new_name, hardcode], channel, ifidx, sudo) + + def pair(self, mac, key=networking_keys["handshake_key1"], channel=None, ifidx=None): + self.networking.dprint("net.cmd.pair") + self.send_custom("Pair", mac, key, channel, ifidx) + + def pair_enable(self, mac, pair_bool, channel=None, ifidx=None, sudo=False): + self.networking.dprint("net.cmd.pair") + self.send_custom("Set-Pair", mac, pair_bool, channel, ifidx, sudo) + + def directory_get(self, mac, channel=None, ifidx=None, sudo=False): + self.networking.dprint("net.cmd.directory_get") + self.send_custom("Directory-Get", mac, None, channel, ifidx, sudo) + + # resend cmd + + def wifi_connect(self, mac, name, password, channel=None, ifidx=None, sudo=False): + self.networking.dprint("net.cmd.wifi_connect") + self.send_custom("Wifi-Connect", mac, [name, password], channel, ifidx, sudo) + + def wifi_disconnect(self, mac, channel=None, ifidx=None, sudo=False): + self.networking.dprint("net.cmd.wifi_disconnect") + self.send_custom("Wifi-Disconnect", mac, None, channel, ifidx, sudo) + + def ap_enable(self, mac, name, password, channel=None, ifidx=None, sudo=False): + self.networking.dprint("net.cmd.ap_enable") + self.send_custom("AP-Enable", mac, [name, password], channel, ifidx, sudo) + + def ap_disable(self, mac, channel=None, ifidx=None, sudo=False): + self.networking.dprint("net.cmd.ap_disable") + self.send_custom("AP-Disable", mac, None, channel, ifidx, sudo) + + def pause(self, mac, channel=None, ifidx=None, sudo=False): + self.networking.dprint("net.cmd.pause") + self.send_custom("Pause", mac, None, channel, ifidx, sudo) + + def resume(self, mac, channel=None, ifidx=None, sudo=False): + self.networking.dprint("net.cmd.resume") + self.send_custom("Resume", mac, None, channel, ifidx, sudo) + + + class Orders: + def __init__(self, master): + self.master = master + self.master.networking.dprint("net.cmd.orders") + self._whitelist = whitelist + + self._pause_function = None + + # Flags + self._pairing_enabled = True + self._pairing = False + self._paired = False + self._paired_macs = [] + self._running = True + + def __check_authorisation(sender_mac, payload): + return sender_mac in self._whitelist or payload == "sudo" or payload[-1] == "sudo" + + def __send_confirmation(msg_type, recipient_mac, msg_subkey_type, payload=None, error=None): + self.master.networking.dprint("net.order.__send_confirmation") + self.master.networking.aen.__send_confirmation(msg_type, recipient_mac, msg_subkey_type, payload, error) + + def custom_cmd_handler(data): + self.master.networking.dprint("net.order.custom_cmd_handler") + sender_mac, subtype, send_timestamp, receive_timestamp, payload, msg_key = data + if (msg_subkey := "Reboot") and subtype == msg_subcodes[msg_key][msg_subkey]: # Reboot + self.master.networking.iprint(f"{msg_subkey} ({subtype}) command received from {sender_mac} ({self.master.networking.aen.peer_name(sender_mac)})") + if __check_authorisation(sender_mac, payload): + __send_confirmation("Confirm", sender_mac, f"{msg_subkey} ({subtype})", payload) + machine.reset() + else: + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, "Not authorised") + elif (msg_subkey := "Hive-Set") and subtype == msg_subcodes[msg_key][msg_subkey]: # Hive-Set + self.master.networking.iprint(f"{msg_subkey} ({subtype}) command received from {sender_mac} ({self.master.networking.aen.peer_name(sender_mac)})") + if __check_authorisation(sender_mac, payload): + try: + from config import hive_config + hive_config["hive"] = payload[0] + file_path = "config.py" + with open(file_path, "r") as f: + lines = f.readlines() + with open(file_path, "w") as f: + for line in lines: + if line.startswith(f'hive_config = '): + f.write(f'hive_config = {hive_config}\n') + else: + f.write(line) + __send_confirmation("Confirm", sender_mac, f"{msg_subkey} ({subtype})", payload) + machine.reset() + except Exception as e: + self.master.networking.eprint(f"Error: {e} with payload: {payload}") + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, e) + else: + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, "Not authorised") + elif (msg_subkey := "Hive-Configure") and subtype == msg_subcodes[msg_key][msg_subkey]: # Hive-Configure + self.master.networking.iprint(f"{msg_subkey} ({subtype}) command received from {sender_mac} ({self.master.networking.aen.peer_name(sender_mac)})") + if __check_authorisation(sender_mac, payload): + try: + from config import hive_config + new_config = eval(payload[0]) + hive_config.update(new_config) + file_path = "config.py" + with open(file_path, "r") as f: + lines = f.readlines() + with open(file_path, "w") as f: + for line in lines: + if line.startswith(f'hive_config = '): + f.write(f'hive_config = {hive_config}\n') + else: + f.write(line) + __send_confirmation("Success", sender_mac, f"{msg_subkey} ({subtype})", payload) + machine.reset() + except Exception as e: + self.master.networking.eprint(f"Error: {e} with payload: {payload}") + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, e) + else: + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, "Not authorised") + elif (msg_subkey := "Hive-File") and subtype == msg_subcodes[msg_key][msg_subkey]: # File-Hive + self.master.networking.iprint(f"{msg_subkey} ({subtype}) command received from {sender_mac} ({self.master.networking.aen.peer_name(sender_mac)})") + if __check_authorisation(sender_mac, payload): + try: + # Insert write file logic here + file_path = eval(payload[0]) + file_hive = eval(payload[1]) + if isinstance(file_hive, str): + file_hive = [file_hive] + with open(file_path, "w") as f: + f.write(f"{file_hive}\n") + self.master.networking.iprint("No file-hive logic written just yet") + __send_confirmation("Success", sender_mac, f"{msg_subkey} ({subtype})", payload) + except Exception as e: + self.master.networking.eprint(f"Error: {e} with payload: {payload}") + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, e) + else: + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, "Not authorised") + elif (msg_subkey := "Firmware-Update") and subtype == msg_subcodes[msg_key][msg_subkey]: # Firmware-Update + self.master.networking.iprint(f"{msg_subkey} ({subtype}) command received from {sender_mac} ({self.master.networking.aen.peer_name(sender_mac)})") + if __check_authorisation(sender_mac, payload): + try: + # Insert update logic here + self.master.networking.iprint("no update logic written just yet") + __send_confirmation("Success", sender_mac, f"{msg_subkey} ({subtype})", payload) + except Exception as e: + self.master.networking.eprint(f"Error: {e} with payload: {payload}") + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, e) + else: + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, "Not authorised") + elif (msg_subkey := "File-Update") and subtype == msg_subcodes[msg_key][msg_subkey]: # File-Update + self.master.networking.iprint(f"{msg_subkey} ({subtype}) command received from {sender_mac} ({self.master.networking.aen.peer_name(sender_mac)})") + if __check_authorisation(sender_mac, payload): + try: + # Insert update logic here + self.master.networking.iprint("No update logic written just yet") + __send_confirmation("Success", sender_mac, f"{msg_subkey} ({subtype})", payload) + except Exception as e: + self.master.networking.eprint(f"Error: {e} with payload: {payload}") + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, e) + else: + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, "Not authorised") + elif (msg_subkey := "File-Download") and subtype == msg_subcodes[msg_key][msg_subkey]: # File-Download + self.master.networking.iprint(f"{msg_subkey} ({subtype}) command received from {sender_mac} ({self.master.networking.aen.peer_name(sender_mac)})") + # should return a list with a link and the list of files to download + if __check_authorisation(sender_mac, payload): + try: + # import mip + # base = payload[0] + # files_to_copy = payload[1] + # if files_to_copy is None: + # mip.install(base) + # else: + # for f in files_to_copy: + # mip.install(base + f) + __send_confirmation("Success", sender_mac, f"{msg_subkey} ({subtype})", payload) + except Exception as e: + self.master.networking.eprint(f"Error: {e} with payload: {payload}") + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, e) + else: + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, "Not authorised") + elif (msg_subkey := "Web-Repl") and subtype == msg_subcodes[msg_key][msg_subkey]: # Web-Repl + self.master.networking.iprint(f"{msg_subkey} ({subtype}) command received from {sender_mac} ({self.master.networking.aen.peer_name(sender_mac)})") + # should be a list with name and password + if __check_authorisation(sender_mac, payload): + try: + # add logic to connect to Wi-Fi and set up webrepl + webrepl.start() + self.master.sta.connect(payload[0], payload[1]) + link = "webrepl link" + __send_confirmation("Success", sender_mac, f"{msg_subkey} ({subtype})", link) + except Exception as e: + self.master.networking.eprint(f"Error: {e} with payload: {payload}") + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, e) + else: + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, "Not authorised") + elif (msg_subkey := "File-Run") and subtype == msg_subcodes[msg_key][msg_subkey]: # File-Run + self.master.networking.iprint( + f"{msg_subkey} ({subtype}) command received from {sender_mac} ({self.master.networking.aen.peer_name(sender_mac)})") + if __check_authorisation(sender_mac, payload): + try: + file_name = payload[0] + if not file_name.endswith(".py"): + file_name += ".py" + with open(file_name) as f: + code = f.read() + __send_confirmation("Confirm", sender_mac, f"{msg_subkey} ({subtype})", payload) + exec(code) + except Exception as e: + self.master.networking.eprint(f"Error: {e} with payload: {payload}") + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, e) + else: + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, "Not authorised") + elif (msg_subkey := "Set-Admin") and subtype == msg_subcodes[msg_key][msg_subkey]: # Set Admin Bool + self.master.networking.iprint( + f"{msg_subkey} ({subtype}) command received from {sender_mac} ({self.master.networking.aen.peer_name(sender_mac)})") + if __check_authorisation(sender_mac, payload): + self.master.networking.iprint(f"Received Set-Admin command: self.admin set to {payload[0]}") + try: + self.master.admin = payload[0] + __send_confirmation("Success", sender_mac, f"{msg_subkey} ({subtype})", payload) + except Exception as e: + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, e) + else: + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, "Not authorised") + elif (msg_subkey := "Whitelist-Add") and subtype == msg_subcodes[msg_key][ + msg_subkey]: # Whitelist-Add - Add Admin macs to _whitelist + self.master.networking.iprint( + f"{msg_subkey} ({subtype}) command received from {sender_mac} ({self.master.networking.aen.peer_name(sender_mac)})") + if __check_authorisation(sender_mac, payload): + self.master.networking.iprint( + f"Received add admin macs to _whitelist command, added {payload[0]} and {payload[1]}") + try: + self._whitelist.append(ubinascii.unhexlify(payload[0].replace(':', ''))) + self._whitelist.append(ubinascii.unhexlify(payload[1].replace(':', ''))) + __send_confirmation("Success", sender_mac, f"{msg_subkey} ({subtype})", payload) + except Exception as e: + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, e) + else: + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, "Not authorised") + elif (msg_subkey := "Config-Change") and subtype == msg_subcodes[msg_key][msg_subkey]: # change config + self.master.networking.iprint( + f"{msg_subkey} ({subtype}) command received from {sender_mac} ({self.master.networking.aen.peer_name(sender_mac)})") + if __check_authorisation(sender_mac, payload): + try: + if isinstance(payload[0], dict): + new_config = payload[0] + else: + new_config = eval(payload[0]) + self.master.config.update(new_config) + if payload[1]: + file_path = "config.py" + with open(file_path, "r") as f: + lines = f.readlines() + with open(file_path, "w") as f: + for line in lines: + if line.startswith(f'config = '): + f.write(f'config = {self.master.config}\n') + else: + f.write(line) + if "sta_channel" in new_config: + self.master.networking.sta.set_channel(new_config["sta_channel"]) + if "ap_channel" in new_config: + self.master.networking.ap.set_channel(new_config["ap_channel"]) + __send_confirmation("Success", sender_mac, f"{msg_subkey} ({subtype})", payload) + except Exception as e: + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, e) + else: + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, "Not authorised") + elif (msg_subkey := "Name-Change") and subtype == msg_subcodes[msg_key][msg_subkey]: # change name + self.master.networking.iprint( + f"{msg_subkey} ({subtype}) command received from {sender_mac} ({self.master.networking.aen.peer_name(sender_mac)})") + if __check_authorisation(sender_mac, payload): + try: + self.master.config["name"] = payload[0] + if payload[1]: + file_path = "config.py" + with open(file_path, "r") as f: + lines = f.readlines() + with open(file_path, "w") as f: + for line in lines: + if line.startswith(' "name": '): + f.write(f' "name": "{payload[0]}"\n') + else: + f.write(line) + __send_confirmation("Success", sender_mac, f"{msg_subkey} ({subtype})", payload) + except Exception as e: + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, e) + else: + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, "Not authorised") + elif (msg_subkey := "Ping") and subtype == msg_subcodes[msg_key][msg_subkey]: # Ping + self.master.networking.iprint( + f"{msg_subkey} ({subtype}) command received from {sender_mac} ({self.master.networking.aen.peer_name(sender_mac)})") + self.master.networking.aen.add_peer(sender_mac, payload[2]["name"], payload[2]["id"], payload[2]["configuration"], + payload[2]["version"], payload[0], payload[1]) + if bool(self.master.networking.aen.ifidx): + channel = self.master.ap.channel() + else: + channel = self.master.sta.channel() + response = [channel, self.master.networking.aen.ifidx, self.master.config, send_timestamp] + self.master.networking.aen.send_custom(0x03, 0x10, sender_mac, response) + # elif (msg_subkey := "Pair") and subtype == msg_subcodes[msg_key][msg_subkey]: # Pair #add something that checks that the messages are from the same mac # BREAKS NETWORK + # self.master.networking.iprint(f"{msg_subkey} ({subtype}) command received from {sender_mac} ({self.peer_name(sender_mac)})") + # if self._pairing_enabled and networking_keys["handshake_key_1"] == payload: + # self._pairing = True + # self.pair(sender_mac, networking_keys["handshake_key_2"]) + # # some timer for if key 3 is not received to reset states + # elif self._pairing_enabled and self._pairing and networking_keys["handshake_key_2"] == payload: + # self._paired = True + # self._paired_macs.append(sender_mac) + # self.pair(sender_mac, networking_keys["handshake_key_3"]) + # # some timer that sets to false if key 4 is not received + # elif self._pairing_enabled and self._pairing and networking_keys["handshake_key_3"] == payload: + # try: + # # Insert pairing logic here do a reverse handshake + # self._paired = True + # self._paired_macs.append(sender_mac) + # self.pair(sender_mac, networking_keys["handshake_key_4"]) + # self.master.networking.iprint("no pairing logic written just yet") + # __send_confirmation("Success", sender_mac, f"{msg_subkey} ({subtype})", payload) + # except Exception as e: + # self.master.networking.eprint(f"Error: {e} with payload: {payload}") + # __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, e) + # elif self._pairing_enabled and self._pairing and networking_keys["handshake_key_3"] == payload: + # self._paired = True + # # remove timer from before + # else: + # __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", "Pairing disabled", payload) + # elif (msg_subkey := "Set-Pair") and subtype == msg_subcodes[msg_key][msg_subkey]: # Enable pairing mode + # self.master.networking.iprint(f"{msg_subkey} ({subtype}) command received from {sender_mac} ({self.peer_name(sender_mac)})") + # if __check_authorisation(sender_mac, payload): + # try: + # self._pairing_enabled = payload[0] + # __send_confirmation("Success", sender_mac, f"{msg_subkey} ({subtype})", payload) + # except Exception as e: + # self.master.networking.eprint(f"Error: {e} with payload: {payload}") + # __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, e) + # else: + # __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, "Not authorised") + elif (msg_subkey := "RSSI/Status/Config-Boop") and subtype == msg_subcodes[msg_key][ + msg_subkey]: # RSSI/Status/Config Boop + self.boops = self.boops + 1 + self.master.networking.iprint( + f"{msg_subkey} ({subtype}) command received from {sender_mac} ({self.master.networking.aen.peer_name(sender_mac)}), Received total of {self.boops} boops!") + try: + self.master.networking.aen.send_custom(0x02, 0x20, sender_mac,[self.master.config, self.master.version, self.master.sta.mac, self.master.ap.mac, self.master.networking.aen.rssi()]) # [ID, Name, Config, Version, sta mac, ap mac, rssi] + except Exception as e: + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, e) + elif (msg_subkey := "Directory-Get") and subtype == msg_subcodes[msg_key][ + msg_subkey]: # Get List of Files # BREAKS NETWORK + self.master.networking.iprint( + f"{msg_subkey} ({subtype}) command received from {sender_mac} ({self.master.networking.aen.peer_name(sender_mac)})") + try: + def ___list_all_files(path): + result = [] + entries = os.listdir(path) + for entry in entries: + full_path = path + "/" + entry + try: + if os.stat(full_path)[0] & 0x4000: + result.extend(___list_all_files(full_path)) + else: + result.append(full_path) + except OSError: + # Handle inaccessible files or directories + continue + return result + + start_path = '.' + all_files = ___list_all_files(start_path) + self.master.networking.aen.send_custom(0x02, 0x20, sender_mac, all_files) + except Exception as e: + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, e) + elif (msg_subkey := "Echo") and subtype == msg_subcodes[msg_key][msg_subkey]: # Echo + self.master.networking.iprint( + f"{msg_subkey} ({subtype}) command received from {sender_mac} ({self.master.networking.aen.peer_name(sender_mac)}): {payload}") # Check i or d + self.master.networking.aen.send_custom(0x03, 0x15, sender_mac, payload) + elif (msg_subkey := "Resend") and subtype == msg_subcodes[msg_key][msg_subkey]: # Resend lost long messages + self.master.networking.iprint( + f"{msg_subkey} ({subtype}) command received from {sender_mac} ({self.master.networking.aen.peer_name(sender_mac)})") + self.master.networking.iprint("Long_sent_buffer disabled due to memory constraints") + elif (msg_subkey := "WiFi-Connect") and subtype == msg_subcodes[msg_key][msg_subkey]: # Connect to Wi-Fi + self.master.networking.iprint( + f"{msg_subkey} ({subtype}) command received from {sender_mac} ({self.master.networking.aen.peer_name(sender_mac)})") + if __check_authorisation(sender_mac, payload): + try: + self.master.sta.connect(payload[0], payload[1]) + __send_confirmation("Success", sender_mac, f"{msg_subkey} ({subtype})", payload) + except Exception as e: + self.master.networking.eprint(f"Error: {e} with payload: {payload}") + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, e) + else: + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, "Not authorised") + elif (msg_subkey := "WiFi-Disconnect") and subtype == msg_subcodes[msg_key][ + msg_subkey]: # Disconnect from Wi-Fi + self.master.networking.iprint( + f"{msg_subkey} ({subtype}) command received from {sender_mac} ({self.master.networking.aen.peer_name(sender_mac)})") + if __check_authorisation(sender_mac, payload): + try: + self.master.sta.disconnect() + __send_confirmation("Success", sender_mac, f"{msg_subkey} ({subtype})", payload) + except Exception as e: + self.master.networking.eprint(f"Error: {e}") + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, e) + else: + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, "Not authorised") + elif (msg_subkey := "AP-Enable") and subtype == msg_subcodes[msg_key][msg_subkey]: # Enable AP + self.master.networking.iprint( + f"{msg_subkey} ({subtype}) command received from {sender_mac} ({self.master.networking.aen.peer_name(sender_mac)})") + if __check_authorisation(sender_mac, payload): + try: + ssid = payload[0] + if ssid == "": + ssid = self.master.config["name"] + password = payload[1] + self.master.ap.setap(ssid, password) + __send_confirmation("Success", sender_mac, f"{msg_subkey} ({subtype})", payload) + except Exception as e: + self.master.networking.eprint(f"Error: {e} with payload: {payload}") + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, e) + else: + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, "Not authorised") + elif (msg_subkey := "AP-Disable") and subtype == msg_subcodes[msg_key][msg_subkey]: # Disable AP + self.master.networking.iprint( + f"{msg_subkey} ({subtype}) command received from {sender_mac} ({self.master.networking.aen.peer_name(sender_mac)})") + if __check_authorisation(sender_mac, payload): + try: + self.master.ap.deactivate() + __send_confirmation("Success", sender_mac, f"{msg_subkey} ({subtype})", payload) + except Exception as e: + self.master.networking.iprint(f"Error: {e}") + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, e) + else: + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, "Not authorised") + elif (msg_subkey := "Pause") and subtype == msg_subcodes[msg_key][msg_subkey]: # Set Pause + self.master.networking.iprint( + f"{msg_subkey} ({subtype}) command received from {sender_mac} ({self.master.networking.aen.peer_name(sender_mac)})") + if __check_authorisation(sender_mac, payload): + try: + self.master.networking.iprint(f"Received pause command: {payload[0]}") + self._running = False + __send_confirmation("Success", sender_mac, f"{msg_subkey} ({subtype})", payload) + if self._pause_function: + self._pause_function() # calls the custom set pause function to display a screen + while not self._running: + time.sleep(0.5) + except Exception as e: + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, e) + else: + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, "Not authorised") + elif (msg_subkey := "Resume") and subtype == msg_subcodes[msg_key][msg_subkey]: # Set Continue + self.master.networking.iprint( + f"{msg_subkey} ({subtype}) command received from {sender_mac} ({self.master.networking.aen.peer_name(sender_mac)})") + if __check_authorisation(sender_mac, payload): + try: + self.master.networking.iprint(f"Received continue command: {payload}") + self.master._running = True + __send_confirmation("Success", sender_mac, f"{msg_subkey} ({subtype})", payload) + except Exception as e: + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, e) + else: + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, "Not authorised") + else: + self.master.networking.iprint( + f"Unknown command subtype from {sender_mac} ({self.master.networking.aen.peer_name(sender_mac)}): {subtype}") + + def custom_inf_handler(data): + self.master.networking.dprint("net.order.custom_inf_handler") + sender_mac, subtype, send_timestamp, receive_timestamp, payload, msg_key = data + if (msg_subkey := "Directory") and subtype == msg_subcodes[msg_key][msg_subkey]: # File Directory + self.master.networking.iprint(f"{msg_subkey} ({subtype}) data received from {sender_mac} ({self.master.networking.aen.peer_name(sender_mac)}): {payload}") + # __send_confirmation("Confirm", sender_mac, f"{msg_subkey} ({subtype})", payload) #confirm message recv + + def custom_ack_handler(data): + self.master.networking.dprint("net.order.custom_ack_handler") + sender_mac, subtype, send_timestamp, receive_timestamp, payload, msg_key = data + # data contains [sender_mac, subtype, send_timestamp, receive_timestamp, payload, msg_key] + + + self.master.networking.aen.cmd(custom_cmd_handler) + self.master.networking.aen.inf(custom_inf_handler) + self.master.networking.aen.ack(custom_ack_handler) diff --git a/software/archive/backupc3d/am1.py b/software/archive/backupc3d/am1.py new file mode 100644 index 0000000..5a9f48b --- /dev/null +++ b/software/archive/backupc3d/am1.py @@ -0,0 +1,89 @@ +from machine import Pin +import machine +from config import config + +import gc + +gc.collect() + +import network + +print("Running am1.py") + +# just to be safe +sta = network.WLAN(network.STA_IF) +ap = network.WLAN(network.AP_IF) +sta.active(True) +ap.active(True) +sta.active(False) +ap.active(False) + +from ssp_networking import SSP_Networking + +# Network +infmsg = True +dbgmsg = False +errmsg = True +configuration = config["configuration"] +if configuration == "AM1": + infmsg = True + +networking = SSP_Networking(infmsg, dbgmsg, errmsg) + +peer_mac = b'\xff\xff\xff\xff\xff\xff' + +import time + +global timer + +print("{:.3f} Name: {}, ID: {}, Configuration: {}, Sta mac: {}, Ap mac: {}, Version: {}".format( + (time.ticks_ms() - networking.inittime) / 1000, + networking.config["name"], + networking.config["id"], + networking.config["configuration"], + networking.config["ap_mac"], + networking.config["sta_mac"], + networking.config["version"] +)) + +if configuration == "AM1": + lastPressed = 0 + + message = "Boop!" + + networking.ping(peer_mac) + + def boop(pin): + global lastPressed + if (time.ticks_ms() - lastPressed > 1000): + #lastPressed = time.ticks_ms() + networking.ping(peer_mac) + #networking.echo(peer_mac, message) + #networking.send(peer_mac, time.ticks_ms()) + print(f"{(time.ticks_ms() - networking.inittime) / 1000:.3f} Networking Tool: Sent {message} to {peer_mac}") + #print(f"{(time.ticks_ms() - networking.inittime) / 1000:.3f} Networking Tool: RSSI table: {networking.rssi()}") + + # Buttons + switch_select = Pin(9, Pin.IN, Pin.PULL_UP) + switch_select.irq(trigger=Pin.IRQ_FALLING, handler=boop) + + + def heartbeat(timer): + print("") + boop(None) + print(f"{(time.ticks_ms() - networking.inittime) / 1000:.3f} Networking Tool Heartbeat: {gc.mem_free()} bytes") + print("") + gc.collect() + + + timer = machine.Timer(0) + timer.init(period=5000, mode=machine.Timer.PERIODIC, callback=heartbeat) + + +def deinit(): + networking.cleanup() + try: + timer.deinit() + except Exception as e: + print(e) + machine.reset() diff --git a/software/archive/backupc3d/boot.py b/software/archive/backupc3d/boot.py new file mode 100644 index 0000000..8974e41 --- /dev/null +++ b/software/archive/backupc3d/boot.py @@ -0,0 +1,108 @@ +import config +import time +from machine import Pin +import machine + +print("Running boot.py") + +try: + time.sleep(3) +except Exception as e: + print(f"Error {e}") + +module_name = config.config["configuration"].lower() + +import gc + +gc.collect() + +import network + +# just to be safe +sta = network.WLAN(network.STA_IF) +ap = network.WLAN(network.AP_IF) +sta.active(True) +ap.active(True) +sta.active(False) +ap.active(False) + +from ssp_networking import SSP_Networking +import ssp_networking + +# Network +infmsg = True +dbgmsg = False +errmsg = True +configuration = config.config["configuration"] + +config.networking = SSP_Networking(infmsg, dbgmsg, errmsg) +networking = config.networking + +peer_mac = b'\xff\xff\xff\xff\xff\xff' + +import time + +global timer + +print("{:.3f} Name: {}, ID: {}, Configuration: {}, Sta mac: {}, Sta channel: {}, Ap mac: {}, Ap channel: {}, Version: {}".format( + (time.ticks_ms() - networking.inittime) / 1000, + networking.config["name"], + networking.config["id"], + networking.config["configuration"], + networking.config["sta_mac"], + networking.config["sta_channel"], + networking.config["ap_mac"], + networking.config["ap_channel"], + networking.config["version"] + )) + +def am1(): + lastPressed = 0 + + message = "Boop!" + + networking.ping(peer_mac) + + def boop(pin): + global lastPressed + if (time.ticks_ms() - lastPressed > 1000): + # lastPressed = time.ticks_ms() + networking.ping(peer_mac) + # networking.echo(peer_mac, message) + # networking.send(peer_mac, time.ticks_ms()) + print(f"{(time.ticks_ms() - networking.inittime) / 1000:.3f} Networking Tool: Sent {message} to {peer_mac}") + # print(f"{(time.ticks_ms() - networking.inittime) / 1000:.3f} Networking Tool: RSSI table: {networking.rssi()}") + + # Buttons + switch_select = Pin(9, Pin.IN, Pin.PULL_UP) + switch_select.irq(trigger=Pin.IRQ_FALLING, handler=boop) + + def heartbeat(timer): + print("") + boop(None) + print(f"{(time.ticks_ms() - networking.inittime) / 1000:.3f} Networking Tool Heartbeat: {gc.mem_free()} bytes") + print("") + gc.collect() + + timer = machine.Timer(0) + timer.init(period=5000, mode=machine.Timer.PERIODIC, callback=heartbeat) + + +def deinit(): + networking.cleanup() + try: + timer.deinit() + except Exception as e: + print(e) + machine.reset() + + +try: + if configuration == "AM1": + am1() + else: + with open(module_name + ".py") as f: + code = f.read() + exec(code, {"networking": networking, "configuration": configuration, "__name__": "__main__"}) +except Exception as e: + print(f"Error running {module_name}: {e}") \ No newline at end of file diff --git a/software/archive/backupc3d/config.py b/software/archive/backupc3d/config.py new file mode 100644 index 0000000..953eaaa --- /dev/null +++ b/software/archive/backupc3d/config.py @@ -0,0 +1,81 @@ +config = {'ap_channel': 1, 'id': '64e833844490', 'name': 'Milan', 'sta_mac': '64:e8:33:84:44:90', 'ap_mac': '64:e8:33:84:44:91', 'configuration': 'AM1', 'version': '111422222410322', 'sta_channel': 1} +version = {'adxl345.py': 3, + 'hm1.py': 1, + 'ssp_networking.py': 2, + 'files.py': 2, + 'icons.py': 2, + 'prefs.py': 2, + 'sensors.py': 4, + 'servo.py': 2, + 'ssd1306.py': 2, + 'sm3.py': 1, + 'sl1.py': 1, + 'smartlight.py': 1, + 'networking.py': 4, + 'main.py': 2, + 'boot.py': 0 + } +mysecrets = {"SSID": "Tufts_Robot", "key": ""} +msg_codes = {"cmd": 0x01, "inf": 0x02, "ack": 0x03} +msg_subcodes = { + "cmd": { + "Reboot": 0x00, + "Firmware-Update": 0x01, + "File-Update": 0x02, + "File-Download": 0x03, + "File-Run": 0x05, + "Set-Admin": 0x06, + "Whitelist-Add": 0x07, + "Config-Change": 0x08, + "Name-Change": 0x09, + "Ping": 0x10, + "Pair": 0x11, + "Set-Pair": 0x12, + "RSSI/Status/Config-Boop": 0x13, + "Directory-Get": 0x14, + "Echo": 0x15, + "Resend": 0x16, + "Hive-Set": 0x17, + "Hive-Configure": 0x18, + "Web-Repl": 0x20, + "WiFi-Connect": 0x21, + "WiFi-Disconnect": 0x22, + "AP-Enable": 0x23, + "AP-Disable": 0x24, + "Pause": 0x25, + "Resume": 0x26, + }, + "inf": { + "RSSI/Status/Config-Boop": 0x20, + "Data": 0x21, + "Message": 0x22, + "Directory": 0x23, + }, + "ack": { + "Pong": 0x10, + "Success": 0x11, + "Fail": 0x12, + "Confirm": 0x13, + "Echo": 0x15, + }, +} +networking_keys = { + "default_handshake_key": "handshake", + "handshake_key1": "handshake1", + "handshake_key2": "handshake2", + "handshake_key3": "handshake3", + "handshake_key4": "handshake4", + "default_ap_key": "password", + "default_wifi_key": "password" +} +whitelist = [b'd\xe83\x84\xd8\x18', b'd\xe83\x84\xd8\x19', + b'd\xe83\x85\xd3\xbc', b'd\xe83\x85\xd3\xbd', + b'd\xe83\x84\xd8\x18', b'd\xe83\x84\xd8\x19'] #each ESP32 has two MAC addresses +i2c_dict = { + "0x3C": ["pca9685", 0, "screen"], + "0x53": ["ACCEL", 1, "accelerometer"] +} #key is i2c address: ["device name", Output (0) or Input (1), "Description"] +sensor_dict = {"sensor": [0,4095], "potentiometer": [0,180], "select": [0,1], "up": [0,1], "down": [0,1], "button": [0,1], "sw1": [0,1], "sw2": [0,1], "sw3": [0,1], "sw4": [0,1]} +hive_config = {'hive': True, 'recipients': [b'd\xe83\x84\xd8\x18'], 'sender_sensor_list': [[b'd\xe83\x84\xd8\x18', 'None']], 'refreshrate': 200, 'mode': 'None'} + +networking = None \ No newline at end of file diff --git a/software/archive/backupc3d/example.py b/software/archive/backupc3d/example.py new file mode 100644 index 0000000..957452b --- /dev/null +++ b/software/archive/backupc3d/example.py @@ -0,0 +1,91 @@ +import time + + +# Network +from ssp_networking import SSP_Networking + +# Initialise +infmsg = True +dbgmsg = False +errmsg = True +networking = SSP_Networking(infmsg, dbgmsg, errmsg) + +print(f"{(time.ticks_ms() - networking.inittime) / 1000:.3f} Networking Example: Running example.py") +print() + + +###Example code### + +recipient_mac = b'\xff\xff\xff\xff\xff\xff' # This mac sends to all +message = b'Boop' + +# Print device networking configuration +print(f"{(time.ticks_ms() - networking.inittime) / 1000:.3f} Networking Example: Config: {networking.config}") +print() + + +#Command Types: +# Ping, calculates the time until you receive a response from the peer and adds their infor to the networking address book +networking.ping(recipient_mac) +print() + +# Echo, sends a message that will be repeated back by the recipient +networking.echo(recipient_mac, message) +print() + +# There are a bunch of more command types, the complete list can be found in the config.py or ssp_networking.py + + +#Message Type +# Message, sends the specified message to the recipient, supported formats are bytearrays, bytes, int, float, string, bool, list and dict, if above 241 bytes, it will send in multiple packages: max 60928 bytes +networking.send(recipient_mac, message) +print() + +# Check if there are any messages in the message buffer, returns True or False +print(f"{(time.ticks_ms() - networking.inittime) / 1000:.3f} Networking Example: {networking.check_messages()}") +print() + +# Get Last Received Message +print(f"{(time.ticks_ms() - networking.inittime) / 1000:.3f} Networking Example: {networking.return_message()}") # Returns none, none, none if there are no messages +print() + + +#Other +# Get the RSSI table +print(f"{(time.ticks_ms() - networking.inittime) / 1000:.3f} Networking Example: {networking.rssi()}") +print() + + +#IRQ +# Set up an interrupt which runs a function as soon as possible after receiving a new message +def receive(): + for mac, message, rtime in networking.return_messages(): # You can directly iterate over the function + print(f"{(time.ticks_ms() - networking.inittime) / 1000:.3f} Networking Example: Received {message} from {peer_mac} at {rtime}") + +networking.irq(receive) # interrupt handler + + +#Send trigger +#You could set up a function that sends a message to all and is called when the select button on the dahal board is pressed +from machine import Pin +import machine + +lastPressed = 0 + +def boop(pin): + global lastPressed + if (time.ticks_ms() - lastPressed > 1000): #prevents button to be called multiple times within 1 second + lastPressed = time.ticks_ms() + networking.send(peer_mac, "Hewwo!") + print(f"{(time.ticks_ms() - networking.inittime) / 1000:.3f} Networking Example: Sent {message} to {peer_mac}") + +switch_select = Pin(9, Pin.IN, Pin.PULL_UP) +switch_select.irq(trigger=Pin.IRQ_FALLING, handler=boop) + + +#print something on the screen + +#set up servo + +#set up sensor + diff --git a/software/archive/backupc3d/main.py b/software/archive/backupc3d/main.py new file mode 100644 index 0000000..5dd8fd0 --- /dev/null +++ b/software/archive/backupc3d/main.py @@ -0,0 +1 @@ +#empty \ No newline at end of file diff --git a/software/archive/backupc3d/networking.py b/software/archive/backupc3d/networking.py new file mode 100644 index 0000000..f434404 --- /dev/null +++ b/software/archive/backupc3d/networking.py @@ -0,0 +1,783 @@ +import network +import time +import ubinascii +import espnow +import gc +import struct +import json +import machine + + +class Networking: + def __init__(self, infmsg=False, dbgmsg=False, errmsg=False, admin=False, inittime=0): + gc.collect() + self.inittime = inittime + if infmsg: + print(f"{(time.ticks_ms() - self.inittime) / 1000:.3f} Initialising Networking") + self.master = self + self.infmsg = infmsg + self.dbgmsg = dbgmsg + self.errmsg = errmsg + self.admin = admin + self.config = {"Name": None, + "Configuration": None, + "id": None, + "Version": None, + } #Just as an example, can include more information here, but for interoperability purposes please keep the basic four attributes + + self._staif = network.WLAN(network.STA_IF) + self._apif = network.WLAN(network.AP_IF) + + self.sta = self.Sta(self, self._staif) + self.ap = self.Ap(self, self._apif) + self.aen = self.Aen(self) + + if infmsg: + print(f"{(time.ticks_ms() - self.inittime) / 1000:.3f} Networking initialised and ready") + + def cleanup(self): + self.dprint(".cleanup") + self.aen.cleanup() + self._staif.active(False) + self._apif.active(False) + + def log(self, text): + if self.admin: + with open("log.txt", "a") as f: + f.write(f"{text}\n") + + def iprint(self, message): + if self.infmsg: + try: + text = f"{(time.ticks_ms() - self.inittime) / 1000:.3f} Networking Info: {message}" + print(text) + self.log(text) + except Exception as e: + print(f"Error printing Networking Info: {e}") + return + + def dprint(self, message): + if self.dbgmsg: + try: + text = f"{(time.ticks_ms() - self.inittime) / 1000:.3f} Networking Debug: {message}" + print(text) + self.log(text) + except Exception as e: + print(f"Error printing Networking Debug: {e}") + return + + def eprint(self, message): + if self.errmsg: + try: + text = f"{(time.ticks_ms() - self.inittime) / 1000:.3f} Networking Error: {message}" + print(text) + self.log(text) + except Exception as e: + print(f"Error printing Networking Error: {e}") + return + + class Sta: + def __init__(self, master, _staif): + self.master = master + self._sta = _staif + self._sta.active(True) + self.master.iprint("STA initialised and ready") + + def scan(self): + self.master.dprint("sta.scan") + scan_result = self._sta.scan() + if self.master.infmsg: + for ap in scan_result: + self.master.iprint(f"SSID:%s BSSID:%s Channel:%d Strength:%d RSSI:%d Auth:%d " % ap) + return scan_result + + def connect(self, ssid, key="", timeout=10): + self.master.dprint("sta.connect") + self._sta.connect(ssid, key) + stime = time.time() + while time.time() - stime < timeout: + if self._sta.ifconfig()[0] != '0.0.0.0': + self.master.iprint("Connected to WiFi") + return + time.sleep(0.1) + self.master.iprint(f"Failed to connect to WiFi: {self._sta.status()}") + + def disconnect(self): + self.master.dprint("sta.disconnect") + self._sta.disconnect() + + def ip(self): + self.master.dprint("sta.ip") + return self._sta.ifconfig() + + def mac(self): + self.master.dprint("sta.mac") + return bytes(self._sta.config('mac')) + + def mac_decoded(self): # Necessary? + self.master.dprint("sta.mac_decoded") + return ubinascii.hexlify(self._sta.config('mac'), ':').decode() + + def channel(self): + self.master.dprint("sta.channel") + return self._sta.config('channel') + + def set_channel(self, number): + self.master.dprint("sta.set_channel") + if number > 14 or number < 0: + number = 0 + self._sta.config(channel=number) + self.master.iprint(f"STA channel set to {number}") + + class Ap: + def __init__(self, master, _apif): + self.master = master + self._ap = _apif + self._ap.active(True) + self.master.iprint("AP initialised and ready") + + def set_ap(self, name="", password="", max_clients=10): + self.master.dprint("ap.set_ap") + if name == "": + name = self.master.config["name"] + self._ap.active(True) + self._ap.config(essid=name) + if password: + self._ap.config(authmode=network.AUTH_WPA_WPA2_PSK, password=password) + self._ap.config(max_clients=max_clients) + self.master.iprint(f"Access Point {name} set with max clients {max_clients}") + + def deactivate(self): + self.master.dprint("ap.deactivate") + self._ap.active(False) + self.master.iprint("Access Point deactivated") + + def ip(self): + self.master.dprint("ap.ip") + return self._ap.ifconfig() + + def mac(self): + self.master.dprint("ap.mac") + return bytes(self._ap.config('mac')) + + def mac_decoded(self): + self.master.dprint("ap.mac_decoded") + return ubinascii.hexlify(self._ap.config('mac'), ':').decode() + + def channel(self): + self.master.dprint("ap.channel") + return self._ap.config('channel') + + def set_channel(self, number): + self.master.dprint("ap.set_channel") + if number > 14 or number < 0: + number = 0 + self._ap.config(channel=number) + self.master.iprint(f"AP channel set to {number}") + + class Aen: + def __init__(self, master): + self.master = master + self._aen = espnow.ESPNow() + self._aen.active(True) + + self._peers = {} + self._received_messages = [] + self._received_messages_size = [] + self._long_buffer = {} + self._long_buffer_size = {} + self.received_sensor_data = {} + self.received_rssi_data = {} + self._irq_function = None + self.boop_irq = None + self.data_irq = None + self.msg_irq = None + self.ack_irq = None + self.custom_cmd = None + self.custom_inf = None + self.custom_ack = None + self.boops = 0 + self.ifidx = 0 # 0 sends via sta, 1 via ap + # self.channel = 0 + + self._aen.irq(self._irq) + + self.master.iprint("ESP-NOW initialised and ready") + + def cleanup(self): + self.master.iprint("aen.cleanup") + self.irq(None) + self._aen.active(False) + # add delete buffers and stuff + + def update_peer(self, peer_mac, peer_config=None, channel=None, ifidx=None): + self.master.dprint("aen.update_peer") + if peer_mac == b'\xff\xff\xff\xff\xff\xff': + return + if peer_mac in self._peers: + try: + if peer_config is not None: + self._peers[peer_mac].update(peer_config) + if channel is not None: + self._peers[peer_mac].update({'channel': channel}) + if ifidx is not None: + self._peers[peer_mac].update({'ifidx': ifidx}) + self.master.dprint(f"Peer {peer_mac} updated to channel {channel}, ifidx {ifidx} and name {self.peer_name(peer_mac)}") + except OSError as e: + self.master.eprint(f"Error updating peer {peer_mac}: {e}") + return + self.master.iprint(f"Peer {peer_mac} not found") + + def add_peer(self, peer_mac, peer_config=None, channel=None, ifidx=None): + self.master.dprint("aen.add_peer") + if peer_mac == b'\xff\xff\xff\xff\xff\xff': + return + if peer_mac not in self._peers: + try: + self._peers[peer_mac] = {} + if channel is not None: + self._peers[peer_mac].update({'channel': channel}) + if ifidx is not None: + self._peers[peer_mac].update({'ifidx': ifidx}) + if peer_config is not None: + self._peers[peer_mac].update(peer_config) + self._peers[peer_mac].update({'rssi': None, 'time': None, 'last_ping': 0}) + self.master.dprint(f"Peer {peer_mac} added with channel {channel}, ifidx {ifidx} and name {self.peer_name(peer_mac)}") + except OSError as e: + self.master.eprint(f"Error adding peer {peer_mac}: {e}") + else: + self.master.dprint(f"Peer {peer_mac} already exists, updating") + self.update_peer(peer_mac, peer_config, channel, ifidx) + + def remove_peer(self, peer_mac): + self.master.dprint("aen.remove_peers") + if peer_mac in self._peers: + try: + del self._peers[peer_mac] + self.master.iprint(f"Peer {peer_mac} removed") + except OSError as e: + self.master.eprint(f"Error removing peer {peer_mac}: {e}") + + def peers(self): + self.master.dprint("aen.peers") + rssi_table = self._aen.peers_table + for key in self._peers: + self._peers[key].update({'rssi': rssi_table[key][0]}) + self._peers[key].update({'time': rssi_table[key][1]-self.master.inittime}) + return self._peers + + def peer_name(self, key): + self.master.dprint("aen.name") + if isinstance(key, list): + return "..." + if key in self._peers: + if 'name' in self._peers[key]: + return self._peers[key]['name'] + else: + return None + else: + return None + + def rssi(self): + self.master.dprint("aen.rssi") + return self._aen.peers_table + + # Send cmds + def send_custom(self, msg_code, msg_subcode, mac, payload=None, channel=None, ifidx=None): + self.master.dprint("aen.send_custom") + self._compose(mac, payload, msg_code, msg_subcode, channel, ifidx) + gc.collect() + + def ping(self, mac, channel=None, ifidx=None): #Ping + self.master.dprint("aen.ping") + if bool(self.ifidx): + send_channel = self.master.ap.channel() + else: + send_channel = self.master.sta.channel() + self.send_custom(0x01, 0x10, mac, [send_channel, self.ifidx, self.master.config], channel, ifidx) # sends channel, ifidx and name + if isinstance(mac, list): + for key in mac: + self._peers[key].update({'last_ping': time.ticks_ms()}) + elif mac == b'\xff\xff\xff\xff\xff\xff': + for key in self._peers: + self._peers[key].update({'last_ping': time.ticks_ms()}) + else: + self._peers[mac].update({'last_ping': time.ticks_ms()}) + + def boop(self, mac, channel=None, ifidx=None): #"RSSI/Status/Config-Boop" + self.master.dprint("aen.boop") + self.send_custom(0x01, 0x15, mac, None, channel, ifidx) + + def echo(self, mac, message, channel=None, ifidx=None): + self.master.dprint("aen.echo") + if len(str(message)) > 241: + try: + self.master.iprint(f"Sending echo ({message}) to {mac} ({self.peer_name(mac)})") + except Exception as e: + self.master.eprint(f"Sending echo to {mac}, but error printing message content: {e}") + else: + self.master.iprint(f"Sending echo ({message}) to {mac} ({self.peer_name(mac)})") + self.send_custom(0x01, 0x15, mac, message, channel, ifidx) + gc.collect() + + def send_message(self, mac, message, channel=None, ifidx=None): + self.send(mac, message, channel, ifidx) + + def send(self, mac, message, channel=None, ifidx=None): + self.master.dprint("aen.send_message") + if len(str(message)) > 241: + try: + self.master.iprint( + f"Sending message ({str(message)[:50] + '... (truncated)'}) to {mac} ({self.peer_name(mac)})") + except Exception as e: + self.master.eprint(f"Sending message to {mac} ({self.peer_name(mac)}), but error printing message content: {e}") + gc.collect() + else: + self.master.iprint(f"Sending message ({message}) to {mac} ({self.peer_name(mac)})") + self._compose(mac, message, 0x02, 0x22, channel, ifidx) + gc.collect() + + def send_data(self, mac, message, channel=None,ifidx=None): # message is a dict, key is the sensor type and the value is the sensor value, fix name!!!!!!! + self.master.dprint("aen.message") + try: + self.master.iprint(f"Sending sensor data ({message}) to {mac} ({self.peer_name(mac)})") + except Exception as e: + self.master.eprint(f"Sending sensor data to {mac} ({self.peer_name(mac)}), but error printing message content: {e}") + self._compose(mac, message, 0x02, 0x21, channel, ifidx) + + def check_messages(self): + self.master.dprint("aen.check_message") + return len(self._received_messages) > 0 + + def return_message(self): + self.master.dprint("aen.return_message") + if self.check_messages(): + self._received_messages_size.pop() + return self._received_messages.pop() + return None, None, None + + def return_messages(self): + self.master.dprint("aen.return_messages") + if self.check_messages(): + messages = self._received_messages[:] + self._received_messages.clear() + self._received_messages_size.clear() + gc.collect() + return messages + return [(None, None, None)] + + def return_data(self): + self.master.dprint("aen.return_data") + return self.received_sensor_data + + def _irq(self, espnow): + self.master.dprint("aen._irq") + if self.master.admin: + try: + self._receive() + if self._irq_function and self.check_messages(): + self._irq_function() + gc.collect() + return + except KeyboardInterrupt: + self.master.eprint("aen._irq except KeyboardInterrupt") + self.master.cleanup() #network cleanup + raise SystemExit("Stopping networking execution. ctrl-c or ctrl-d again to stop main code") # in thonny stops library code but main code keeps running, same in terminal + else: + self._receive() + if self._irq_function and self.check_messages(): + self._irq_function() + gc.collect() + return + + def irq(self, func): + self.master.dprint("aen.irq") + self._irq_function = func + + def cmd(self, func): + self.master.dprint("aen.cmd") + self.custom_cmd = func + + def inf(self, func): + self.master.dprint("aen.inf") + self.custom_inf = func + + def ack(self, func): + self.master.dprint("aen.ack") + self.custom_ack = func + + def _send(self, peers_mac, messages, channel, ifidx, long_msg=False): + self.master.dprint("aen._send") + if isinstance(peers_mac, bytes): + peers_mac = [peers_mac] + for peer_mac in peers_mac: + try: + if channel is None: + if peer_mac in self._peers: + if 'channel' in self._peers[peer_mac]: + channel=self._peers[peer_mac]['channel'] + else: + channel = 0 + elif ifidx is None: + if peer_mac in self._peers: + if 'ifidx' in self._peers[peer_mac]: + ifidx=self._peers[peer_mac]['ifidx'] + else: + ifidx=self.ifidx + self._aen.add_peer(peer_mac, channel=channel, ifidx=ifidx) + self.master.dprint(f"Added {peer_mac} to espnow buffer with channel {channel} and ifidx {ifidx}") + except Exception as e: + self.master.eprint(f"Error adding {peer_mac} to espnow buffer: {e}") + for m in range(len(messages)): + i = 0 + while i in range(3): + i += i + ack = False + try: + ack = self._aen.send(peer_mac, messages[m], True) + self.master.dprint(f"Receipt confirm: {ack}") + if ack: + break + except Exception as e: + self.master.eprint(f"Error sending to {peer_mac}: {e}") + self.master.dprint(f"Sent {messages[m]} to {peer_mac} ({self.peer_name(peer_mac)}) with {ack}") + gc.collect() + try: + self._aen.del_peer(peer_mac) + self.master.dprint(f"Removed {peer_mac} from espnow buffer") + except Exception as e: + self.master.eprint(f"Error removing {peer_mac} from espnow buffer: {e}") + + def __send_confirmation(self, msg_type, recipient_mac, msg_subkey_type, payload=None, error=None): + if msg_type == "Success": + self._compose(recipient_mac, [msg_subkey_type, payload], 0x03, 0x11) + elif msg_type == "Fail": + self._compose(recipient_mac, [msg_subkey_type, error, payload], 0x03, 0x12) + else: + self._compose(recipient_mac, [msg_subkey_type, payload], 0x03, 0x13) + + def _compose(self, peer_mac, payload=None, msg_type=0x02, subtype=0x22, channel=None, ifidx=None): + self.master.dprint("aen._compose") + + if isinstance(peer_mac, list): + for peer_macs in peer_mac: + if peer_macs not in self._peers: + self.add_peer(peer_macs, None, channel, ifidx) + elif peer_mac not in self._peers: + self.add_peer(peer_mac, None, channel, ifidx) + + payload_type, payload_bytes = None, None + self.master.dprint("aen.__encode_payload") + if payload is None: # No payload type + payload_type, payload_bytes = b'\x00', b'' + elif isinstance(payload, bytearray): # bytearray + payload_type, payload_bytes = b'\x01', bytes(payload) + elif isinstance(payload, bytes): # bytes + payload_type, payload_bytes = b'\x01', payload + elif isinstance(payload, bool): # bool + payload_type, payload_bytes = b'\x02', (b'\x01' if payload else b'\x00') + elif isinstance(payload, int): # int + payload_type, payload_bytes = b'\x03', str(payload).encode('utf-8') + elif isinstance(payload, float): # float + payload_type, payload_bytes = b'\x04', str(payload).encode('utf-8') + elif isinstance(payload, str): # string + payload_type, payload_bytes = b'\x05', payload.encode('utf-8') + elif isinstance(payload, dict) or isinstance(payload, list): # json dict or list + json_payload = json.dumps(payload) + payload_type, payload_bytes = b'\x06', json_payload.encode('utf-8') + else: + raise ValueError("Unsupported payload type") + + messages = [] + identifier = 0x2a + timestamp = time.ticks_ms() + header = bytearray(8) + header[0] = identifier + header[1] = msg_type + header[2] = subtype + header[3:7] = timestamp.to_bytes(4, 'big') + long_msg = False + if len(payload_bytes) < 242: # 250-9=241=max_length + header[7] = payload_type[0] + total_length = 1 + 1 + 1 + 4 + 1 + len(payload_bytes) + 1 + message = bytearray(total_length) + message[:8] = header + message[8:-1] = payload_bytes + message[-1:] = (sum(message) % 256).to_bytes(1, 'big') # Checksum + self.master.dprint(f"Message {1}/{1}; Length: {len(message)}; Free memory: {gc.mem_free()}") + messages.append(message) + else: + self.master.dprint("Long message: Splitting!") + max_size = 238 # 241-3 + total_chunk_number = (-(-len(payload_bytes) // max_size)) # Round up division + lba = b'\x07' + header[7] = lba[0] # Long byte array + if total_chunk_number > 256: + raise ValueError("More than 256 chunks, unsupported") + for chunk_index in range(total_chunk_number): + message = bytearray(9 + 3 + min(max_size, len(payload_bytes) - chunk_index * max_size)) + message[:8] = header + message[8:10] = chunk_index.to_bytes(1, 'big') + total_chunk_number.to_bytes(1, 'big') + message[10] = payload_type[0] + message[11:-1] = payload_bytes[chunk_index * max_size: (chunk_index + 1) * max_size] + message[-1:] = (sum(message) % 256).to_bytes(1, 'big') # Checksum + self.master.dprint(message) + messages.append(bytes(message)) + self.master.dprint( + f"Message {chunk_index + 1}/{total_chunk_number}; length: {len(message)}; Free memory: {gc.mem_free()}") + gc.collect() + long_msg = True + gc.collect() + self._send(peer_mac, messages, channel, ifidx, long_msg) + + + def __remove_long_message_from_buffer(self, timer, key): + self.master.dprint("aen.__remove_long_message_from_buffer") + if key in self._long_buffer: + del self._long_buffer[key] + if key in self._long_buffer_size: + self._long_buffer_size[key] + gc.collect() + + def _receive(self): + self.master.dprint("aen._receive") + + + + def __process_message(self, sender_mac, message, receive_timestamp): + self.master.dprint("aen.__process_message") + if message[0] != 0x2a: # Unique Message Identifier Check + self.master.dprint("Invalid message: Message ID Fail") + return None + if len(message) < 9: # Min size + self.master.dprint("Invalid message: too short") + return None + + msg_type = int.from_bytes(message[1:2], 'big') + subtype = subtype = int.from_bytes(message[2:3], 'big') + send_timestamp = int.from_bytes(message[3:7], 'big') + payload_type = bytes(message[7:8]) + payload_bytes = message[8:-1] + checksum = message[-1] + self.master.dprint(f"{type(msg_type)}: {msg_type}, {type(subtype)}: {subtype}, {type(send_timestamp)}: {send_timestamp}, {type(payload_type)}: {payload_type}, {type(payload_bytes)}: {payload_bytes}, {type(checksum)}: {checksum}") + + # Checksum + if checksum != sum(message[:-1]) % 256: + self.master.dprint("Invalid message: checksum mismatch") + return None + + if sender_mac not in self._peers: + self.add_peer(sender_mac) + + payload = None + + if payload_type == b'\x07': + self.master.dprint("Long message received, processing...") + part_n = int.from_bytes(payload_bytes[0:1], 'big') + total_n = int.from_bytes(payload_bytes[1:2], 'big') + payload_type = bytes(payload_bytes[2:3]) + + # Create a key as a bytearray: (msg_type, subtype, timestamp, payload_type, total_n) + key = bytearray() + key.extend(message[1:2]) + key.extend(message[2:3]) + key.extend(message[3:7]) + key.extend(payload_type) + key.extend(payload_bytes[1:2]) + key = bytes(key) + self.master.dprint(f"Key: {key}") + + payload_bytes = payload_bytes[3:] + + # Check if the key already exists in the long buffer + if key in self._long_buffer: + # If the part is None, add the payload + if self._long_buffer[key][part_n] is None: + self._long_buffer[key][part_n] = payload_bytes + self._long_buffer_size[key] = self._long_buffer_size[key] + len(payload_bytes) + self.master.dprint( + f"Long message: Key found, message added to entry in long_message_buffer, {sum(1 for item in self._long_buffer[key] if item is not None)} out of {total_n} packages received") + # If there are still missing parts, return + if any(value is None for value in self._long_buffer[key]): + gc.collect() + return + else: + return + else: + # Initialize the long message buffer for this key + payloads = [None] * total_n + payloads[part_n] = payload_bytes + self._long_buffer[key] = payloads + self._long_buffer_size[key] = len(payload_bytes) + self.master.dprint( + f"Long message: Key not found and new entry created in long_message_buffer, {sum(1 for item in self._long_buffer[key] if item is not None)} out of {total_n} packages received") + + while len(self._long_buffer) > 8 or sum(self._long_buffer_size.values()) > 75000: + self.master.dprint( + f"Maximum buffer size reached: {len(self._long_buffer)}, {sum(self._long_buffer_size.values())} bytes; Reducing!") + oldest_key = next(iter(self._long_buffer)) + del self._long_buffer[oldest_key] + del self._long_buffer_size[oldest_key] + gc.collect() + timer = machine.Timer(0) + timer.init(period=10000, mode=machine.Timer.ONE_SHOT, callback=lambda t: self.__remove_long_message_from_buffer(t, key)) + return + + # If all parts have been received, reconstruct the message + if not any(value is None for value in self._long_buffer[key]): + payload_bytes = bytearray() + for i in range(0, total_n): + payload_bytes.extend(self._long_buffer[key][i]) + del self._long_buffer[key] + del self._long_buffer_size[key] + self.master.dprint("Long message: All packages received!") + else: + self.master.dprint("Long Message: Safeguard triggered, code should not have gotten here") + gc.collect() + return + + payload_size = len(payload_bytes) + + self.master.dprint("aen.__decode_payload") + if payload_type == b'\x00': # None + payload = None + elif payload_type == b'\x01': # bytearray or bytes + payload = bytes(payload_bytes) + elif payload_type == b'\x02': # bool + payload = payload_bytes[0:1] == b'\x01' + elif payload_type == b'\x03': # int + payload = int(payload_bytes.decode('utf-8')) + elif payload_type == b'\x04': # float + payload = float(payload_bytes.decode('utf-8')) + elif payload_type == b'\x05': # string + payload = payload_bytes.decode('utf-8') + elif payload_type == b'\x06': # json dict or list + payload = json.loads(payload_bytes.decode('utf-8')) #use eval instead? #FIX + elif payload_type == b'\x07': # Long byte array + payload = bytes(payload_bytes) + else: + raise ValueError(f"Unsupported payload type: {payload_type} Message: {payload_bytes}") + + # Handle the message based on type + if msg_type == 0x01: # Command Message + msg_key = "cmd" + __handle_cmd(self, sender_mac, subtype, send_timestamp, receive_timestamp, payload, payload_size, msg_key) + elif msg_type == 0x02: # Informational Message + msg_key = "inf" + __handle_inf(self, sender_mac, subtype, send_timestamp, receive_timestamp, payload, payload_size, msg_key) + elif msg_type == 0x03: # Acknowledgement Message + msg_key = "ack" + __handle_ack(self, sender_mac, subtype, send_timestamp, receive_timestamp, payload, payload_size, msg_key) + else: + self.master.iprint(f"Unknown message type from {sender_mac} ({self.peer_name(sender_mac)}): {message}") + + def __handle_cmd(self, sender_mac, subtype, send_timestamp, receive_timestamp, payload, payload_size, msg_key): + self.master.dprint(f"aen.__handle_cmd") + if (msg_subkey := "Ping") and subtype == 0x01 or subtype == 0x10: # Ping + self.master.iprint(f"{msg_subkey} ({subtype}) command received from {sender_mac} ({self.peer_name(sender_mac)})") + self.add_peer(sender_mac, payload[2], payload[0], payload[1]) + if bool(self.ifidx): + channel = self.master.ap.channel() + else: + channel = self.master.sta.channel() + response = [channel, self.ifidx, self.master.config, send_timestamp] + self._compose(sender_mac, response, 0x03, 0x10) + elif (msg_subkey := "RSSI/Status/Config-Boop") and subtype == 0x03 or subtype == 0x13: # RSSI/Status/Config Boop + self.boops = self.boops + 1 + self.master.iprint(f"{msg_subkey} ({subtype}) command received from {sender_mac} ({self.peer_name(sender_mac)}), Received total of {self.boops} boops!") + try: + self._compose(sender_mac, [self.master.config, self.master.version, self.master.sta.mac, self.master.ap.mac, self.rssi()], 0x02, 0x20) # [ID, Name, Config, Version, sta mac, ap mac, rssi] + except Exception as e: + self.master.aen.__send_confirmation(self, "Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, e) + elif (msg_subkey := "Echo") and subtype == subtype == 0x02 or subtype == 0x15: # Echo + self.master.iprint(f"{msg_subkey} ({subtype}) command received from {sender_mac} ({self.peer_name(sender_mac)}): {payload}") # Check i or d + self._compose(sender_mac, payload, 0x03, 0x15) + else: + if self.custom_cmd: + self.master.iprint(f"Checking custom cmd handler library") + self.custom_cmd([sender_mac, subtype, send_timestamp, receive_timestamp, payload, msg_key]) + self.master.iprint(f"Unknown command subtype from {sender_mac} ({self.peer_name(sender_mac)}): {subtype}") + + def __handle_inf(self, sender_mac, subtype, send_timestamp, receive_timestamp, payload, payload_size, msg_key): + self.master.dprint("aen.__handle_inf") + if (msg_subkey := "RSSI/Status/Config-Boop") and subtype == 0x00 or subtype == 0x20: # RSSI/Status/Config-Boop + self.master.iprint(f"{msg_subkey} ({subtype}) data received from {sender_mac} ({self.peer_name(sender_mac)}): {payload}") + self.received_rssi_data[sender_mac] = payload + # self.master.aen.__send_confirmation(self, "Confirm", sender_mac, f"{msg_subkey} ({subtype})", payload) #confirm message recv + if self.boop_irq: + self.boop_irq() + elif (msg_subkey := "Data") and subtype == 0x01 or subtype == 0x21: # Sensor Data + payload["time_sent"] = send_timestamp + payload["time_recv"] = receive_timestamp + self.master.iprint(f"{msg_subkey} ({subtype}) data received from {sender_mac} ({self.peer_name(sender_mac)}): {payload}") + if sender_mac in self.received_sensor_data: + self.received_sensor_data[b'prev_' + sender_mac] = self.received_sensor_data[sender_mac] + self.received_sensor_data[sender_mac] = payload + # self.master.aen.__send_confirmation(self, "Confirm", sender_mac, f"{msg_subkey} ({subtype})", payload) #confirm message recv + if self.data_irq: + self.data_irq() + elif (msg_subkey := "Message") and subtype == 0x02 or subtype == 0x22: # Message / Other + self.master.iprint(f"{msg_subkey} ({subtype}) received from {sender_mac} ({self.peer_name(sender_mac)}): {payload}") + self._received_messages.append((sender_mac, payload, receive_timestamp)) + self._received_messages_size.append(payload_size) + while len(self._received_messages) > 2048 or sum(self._received_messages_size) > 20000: + self.master.dprint(f"Maximum buffer size reached: {len(self._received_messages)}, {sum(self._received_messages_size)} bytes; Reducing!") + self._received_messages.pop(0) + self._received_messages_size.pop(0) + # self.master.aen.__send_confirmation(self, "Confirm", sender_mac, f"{msg_subkey} ({subtype})", payload) #confirm message recv + if self.msg_irq: + self.msg_irq() + else: + if self.custom_inf: + self.master.iprint(f"Checking custom inf handler library") + self.custom_inf([sender_mac, subtype, send_timestamp, receive_timestamp, payload, msg_key]) + self.master.iprint(f"Unknown info subtype from {sender_mac} ({self.peer_name(sender_mac)}): {subtype}") + + def __handle_ack(self, sender_mac, subtype, send_timestamp, receive_timestamp, payload, payload_size, msg_key): + self.master.dprint("aen.__handle_ack") + if (msg_subkey := "Pong") and subtype == 0x10: # Pong + self.add_peer(sender_mac, payload[2], payload[0], payload[1]) + self.master.iprint(f"{msg_subkey} ({subtype}) received from {sender_mac} ({self.peer_name(sender_mac)}), {receive_timestamp - payload[3]}") + elif (msg_subkey := "Echo") and subtype == 0x15: # Echo + self.master.iprint(f"{msg_subkey} ({subtype}) received from {sender_mac} ({self.peer_name(sender_mac)}), {payload}") + self.last_echo = payload + elif (msg_subkey := "Success") and subtype == 0x11: # Success + # payload should return a list with a cmd type and payload + self.master.iprint(f"{msg_subkey} ({subtype}) received from {sender_mac} ({self.peer_name(sender_mac)}) for type {payload[0]} with payload {payload[1]}") + # add to ack buffer + elif (msg_subkey := "Fail") and subtype == 0x12: # Fail + # payload should return a list with a cmd type, error and payload + self.master.iprint(f"{msg_subkey} ({subtype}) received from {sender_mac} ({self.peer_name(sender_mac)}) for type {payload[0]} with error {payload[1]} and payload {payload[2]}") + # add to ack buffer + elif (msg_subkey := "Confirm") and subtype == 0x13: # Confirmation + # payload should return a list with message type and payload + self.master.iprint(f"{msg_subkey} ({subtype}) received from {sender_mac} ({self.peer_name(sender_mac)}) for type {payload[0]} with payload {payload[1]}") + # add to ack buffer + else: + if self.custom_ack: + self.master.iprint(f"Checking custom ack handler library") + self.custom_ack([sender_mac, subtype, send_timestamp, receive_timestamp, payload, msg_key]) + self.master.iprint(f"Unknown ack subtype from {sender_mac} ({self.peer_name(sender_mac)}): {subtype}, Payload: {payload}") + # Insert more acknowledgement logic here and/or add message to acknowledgement buffer + if self.ack_irq: + self.ack_irq() + + if self._aen.any(): + timestamp = time.ticks_ms() + for mac, data in self._aen: + self.master.dprint(f"Received {mac, data}") + if mac is None: # mac, msg will equal (None, None) on timeout + break + if data: + if mac and data is not None: + # self._received_messages.append((sender_mac, data, receive_timestamp))#Messages will be saved here, this is only for debugging purposes + __process_message(self, mac, data, timestamp) + if not self._aen.any(): # this is necessary as the for loop gets stuck and does not exit properly. + break + +# message structure (what kind of message types do I need?: Command which requires me to do something (ping, pair, change state(update, code, mesh mode, run a certain file), Informational Message (Sharing Sensor Data and RSSI Data) +# | Header (1 byte) | Type (1 byte) | Subtype (1 byte) | Timestamp (ms ticks) (4 bytes) | Payload type (1) | Payload (variable) | Checksum (1 byte) | + + + + diff --git a/software/archive/backupc3d/ssp_networking.py b/software/archive/backupc3d/ssp_networking.py new file mode 100644 index 0000000..a4809a4 --- /dev/null +++ b/software/archive/backupc3d/ssp_networking.py @@ -0,0 +1,622 @@ +from config import mysecrets, config, whitelist, i2c_dict, version, msg_codes, msg_subcodes, networking_keys +from networking import Networking +import ubinascii +import machine +import time +import os +import gc +import webrepl + +class SSP_Networking: + def __init__(self, infmsg=False, dbgmsg=False, errmsg=False, admin=False, inittime=0): + if infmsg: + print(f"{(time.ticks_ms() - inittime) / 1000:.3f} Initialising Smart System Platform Networking") + self.networking = Networking(infmsg, dbgmsg, errmsg, admin, inittime) + config["id"] = ubinascii.hexlify(machine.unique_id()).decode() + config["version"] = ''.join(str(value) for value in version.values()) + config["ap_mac"] = self.networking.ap.mac_decoded() + config["sta_mac"] = self.networking.sta.mac_decoded() + self.networking.config = config + self.config = self.networking.config + self.version = version + self.orders = self.Orders(self) + self.inittime = self.networking.inittime + try: + if self.config["ap_channel"] is not None: + self.networking.ap.set_channel(self.config["ap_channel"]) + if self.config["sta_channel"] is not None: + self.networking.sta.set_channel(self.config["sta_channel"]) + except Exception as e: + self.networking.eprint(f"Error: {e}") + try: + file_path = "config.py" + with open(file_path, "r") as f: + lines = f.readlines() + with open(file_path, "w") as f: + for line in lines: + if line.startswith(f'config = '): + f.write(f'config = {self.config}\n') + else: + f.write(line) + except Exception as e: + self.networking.eprint(f"Error: {e}") + + def cleanup(self): + self.networking.cleanup() + + def rssi(self): + return self.networking.aen.rssi() + + def peers(self): + return self.networking.aen.peers() + + def wpeers(self): + self.networking.iprint(f"time.ticks_ms(): {time.ticks_ms()}") + original_dict = self.networking.aen.peers() + #decoded_dict = {ubinascii.hexlify(key, ':').decode(): value for key, value in original_dict.items()} + networking_peer_info = f"networking_peers_info_start{original_dict}networking_peers_info_end" + print(networking_peer_info) + + def irq(self, func): + self.networking.aen.irq(func) + + def check_messages(self): + return self.networking.aen.check_messages() + + def return_message(self): + return self.networking.aen.return_message() + + def return_messages(self): + return self.networking.aen.return_messages() + + def return_data(self): + return self.networking.aen.return_data() + + def send_custom(self, msg_subkey, mac, payload=None, channel=None, ifidx=None, sudo=False): + self.networking.dprint("aen.send_custom") + if sudo and isinstance(payload, list): + payload.append("sudo") + elif sudo and payload is None: + payload = ["sudo"] + else: + payload = [payload, "sudo"] + if (msg_key := "cmd") and msg_subkey in msg_subcodes[msg_key]: + if isinstance(mac, list): + self.networking.iprint(f"Sending {msg_subkey} ({bytes([msg_subcodes[msg_key][msg_subkey]])}) command to {mac}") + else: + self.networking.iprint(f"Sending {msg_subkey} ({bytes([msg_subcodes[msg_key][msg_subkey]])}) command to {mac} ({self.networking.aen.peer_name(mac)})") + self.networking.aen.send_custom(msg_codes[msg_key], msg_subcodes[msg_key][msg_subkey], mac, payload, channel, ifidx) + else: + self.networking.iprint(f"Command {msg_subkey} not found") + gc.collect() + + def ping(self, mac, channel=None, ifidx=None): + self.networking.dprint("net.cmd.ping") + self.networking.aen.ping(mac, channel, ifidx) + + def echo(self, mac, message, channel=None, ifidx=None): + self.networking.dprint("net.cmd.echo") + self.networking.aen.echo(mac, message, channel, ifidx) + + def boop(self, mac, channel=None, ifidx=None, sudo=False): + self.networking.dprint("net.cmd.boop") + self.networking.aen.boop(mac, channel, ifidx) + + def send(self, mac, message, channel=None, ifidx=None): + self.networking.dprint("net.cmd.message") + self.networking.aen.send(mac, message, channel, ifidx) + + def broadcast(self, message, channel=None, ifidx=None): + self.networking.dprint("net.cmd.broadcast") + mac = b'\xff\xff\xff\xff\xff\xff' + self.send(mac, message, channel, ifidx) + + def send_data(self, mac, message, channel=None, ifidx=None): # message is a dict, key is the sensor type and the value is the sensor value + self.networking.dprint("net.cmd.message") + self.networking.aen.send_data(mac, message, channel, ifidx) + + def reboot(self, mac, channel=None, ifidx=None, sudo=False): + self.networking.dprint("net.cmd.reboot") + self.send_custom("Reboot", mac, None, channel, ifidx, sudo) + + def hive_set(self, mac, hive_bool, channel=None, ifidx=None, sudo=False): + self.networking.dprint("net.cmd.hive_set") + self.send_custom("Hive-Set", mac, hive_bool, channel, ifidx, sudo) + + def hive_configure(self, mac, hive_config, channel=None, ifidx=None, sudo=False): + self.networking.dprint("net.cmd.hive_configure") + self.send_custom("Hive-Configure", mac, f"{hive_config}", channel, ifidx, sudo) + + def firmware_update(self, mac, channel=None, ifidx=None, sudo=False): + self.networking.dprint("net.cmd.firmware_update") + self.send_custom("Firmware-Update", mac, None, channel, ifidx, sudo) + + def file_update(self, mac, channel=None, ifidx=None, sudo=False): + self.networking.dprint("net.cmd.file_update") + self.send_custom("File-Update", mac, None, channel, ifidx, sudo) + + def file_download(self, mac, link, file_list=None, channel=None, ifidx=None, sudo=False): + self.networking.dprint("net.cmd.file_download") + self.send_custom("File-Download", mac, [link, file_list], channel, ifidx, sudo) + + def web_repl(self, mac, channel=None, ifidx=None, sudo=False): + self.networking.dprint("net.cmd.web_repl") + self.networking.ap.set_ap(ap_name := self.networking.config["name"], password := networking_keys["default_ap_key"]) + self.send_custom("Web-Repl", mac, [ap_name, password], channel, ifidx, sudo) + # await success message and if success False disable AP or try again + + def file_run(self, mac, filename, channel=None, ifidx=None, sudo=False): + self.networking.dprint("net.cmd.file_run") + self.send_custom("File-Run", mac, filename, channel, ifidx, sudo) + + def admin_set(self, mac, new_bool, channel=None, ifidx=None, sudo=False): + self.networking.dprint("net.cmd.admin_set") + self.send_custom("Admin-Set", mac, new_bool, channel, ifidx, sudo) + + def whitelist_add(self, mac, mac_list=None, channel=None, ifidx=None, sudo=False): + self.networking.dprint("net.cmd.whitelist_add") + if mac_list is not None: + mac_list = [self.networking.sta.mac_decoded, self.networking.ap.mac_decoded] + self.send_custom("Whitelist-Add", mac, mac_list, channel, ifidx, sudo) + + def config_change(self, mac, new_config, hardcode=False, channel=None, ifidx=None, sudo=False): + self.networking.dprint("net.cmd.config_change") + self.send_custom("Config-Change", mac, [new_config, hardcode], channel, ifidx, sudo) + + def name_change(self, mac, new_name, hardcode=False, channel=None, ifidx=None, sudo=False): + self.networking.dprint("net.cmd.name_change") + self.send_custom("Name-Change", mac, [new_name, hardcode], channel, ifidx, sudo) + + def pair(self, mac, key=networking_keys["handshake_key1"], channel=None, ifidx=None): + self.networking.dprint("net.cmd.pair") + self.send_custom("Pair", mac, key, channel, ifidx) + + def pair_enable(self, mac, pair_bool, channel=None, ifidx=None, sudo=False): + self.networking.dprint("net.cmd.pair") + self.send_custom("Set-Pair", mac, pair_bool, channel, ifidx, sudo) + + def directory_get(self, mac, channel=None, ifidx=None, sudo=False): + self.networking.dprint("net.cmd.directory_get") + self.send_custom("Directory-Get", mac, None, channel, ifidx, sudo) + + # resend cmd + + def wifi_connect(self, mac, name, password, channel=None, ifidx=None, sudo=False): + self.networking.dprint("net.cmd.wifi_connect") + self.send_custom("Wifi-Connect", mac, [name, password], channel, ifidx, sudo) + + def wifi_disconnect(self, mac, channel=None, ifidx=None, sudo=False): + self.networking.dprint("net.cmd.wifi_disconnect") + self.send_custom("Wifi-Disconnect", mac, None, channel, ifidx, sudo) + + def ap_enable(self, mac, name, password, channel=None, ifidx=None, sudo=False): + self.networking.dprint("net.cmd.ap_enable") + self.send_custom("AP-Enable", mac, [name, password], channel, ifidx, sudo) + + def ap_disable(self, mac, channel=None, ifidx=None, sudo=False): + self.networking.dprint("net.cmd.ap_disable") + self.send_custom("AP-Disable", mac, None, channel, ifidx, sudo) + + def pause(self, mac, channel=None, ifidx=None, sudo=False): + self.networking.dprint("net.cmd.pause") + self.send_custom("Pause", mac, None, channel, ifidx, sudo) + + def resume(self, mac, channel=None, ifidx=None, sudo=False): + self.networking.dprint("net.cmd.resume") + self.send_custom("Resume", mac, None, channel, ifidx, sudo) + + + class Orders: + def __init__(self, master): + self.master = master + self.master.networking.dprint("net.cmd.orders") + self._whitelist = whitelist + + self._pause_function = None + + # Flags + self._pairing_enabled = True + self._pairing = False + self._paired = False + self._paired_macs = [] + self._running = True + + def __check_authorisation(sender_mac, payload): + return sender_mac in self._whitelist or payload == "sudo" or payload[-1] == "sudo" + + def __send_confirmation(msg_type, recipient_mac, msg_subkey_type, payload=None, error=None): + self.master.networking.dprint("net.order.__send_confirmation") + self.master.networking.aen.__send_confirmation(msg_type, recipient_mac, msg_subkey_type, payload, error) + + def custom_cmd_handler(data): + self.master.networking.dprint("net.order.custom_cmd_handler") + sender_mac, subtype, send_timestamp, receive_timestamp, payload, msg_key = data + if (msg_subkey := "Reboot") and subtype == msg_subcodes[msg_key][msg_subkey]: # Reboot + self.master.networking.iprint(f"{msg_subkey} ({subtype}) command received from {sender_mac} ({self.master.networking.aen.peer_name(sender_mac)})") + if __check_authorisation(sender_mac, payload): + __send_confirmation("Confirm", sender_mac, f"{msg_subkey} ({subtype})", payload) + machine.reset() + else: + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, "Not authorised") + elif (msg_subkey := "Hive-Set") and subtype == msg_subcodes[msg_key][msg_subkey]: # Hive-Set + self.master.networking.iprint(f"{msg_subkey} ({subtype}) command received from {sender_mac} ({self.master.networking.aen.peer_name(sender_mac)})") + if __check_authorisation(sender_mac, payload): + try: + from config import hive_config + hive_config["hive"] = payload[0] + file_path = "config.py" + with open(file_path, "r") as f: + lines = f.readlines() + with open(file_path, "w") as f: + for line in lines: + if line.startswith(f'hive_config = '): + f.write(f'hive_config = {hive_config}\n') + else: + f.write(line) + __send_confirmation("Confirm", sender_mac, f"{msg_subkey} ({subtype})", payload) + machine.reset() + except Exception as e: + self.master.networking.eprint(f"Error: {e} with payload: {payload}") + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, e) + else: + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, "Not authorised") + elif (msg_subkey := "Hive-Configure") and subtype == msg_subcodes[msg_key][msg_subkey]: # Hive-Configure + self.master.networking.iprint(f"{msg_subkey} ({subtype}) command received from {sender_mac} ({self.master.networking.aen.peer_name(sender_mac)})") + if __check_authorisation(sender_mac, payload): + try: + from config import hive_config + new_config = payload[0] + hive_config.update(new_config) + file_path = "config.py" + with open(file_path, "r") as f: + lines = f.readlines() + with open(file_path, "w") as f: + for line in lines: + if line.startswith(f'hive_config = '): + f.write(f'hive_config = {hive_config}\n') + else: + f.write(line) + __send_confirmation("Success", sender_mac, f"{msg_subkey} ({subtype})", payload) + machine.reset() + except Exception as e: + self.master.networking.eprint(f"Error: {e} with payload: {payload}") + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, e) + else: + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, "Not authorised") + elif (msg_subkey := "Firmware-Update") and subtype == msg_subcodes[msg_key][msg_subkey]: # Firmware-Update + self.master.networking.iprint(f"{msg_subkey} ({subtype}) command received from {sender_mac} ({self.master.networking.aen.peer_name(sender_mac)})") + if __check_authorisation(sender_mac, payload): + try: + # Insert update logic here + self.master.networking.iprint("no update logic written just yet") + __send_confirmation("Success", sender_mac, f"{msg_subkey} ({subtype})", payload) + except Exception as e: + self.master.networking.eprint(f"Error: {e} with payload: {payload}") + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, e) + else: + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, "Not authorised") + elif (msg_subkey := "File-Update") and subtype == msg_subcodes[msg_key][msg_subkey]: # File-Update + self.master.networking.iprint(f"{msg_subkey} ({subtype}) command received from {sender_mac} ({self.master.networking.aen.peer_name(sender_mac)})") + if __check_authorisation(sender_mac, payload): + try: + # Insert update logic here + self.master.networking.iprint("No update logic written just yet") + __send_confirmation("Success", sender_mac, f"{msg_subkey} ({subtype})", payload) + except Exception as e: + self.master.networking.eprint(f"Error: {e} with payload: {payload}") + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, e) + else: + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, "Not authorised") + elif (msg_subkey := "File-Download") and subtype == msg_subcodes[msg_key][msg_subkey]: # File-Download + self.master.networking.iprint(f"{msg_subkey} ({subtype}) command received from {sender_mac} ({self.master.networking.aen.peer_name(sender_mac)})") + # should return a list with a link and the list of files to download + if __check_authorisation(sender_mac, payload): + try: + # import mip + # base = payload[0] + # files_to_copy = payload[1] + # if files_to_copy is None: + # mip.install(base) + # else: + # for f in files_to_copy: + # mip.install(base + f) + __send_confirmation("Success", sender_mac, f"{msg_subkey} ({subtype})", payload) + except Exception as e: + self.master.networking.eprint(f"Error: {e} with payload: {payload}") + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, e) + else: + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, "Not authorised") + elif (msg_subkey := "Web-Repl") and subtype == msg_subcodes[msg_key][msg_subkey]: # Web-Repl + self.master.networking.iprint(f"{msg_subkey} ({subtype}) command received from {sender_mac} ({self.master.networking.aen.peer_name(sender_mac)})") + # should be a list with name and password + if __check_authorisation(sender_mac, payload): + try: + # add logic to connect to Wi-Fi and set up webrepl + webrepl.start() + self.master.sta.connect(payload[0], payload[1]) + link = "webrepl link" + __send_confirmation("Success", sender_mac, f"{msg_subkey} ({subtype})", link) + except Exception as e: + self.master.networking.eprint(f"Error: {e} with payload: {payload}") + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, e) + else: + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, "Not authorised") + elif (msg_subkey := "File-Run") and subtype == msg_subcodes[msg_key][msg_subkey]: # File-Run + self.master.networking.iprint( + f"{msg_subkey} ({subtype}) command received from {sender_mac} ({self.master.networking.aen.peer_name(sender_mac)})") + if __check_authorisation(sender_mac, payload): + try: + file_name = payload[0] + if not file_name.endswith(".py"): + file_name += ".py" + with open(file_name) as f: + code = f.read() + __send_confirmation("Confirm", sender_mac, f"{msg_subkey} ({subtype})", payload) + exec(code) + except Exception as e: + self.master.networking.eprint(f"Error: {e} with payload: {payload}") + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, e) + else: + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, "Not authorised") + elif (msg_subkey := "Set-Admin") and subtype == msg_subcodes[msg_key][msg_subkey]: # Set Admin Bool + self.master.networking.iprint( + f"{msg_subkey} ({subtype}) command received from {sender_mac} ({self.master.networking.aen.peer_name(sender_mac)})") + if __check_authorisation(sender_mac, payload): + self.master.networking.iprint(f"Received Set-Admin command: self.admin set to {payload[0]}") + try: + self.master.admin = payload[0] + __send_confirmation("Success", sender_mac, f"{msg_subkey} ({subtype})", payload) + except Exception as e: + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, e) + else: + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, "Not authorised") + elif (msg_subkey := "Whitelist-Add") and subtype == msg_subcodes[msg_key][ + msg_subkey]: # Whitelist-Add - Add Admin macs to _whitelist + self.master.networking.iprint( + f"{msg_subkey} ({subtype}) command received from {sender_mac} ({self.master.networking.aen.peer_name(sender_mac)})") + if __check_authorisation(sender_mac, payload): + self.master.networking.iprint( + f"Received add admin macs to _whitelist command, added {payload[0]} and {payload[1]}") + try: + self._whitelist.append(ubinascii.unhexlify(payload[0].replace(':', ''))) + self._whitelist.append(ubinascii.unhexlify(payload[1].replace(':', ''))) + __send_confirmation("Success", sender_mac, f"{msg_subkey} ({subtype})", payload) + except Exception as e: + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, e) + else: + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, "Not authorised") + elif (msg_subkey := "Config-Change") and subtype == msg_subcodes[msg_key][msg_subkey]: # change config + self.master.networking.iprint( + f"{msg_subkey} ({subtype}) command received from {sender_mac} ({self.master.networking.aen.peer_name(sender_mac)})") + if __check_authorisation(sender_mac, payload): + try: + self.master.config.update(payload[0]) + if payload[1]: + file_path = "config.py" + with open(file_path, "r") as f: + lines = f.readlines() + with open(file_path, "w") as f: + for line in lines: + if line.startswith(f'config = '): + f.write(f'config = {self.master.config}\n') + else: + f.write(line) + __send_confirmation("Success", sender_mac, f"{msg_subkey} ({subtype})", payload) + except Exception as e: + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, e) + else: + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, "Not authorised") + elif (msg_subkey := "Name-Change") and subtype == msg_subcodes[msg_key][msg_subkey]: # change name + self.master.networking.iprint( + f"{msg_subkey} ({subtype}) command received from {sender_mac} ({self.master.networking.aen.peer_name(sender_mac)})") + if __check_authorisation(sender_mac, payload): + try: + self.master.config["name"] = payload[0] + if payload[1]: + file_path = "config.py" + with open(file_path, "r") as f: + lines = f.readlines() + with open(file_path, "w") as f: + for line in lines: + if line.startswith(' "name": '): + f.write(f' "name": "{payload[0]}"\n') + else: + f.write(line) + __send_confirmation("Success", sender_mac, f"{msg_subkey} ({subtype})", payload) + except Exception as e: + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, e) + else: + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, "Not authorised") + elif (msg_subkey := "Ping") and subtype == msg_subcodes[msg_key][msg_subkey]: # Ping + self.master.networking.iprint( + f"{msg_subkey} ({subtype}) command received from {sender_mac} ({self.master.networking.aen.peer_name(sender_mac)})") + self.master.networking.aen.add_peer(sender_mac, payload[2]["name"], payload[2]["id"], payload[2]["configuration"], + payload[2]["version"], payload[0], payload[1]) + if bool(self.master.networking.aen.ifidx): + channel = self.master.ap.channel() + else: + channel = self.master.sta.channel() + response = [channel, self.master.networking.aen.ifidx, self.master.config, send_timestamp] + self.master.networking.aen.send_custom(0x03, 0x10, sender_mac, response) + # elif (msg_subkey := "Pair") and subtype == msg_subcodes[msg_key][msg_subkey]: # Pair #add something that checks that the messages are from the same mac # BREAKS NETWORK + # self.master.networking.iprint(f"{msg_subkey} ({subtype}) command received from {sender_mac} ({self.peer_name(sender_mac)})") + # if self._pairing_enabled and networking_keys["handshake_key_1"] == payload: + # self._pairing = True + # self.pair(sender_mac, networking_keys["handshake_key_2"]) + # # some timer for if key 3 is not received to reset states + # elif self._pairing_enabled and self._pairing and networking_keys["handshake_key_2"] == payload: + # self._paired = True + # self._paired_macs.append(sender_mac) + # self.pair(sender_mac, networking_keys["handshake_key_3"]) + # # some timer that sets to false if key 4 is not received + # elif self._pairing_enabled and self._pairing and networking_keys["handshake_key_3"] == payload: + # try: + # # Insert pairing logic here do a reverse handshake + # self._paired = True + # self._paired_macs.append(sender_mac) + # self.pair(sender_mac, networking_keys["handshake_key_4"]) + # self.master.networking.iprint("no pairing logic written just yet") + # __send_confirmation("Success", sender_mac, f"{msg_subkey} ({subtype})", payload) + # except Exception as e: + # self.master.networking.eprint(f"Error: {e} with payload: {payload}") + # __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, e) + # elif self._pairing_enabled and self._pairing and networking_keys["handshake_key_3"] == payload: + # self._paired = True + # # remove timer from before + # else: + # __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", "Pairing disabled", payload) + # elif (msg_subkey := "Set-Pair") and subtype == msg_subcodes[msg_key][msg_subkey]: # Enable pairing mode + # self.master.networking.iprint(f"{msg_subkey} ({subtype}) command received from {sender_mac} ({self.peer_name(sender_mac)})") + # if __check_authorisation(sender_mac, payload): + # try: + # self._pairing_enabled = payload[0] + # __send_confirmation("Success", sender_mac, f"{msg_subkey} ({subtype})", payload) + # except Exception as e: + # self.master.networking.eprint(f"Error: {e} with payload: {payload}") + # __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, e) + # else: + # __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, "Not authorised") + elif (msg_subkey := "RSSI/Status/Config-Boop") and subtype == msg_subcodes[msg_key][ + msg_subkey]: # RSSI/Status/Config Boop + self.boops = self.boops + 1 + self.master.networking.iprint( + f"{msg_subkey} ({subtype}) command received from {sender_mac} ({self.master.networking.aen.peer_name(sender_mac)}), Received total of {self.boops} boops!") + try: + self.master.networking.aen.send_custom(0x02, 0x20, sender_mac,[self.master.config, self.master.version, self.master.sta.mac, self.master.ap.mac, self.master.networking.aen.rssi()]) # [ID, Name, Config, Version, sta mac, ap mac, rssi] + except Exception as e: + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, e) + elif (msg_subkey := "Directory-Get") and subtype == msg_subcodes[msg_key][ + msg_subkey]: # Get List of Files # BREAKS NETWORK + self.master.networking.iprint( + f"{msg_subkey} ({subtype}) command received from {sender_mac} ({self.master.networking.aen.peer_name(sender_mac)})") + try: + def ___list_all_files(path): + result = [] + entries = os.listdir(path) + for entry in entries: + full_path = path + "/" + entry + try: + if os.stat(full_path)[0] & 0x4000: + result.extend(___list_all_files(full_path)) + else: + result.append(full_path) + except OSError: + # Handle inaccessible files or directories + continue + return result + + start_path = '.' + all_files = ___list_all_files(start_path) + self.master.networking.aen.send_custom(0x02, 0x20, sender_mac, all_files) + except Exception as e: + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, e) + elif (msg_subkey := "Echo") and subtype == msg_subcodes[msg_key][msg_subkey]: # Echo + self.master.networking.iprint( + f"{msg_subkey} ({subtype}) command received from {sender_mac} ({self.master.networking.aen.peer_name(sender_mac)}): {payload}") # Check i or d + self.master.networking.aen.send_custom(0x03, 0x15, sender_mac, payload) + elif (msg_subkey := "Resend") and subtype == msg_subcodes[msg_key][msg_subkey]: # Resend lost long messages + self.master.networking.iprint( + f"{msg_subkey} ({subtype}) command received from {sender_mac} ({self.master.networking.aen.peer_name(sender_mac)})") + self.master.networking.iprint("Long_sent_buffer disabled due to memory constraints") + elif (msg_subkey := "WiFi-Connect") and subtype == msg_subcodes[msg_key][msg_subkey]: # Connect to Wi-Fi + self.master.networking.iprint( + f"{msg_subkey} ({subtype}) command received from {sender_mac} ({self.master.networking.aen.peer_name(sender_mac)})") + if __check_authorisation(sender_mac, payload): + try: + self.master.sta.connect(payload[0], payload[1]) + __send_confirmation("Success", sender_mac, f"{msg_subkey} ({subtype})", payload) + except Exception as e: + self.master.networking.eprint(f"Error: {e} with payload: {payload}") + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, e) + else: + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, "Not authorised") + elif (msg_subkey := "WiFi-Disconnect") and subtype == msg_subcodes[msg_key][ + msg_subkey]: # Disconnect from Wi-Fi + self.master.networking.iprint( + f"{msg_subkey} ({subtype}) command received from {sender_mac} ({self.master.networking.aen.peer_name(sender_mac)})") + if __check_authorisation(sender_mac, payload): + try: + self.master.sta.disconnect() + __send_confirmation("Success", sender_mac, f"{msg_subkey} ({subtype})", payload) + except Exception as e: + self.master.networking.eprint(f"Error: {e}") + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, e) + else: + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, "Not authorised") + elif (msg_subkey := "AP-Enable") and subtype == msg_subcodes[msg_key][msg_subkey]: # Enable AP + self.master.networking.iprint( + f"{msg_subkey} ({subtype}) command received from {sender_mac} ({self.master.networking.aen.peer_name(sender_mac)})") + if __check_authorisation(sender_mac, payload): + try: + ssid = payload[0] + if ssid == "": + ssid = self.master.config["name"] + password = payload[1] + self.master.ap.setap(ssid, password) + __send_confirmation("Success", sender_mac, f"{msg_subkey} ({subtype})", payload) + except Exception as e: + self.master.networking.eprint(f"Error: {e} with payload: {payload}") + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, e) + else: + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, "Not authorised") + elif (msg_subkey := "AP-Disable") and subtype == msg_subcodes[msg_key][msg_subkey]: # Disable AP + self.master.networking.iprint( + f"{msg_subkey} ({subtype}) command received from {sender_mac} ({self.master.networking.aen.peer_name(sender_mac)})") + if __check_authorisation(sender_mac, payload): + try: + self.master.ap.deactivate() + __send_confirmation("Success", sender_mac, f"{msg_subkey} ({subtype})", payload) + except Exception as e: + self.master.networking.iprint(f"Error: {e}") + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, e) + else: + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, "Not authorised") + elif (msg_subkey := "Pause") and subtype == msg_subcodes[msg_key][msg_subkey]: # Set Pause + self.master.networking.iprint( + f"{msg_subkey} ({subtype}) command received from {sender_mac} ({self.master.networking.aen.peer_name(sender_mac)})") + if __check_authorisation(sender_mac, payload): + try: + self.master.networking.iprint(f"Received pause command: {payload[0]}") + self._running = False + __send_confirmation("Success", sender_mac, f"{msg_subkey} ({subtype})", payload) + if self._pause_function: + self._pause_function() # calls the custom set pause function to display a screen + while not self._running: + time.sleep(0.5) + except Exception as e: + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, e) + else: + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, "Not authorised") + elif (msg_subkey := "Resume") and subtype == msg_subcodes[msg_key][msg_subkey]: # Set Continue + self.master.networking.iprint( + f"{msg_subkey} ({subtype}) command received from {sender_mac} ({self.master.networking.aen.peer_name(sender_mac)})") + if __check_authorisation(sender_mac, payload): + try: + self.master.networking.iprint(f"Received continue command: {payload}") + self.master._running = True + __send_confirmation("Success", sender_mac, f"{msg_subkey} ({subtype})", payload) + except Exception as e: + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, e) + else: + __send_confirmation("Fail", sender_mac, f"{msg_subkey} ({subtype})", payload, "Not authorised") + else: + self.master.networking.iprint( + f"Unknown command subtype from {sender_mac} ({self.master.networking.aen.peer_name(sender_mac)}): {subtype}") + + def custom_inf_handler(data): + self.master.networking.dprint("net.order.custom_inf_handler") + sender_mac, subtype, send_timestamp, receive_timestamp, payload, msg_key = data + if (msg_subkey := "Directory") and subtype == msg_subcodes[msg_key][msg_subkey]: # File Directory + self.master.networking.iprint(f"{msg_subkey} ({subtype}) data received from {sender_mac} ({self.master.networking.aen.peer_name(sender_mac)}): {payload}") + # __send_confirmation("Confirm", sender_mac, f"{msg_subkey} ({subtype})", payload) #confirm message recv + + def custom_ack_handler(data): + self.master.networking.dprint("net.order.custom_ack_handler") + sender_mac, subtype, send_timestamp, receive_timestamp, payload, msg_key = data + # data contains [sender_mac, subtype, send_timestamp, receive_timestamp, payload, msg_key] + + + self.master.networking.aen.cmd(custom_cmd_handler) + self.master.networking.aen.inf(custom_inf_handler) + self.master.networking.aen.ack(custom_ack_handler) + diff --git a/software/main/mm1.py b/software/main/mm1.py new file mode 100644 index 0000000..c58fd66 --- /dev/null +++ b/software/main/mm1.py @@ -0,0 +1,31 @@ +import uasyncio as asyncio +import time + +try: # ensure networking is available + networking +except NameError: + from config import networking as networking + +from config import hive_config + +print("running mm1.py") + +async def heartbeat_loop(): + while hive_config["hive"]: + print("heartbeat") + if hive_config["recipients"]: + test_data = { + "test": 42, + "time_sent": time.ticks_ms() + } + networking.send_data(hive_config["recipients"], test_data) + + recv_sensor_data = networking.return_data() + for mac, data in recv_sensor_data.items(): + print(f"Received from {mac}: {data}") + + await asyncio.sleep(hive_config["refreshrate"] / 1000) + + +if hive_config["hive"]: + asyncio.run(heartbeat_loop()) diff --git a/software/release/all/config.py b/software/release/all/config.py index 78753bb..3b89270 100644 --- a/software/release/all/config.py +++ b/software/release/all/config.py @@ -79,6 +79,6 @@ "0x53": ["ACCEL", 1, "accelerometer"] } #key is i2c address: ["device name", Output (0) or Input (1), "Description"] sensor_dict = {"sensor": [0,4095], "potentiometer": [0,180], "select": [0,1], "up": [0,1], "down": [0,1], "button": [0,1], "sw1": [0,1], "sw2": [0,1], "sw3": [0,1], "sw4": [0,1]} -hive_config = {'hive': False, 'recipients': [], 'sender_sensor_list': [], 'refreshrate': 200, 'mode': 'None'} +hive_config = {'hive': False, 'recipients': [], 'sender_sensor_list': [], 'refreshrate': 1000, 'mode': 'None'} networking = None