From d773b89bedb356d7b00a0635d3eef559e167bf8e Mon Sep 17 00:00:00 2001 From: Kai Junge <kai.junge@epfl.ch> Date: Fri, 15 Apr 2022 17:31:45 +0200 Subject: [PATCH] Testing new communication. First test on windows. --- comms_wrapper_new2.py | 276 ++++++++++++++++++++++++++++++++ test_new.py | 25 +++ test_new/pyCommsLib.cpp | 78 +++++++++ test_new/pyCommsLib.h | 17 ++ test_new/test_new.ino | 26 +++ test_original.py | 2 +- test_original/test_original.ino | 3 - test_windows.py | 2 +- 8 files changed, 424 insertions(+), 5 deletions(-) create mode 100644 comms_wrapper_new2.py create mode 100644 test_new.py create mode 100644 test_new/pyCommsLib.cpp create mode 100644 test_new/pyCommsLib.h create mode 100644 test_new/test_new.ino diff --git a/comms_wrapper_new2.py b/comms_wrapper_new2.py new file mode 100644 index 0000000..989794c --- /dev/null +++ b/comms_wrapper_new2.py @@ -0,0 +1,276 @@ +import serial +import serial.tools.list_ports +from threading import Thread +import time +from pynput import keyboard +import copy + +######## +### Arduino communication +######## + +class Arduino: + def __init__(self, descriptiveDeviceName, portName, baudrate): + # About the device + self.descriptiveDeviceName = descriptiveDeviceName + self.portName = portName + self.baudrate = baudrate + + # Communication + self._rawReceivedMessage = None + self.receivedMessages = {} + self.messageNames = None + self.arduino = None + self.handshakeStatus = False + self.connectionStatus = False + self._echo_python_msg = None + self.newMsgRecieved = False + + # Threading + self.__thread = None + + # Private methods + def _serial_readline(self): + while 1: + try: + self._rawReceivedMessage = self.arduino.readline().decode('utf-8')[:-2] + except: + pass + #time.sleep(0.00001) + + def _startReadingThread(self): + self.__thread = Thread(target=self._serial_readline) + self.__thread.daemon = True + self.__thread.start() + + + def _serial_write(self, msg): + if self.arduino is not None: + self.arduino.write(bytes(msg, 'utf-8')) + + + def _add_new_message_name(self, msgName): + if msgName in self.receivedMessages: + print("!! Message name ", msgName, " already exists so it cannot be added. !!") + else: + self.receivedMessages[msgName] = None + self.messageNames = list(self.receivedMessages.keys()) + + + def _connect_to_arduino(self): + # Connect to the arduino device + try: + self.arduino = serial.Serial(port=self.portName, baudrate=self.baudrate) + + # toggle dtr to reset the arduino + self.arduino.dtr = True + self.arduino.dtr = False + + self.connectionStatus = True + + print("Successfully connected to " + self.descriptiveDeviceName) + return True + except: + print("!! Cannot connect to " + self.descriptiveDeviceName + " !!") + return False + + + def _disect_and_save_message(self, msg): + receivedMessageTemp = copy.deepcopy(self.receivedMessages) + + if msg[-2] != ":": + return False + + msg = msg[1:-2] + splitMsg = msg.split(":") + for singleMsg in splitMsg: + if len(singleMsg.split(";")) == 2: + msgName = singleMsg.split(";")[0] + msgPayload = singleMsg.split(";")[1] + + if msgName == "e*p": + self._echo_python_msg = msgPayload + else: + if msgName not in self.messageNames: + return False + + receivedMessageTemp[msgName] = msgPayload + + else: + return False + + if receivedMessageTemp == self.receivedMessages: + self.newMsgRecieved = False + else: + self.newMsgRecieved = True + + self.receivedMessages = receivedMessageTemp + return True + + + # Public methods + def define_message_names(self, msgNames): + if type(msgNames) == list: + for msg in msgNames: + self._add_new_message_name(msg) + else: + print("Argument should be of type 'list'") + + + def connect_and_handshake(self): + # Connect to the arduino device + + if self._connect_to_arduino(): + pass + else: + return False + + # Start the reading thread + self._startReadingThread() + + # Wait for a bit for the arduino to initialise nicely + time.sleep(0.5) + + # Conduct the handshake process + timeoutTimer = time.time() + handshakeTimeoutSec = 5 + + self.arduino.reset_input_buffer() + self.arduino.reset_output_buffer() + + + while time.time() - timeoutTimer < handshakeTimeoutSec: + self._serial_write("handshake1\n") + time.sleep(0.1) + + if self._rawReceivedMessage == "handshake2": + self.handshakeStatus = True + break + + if self.handshakeStatus: + # while 1: + # self.receive_message() + # if self._echo_python_msg == "NO_PYTHON_MESSAGE": + # break + print("Successfull handshake with " + self.descriptiveDeviceName) + time.sleep(1) + else: + print("!! Handshake failed with " + self.descriptiveDeviceName + " !!") + + return self.handshakeStatus + + + def disconnect_arduino(self): + self.arduino.close() + + + def send_message(self, msg): + if type(msg) != str: + msg = str(msg) + self._serial_write( msg + "\n") + + + def receive_message(self, printOutput = False, verbose = False): + if not self.handshakeStatus: + print("!! Handshake not completed !!") + return False + else: + isMessageValid = True + msg = self._rawReceivedMessage + # sanity check 1: check if ends of the message are < and > + try: + if msg[0] == "<" and msg[-1] == ">": + pass + else: + isMessageValid = False + except: + isMessageValid = False + if isMessageValid: + isMessageValid = self._disect_and_save_message(msg) + else: + pass + + if printOutput: + if verbose: + print("----------------------") + print("Raw message received on python side: ", self._rawReceivedMessage) + print("Messege received from the arduino: ", self.receivedMessages) + print("Python message stored on", self.descriptiveDeviceName, ": ", self._echo_python_msg, "\n") + + else: + print("Messege received from the arduino: ", self.receivedMessages) + + time.sleep(0.0001) + return True + + + def current_status(self): + status = { + "Device name" : self.descriptiveDeviceName, + "Baudrate: ": self.baudrate, + "Portname: ": self.portName, + "Connection: ": self.connectionStatus, + "Handshake: ": self.handshakeStatus, + "Message names: ": self.messageNames + } + + return status + + + def debug(self, verbose = False): + if verbose: + self.receive_message(printOutput=True, verbose = True) + + print("Current status of this device:") + print(self.current_status()) + + else: + print("----------------------") + self.receive_message(printOutput=True) + print("Python message stored on", self.descriptiveDeviceName, ": ", self._echo_python_msg, "\n") + + + for key, value in self.receivedMessages.items(): + if value is None: + print("Check if message names: ", key, " agrees with the arduino side") + + +######## +### Key commands +######## + +class Key(): + def __init__(self): + self.keyPressLatching = None + self._keyReleaseLatching = None + self.keyPress = None + self._start_keyboard_listener() + + def _on_press(self, key): + try: + self.keyPressLatching = key.char + self.keyPress = key.char + + except AttributeError: + self.keyPressLatching = key + self.keyPress = key + + + def _on_release(self, key): + try: + self._keyReleaseLatching = key.char + + if self._keyReleaseLatching == self.keyPress: + self.keyPress = None + + except AttributeError: + self._keyReleaseLatching = key + + if self._keyReleaseLatching == self.keyPress: + self.keyPress = None + + + def _start_keyboard_listener(self): + listener = keyboard.Listener(on_press=self._on_press, on_release=self._on_release) + listener.start() + print("keyboard listener started") \ No newline at end of file diff --git a/test_new.py b/test_new.py new file mode 100644 index 0000000..bc4f9cc --- /dev/null +++ b/test_new.py @@ -0,0 +1,25 @@ +from comms_wrapper_new2 import * +from time import sleep, time + +def main(): + key = Key() + + ad = Arduino(descriptiveDeviceName="myArduino", portName="COM10", baudrate=115200) + ad.define_message_names(["msgA", "msgB"]) + ad.connect_and_handshake() + + timer = time() + while 1: + t = str(round(time() - timer, 5)) + while 1: + if len(t) < 5: + t += "0" + elif len(t) > 5: + t = t[:-1] + else: + break + ad.send_message(t) + print(ad._rawReceivedMessage) + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/test_new/pyCommsLib.cpp b/test_new/pyCommsLib.cpp new file mode 100644 index 0000000..cb2d4b3 --- /dev/null +++ b/test_new/pyCommsLib.cpp @@ -0,0 +1,78 @@ +#include "pyCommsLib.h" +#include "Arduino.h" + + +#define size_of_array(arr) sizeof(arr) / sizeof(*arr) + + +String rawMsgFromPython = "NO_PYTHON_MESSAGE"; +String payload = ""; + + +void load_msg_to_python(String* msgName, String* msg, int numOfMsg) { + // If we have the same number of data compared to the message + payload = ""; + + for (int i = 0; i < numOfMsg; i++) { + payload.concat(msgName[i]); + payload.concat(";"); + payload.concat(msg[i]); + payload.concat(":"); + } +} + + +void receive_msg_from_python() { + + String msg = ""; + + while (Serial.available()) { + if (Serial.available()) { + msg = Serial.readStringUntil('\n'); + } + } + + if (msg != "") { + rawMsgFromPython = msg; + } + +} + + +String latest_received_msg() { + return rawMsgFromPython; +} + +void init_python_communication() { + + while (true) { + // if the python side sent a message + if (Serial.available() > 0) { + String rawMsgFromPython = Serial.readStringUntil('\n'); + if (rawMsgFromPython == "handshake1") { + break; + } + } + } + + long timer = millis(); + while (millis() - timer < 1000) { + Serial.println("handshake2"); + } + + + while (Serial.available()) { + Serial.read(); + } +} + + +void sync() { + receive_msg_from_python(); + String final_payload = "<echo_python;"; + final_payload.concat(latest_received_msg()); + final_payload.concat(":"); + final_payload.concat(payload); + final_payload.concat(">"); + Serial.println(final_payload); +} diff --git a/test_new/pyCommsLib.h b/test_new/pyCommsLib.h new file mode 100644 index 0000000..75ee2f4 --- /dev/null +++ b/test_new/pyCommsLib.h @@ -0,0 +1,17 @@ +#ifndef PYCOMMSLIB_H +#define PYCOMMSLIB_H + +#define size_of_array(arr) sizeof(arr) / sizeof(*arr) + + +#include <Arduino.h> + +void load_msg_to_python(String* msgName, String* msg, int numOfMsg); + +String latest_received_msg(); + +void init_python_communication(); + +void sync(); + +#endif diff --git a/test_new/test_new.ino b/test_new/test_new.ino new file mode 100644 index 0000000..68b6761 --- /dev/null +++ b/test_new/test_new.ino @@ -0,0 +1,26 @@ +#include "pyCommsLib.h" + +String msgName[] = {"msgA", "msgB"}; + +String dataCarrier[2]; + +void setup() { + // Start the serial communication. The baudrate is arbiturary. + Serial.begin(115200); + + // Connect with the Python side + init_python_communication(); +} + +void loop() { + + // This is how you access the latest received message from the Python side + String received_message = latest_received_msg(); + + dataCarrier[0] = "Some very long message that we want to send"; + dataCarrier[1] = String(analogRead(A0)); + + load_msg_to_python(msgName, dataCarrier, size_of_array(msgName)); + sync(); + +} diff --git a/test_original.py b/test_original.py index 848ac76..f9c3849 100644 --- a/test_original.py +++ b/test_original.py @@ -5,7 +5,7 @@ def main(): key = Key() - ad = Arduino(descriptiveDeviceName="myArduino", portName="/dev/ttyACM0", baudrate=115200) + ad = Arduino(descriptiveDeviceName="myArduino", portName="COM", baudrate=115200) #ad.define_message_names(["msgA", "msgB"]) diff --git a/test_original/test_original.ino b/test_original/test_original.ino index b7964ac..8efbce8 100644 --- a/test_original/test_original.ino +++ b/test_original/test_original.ino @@ -27,9 +27,6 @@ void loop() { //load_msg_to_python(msgName, dataCarrier, size_of_array(msgName)); - - - sync(); diff --git a/test_windows.py b/test_windows.py index 54998e7..1435ba8 100644 --- a/test_windows.py +++ b/test_windows.py @@ -4,7 +4,7 @@ from time import sleep, time def main(): key = Key() - arduino1 = Arduino(descriptiveDeviceName="myArduino", portName="/dev/ttyACM0", baudrate=115200) + arduino1 = Arduino(descriptiveDeviceName="myArduino", portName="COM7", baudrate=115200) arduino1.define_number_of_messages(0, 1) arduino1.connect_and_handshake() -- GitLab