From c09ef802209d18aaa74ec39672dbb296248833e1 Mon Sep 17 00:00:00 2001 From: geeksville Date: Mon, 27 Apr 2020 17:49:16 -0700 Subject: [PATCH] protobuf exchange with the device kinda works. --- bin/run.sh | 2 +- meshtastic/__main__.py | 20 -------------- meshtastic/interface.py | 60 ++++++++++++++++++++++++++++++++++++++--- 3 files changed, 58 insertions(+), 24 deletions(-) diff --git a/bin/run.sh b/bin/run.sh index f35148d..65a5b4e 100755 --- a/bin/run.sh +++ b/bin/run.sh @@ -1 +1 @@ -python3 -m meshtastic +python3 -m meshtastic --debug diff --git a/meshtastic/__main__.py b/meshtastic/__main__.py index a013491..d2c1d89 100644 --- a/meshtastic/__main__.py +++ b/meshtastic/__main__.py @@ -9,26 +9,6 @@ def main(): """Perform command line meshtastic operations""" parser = argparse.ArgumentParser() - # Operations for manufacturing - parser.add_argument("--install", choices=['JR', 'JL', 'JM', 'JO', 'JG', 'JK', 'JY', 'JC', 'JT', 'MB', 'MS'], - help="Install the ezdevice code onto a new device (you must select the board type letter - see README.md)") - parser.add_argument("--readee", action="store_true", - help="Extract the eeprom contents from the device, so it can be programmed onto other boards") - - # Operations for app developers - parser.add_argument( - "--target", help="The device we are controlling given as : (get the secret key at https://www.ezdevice.net/my/devices)") - parser.add_argument( - "--displayfile", help="display a text,html,png,svg or jpeg file on the display") - # parser.add_argument("--displayURL", help="display a text,html,png,svg or jpeg from the web on the device") - parser.add_argument( - "--claim", help="Mark that this device is no longer being used for custom development", action="store_true") - parser.add_argument( - "--release", help="Mark that this device is no longer being used for custom development (i.e. return to joyframe demo app)", action="store_true") - - # Operations for server backend developers - parser.add_argument("--localserve", help="Talk to a development server", - action="store_true") parser.add_argument("--debug", help="Show debug log message", action="store_true") diff --git a/meshtastic/interface.py b/meshtastic/interface.py index 1e79172..89141fc 100644 --- a/meshtastic/interface.py +++ b/meshtastic/interface.py @@ -3,6 +3,13 @@ import serial import threading import logging import sys +from . import mesh_pb2 + +START1 = 0x94 +START2 = 0xc3 +HEADER_LEN = 4 +MAX_TO_FROM_RADIO_SIZE = 512 + """ @@ -46,12 +53,27 @@ class MeshInterface: def __init__(self): self.debugOut = sys.stdout self.nodes = None # FIXME + self.configId = 17 + self._startConfig() - def __handleReceived(fromradio): + def _startConfig(self): + """Start device packets flowing""" + startConfig = mesh_pb2.ToRadio() + startConfig.want_config_id = self.configId + self._sendToRadio(startConfig) + + def _sendToRadio(self, toRadio): + """Send a ToRadio protobuf to the device""" + logging.error(f"Subclass must provide toradio: {toRadio}") + + def _handleFromRadio(self, fromRadioBytes): """ Handle a packet that arrived from the radio (update model and publish events) Called by subclasses.""" + fromRadio = mesh_pb2.FromRadio() + fromRadio.ParseFromString(fromRadioBytes) + logging.debug(f"Received: {fromRadio}") class StreamInterface(MeshInterface): @@ -59,18 +81,50 @@ class StreamInterface(MeshInterface): def __init__(self, devPath): """Constructor, opens a connection to a specified serial port""" - MeshInterface.__init__(self) logging.debug(f"Connecting to {devPath}") self.rxBuf = bytes() # empty self.stream = serial.Serial(devPath, 921600) self.rxThread = threading.Thread(target=self.__reader, args=()) self.rxThread.start() + MeshInterface.__init__(self) + + def _sendToRadio(self, toRadio): + """Send a ToRadio protobuf to the device""" + logging.debug(f"Sending: {toRadio}") + b = toRadio.SerializeToString() + bufLen = len(b) + header = bytes([START1, START2, (bufLen >> 8) & 0xff, bufLen & 0xff]) + self.stream.write(header) + self.stream.write(b) def close(self): self.stream.close() # This will cause our reader thread to exit def __reader(self): """The reader thread that reads bytes from our stream""" + empty = bytes() + while True: b = self.stream.read(1) - self.debugOut.write(b.decode("utf-8")) + c = b[0] + ptr = len(self.rxBuf) + + self.rxBuf = self.rxBuf + b # Assume we want to append this byte + + if ptr == 0: # looking for START1 + if c != START1: + self.rxBuf = empty # failed to find start + self.debugOut.write(b.decode("utf-8")) + elif ptr == 1: # looking for START2 + if c != START2: + self.rfBuf = empty # failed to find start2 + elif ptr >= HEADER_LEN: # we've at least got a header + # big endian length follos header + packetlen = (self.rxBuf[2] << 8) + self.rxBuf[3] + + if ptr == HEADER_LEN: # we _just_ finished reading the header, validate length + if packetlen > MAX_TO_FROM_RADIO_SIZE: + self.rfBuf = empty # length ws out out bounds, restart + + if len(self.rxBuf) != 0 and ptr == packetlen + HEADER_LEN: + self._handleFromRadio(self.rxBuf[HEADER_LEN:])