From 8f3fdaa66ff5732b60cba1ff341b189c2085f161 Mon Sep 17 00:00:00 2001 From: abhinavp5 Date: Thu, 6 Nov 2025 18:44:14 -0500 Subject: [PATCH 01/27] Test script for recieving arudino i2c data serially with pi --- Testing_Library/test_motor_interface.py | 51 +++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 Testing_Library/test_motor_interface.py diff --git a/Testing_Library/test_motor_interface.py b/Testing_Library/test_motor_interface.py new file mode 100644 index 0000000..3f67661 --- /dev/null +++ b/Testing_Library/test_motor_interface.py @@ -0,0 +1,51 @@ +import sys +import serial +sys.path.append('/home/solarcar/solarcar/HiL_Testing/Server') + +THROTTLE_ADDR = 0x2F +REGEN_ADDR = 0x2E + + +''' +Both the throttle and regen are 9 it bit values, send them both at once in +a 3 byte value and then just read based on there +''' +# TODO: Serial Configuration +ser = serial.Serial( + port = '/dev/ttyAMA0', + baudrate =115200, + timeout = 1 +) + + +while True: + if ser.read(1) == b'\xFF': + data = ser.read(2) + if len(data) == 2: + throttle_data = data[0] + regen_data = data[1] + print(f"Regen: {regen_data} Throttle: {throttle_data}") + + +''' +API for test Moto interface +''' +class Test_Motor_Interface: + def __init__(self): + self.mru_throttle = None + self.mru_regen = None + + def get_throttle() -> float: + # TODO Return throttle value as a double between [0-1] + + pass + def get_throttle_raw() -> int: + # TODO Return throttle value between [0-255] + pass + def get_regen() -> float: + # TODO Return throttle value between [0-1] + pass + def get_regen_raw() -> int: + # TODO Return throttle value between [0-255] + pass + From ad10ce532411ba1be8621ddad663d63965b0400f Mon Sep 17 00:00:00 2001 From: abhinavp5 Date: Thu, 6 Nov 2025 19:28:42 -0500 Subject: [PATCH 02/27] debuggin output to motor_interface.py --- Testing_Library/test_motor_interface.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Testing_Library/test_motor_interface.py b/Testing_Library/test_motor_interface.py index 3f67661..dd0f529 100644 --- a/Testing_Library/test_motor_interface.py +++ b/Testing_Library/test_motor_interface.py @@ -6,6 +6,8 @@ REGEN_ADDR = 0x2E + + ''' Both the throttle and regen are 9 it bit values, send them both at once in a 3 byte value and then just read based on there @@ -19,13 +21,16 @@ while True: - if ser.read(1) == b'\xFF': + byte_read = ser.read(1) + if byte_read == b'\xFF': data = ser.read(2) if len(data) == 2: throttle_data = data[0] regen_data = data[1] print(f"Regen: {regen_data} Throttle: {throttle_data}") - + else: + print("looping") + print(f"byte_read{byte_read}") ''' API for test Moto interface @@ -49,3 +54,4 @@ def get_regen_raw() -> int: # TODO Return throttle value between [0-255] pass + From cead4bc2f84a41d2a81fff9fe24ec9c145e357c4 Mon Sep 17 00:00:00 2001 From: abhinavp5 Date: Thu, 6 Nov 2025 19:43:58 -0500 Subject: [PATCH 03/27] 3 bytes read for motorinterface.py --- Testing_Library/test_motor_interface.py | 45 ++++++++++++++----------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/Testing_Library/test_motor_interface.py b/Testing_Library/test_motor_interface.py index dd0f529..230967c 100644 --- a/Testing_Library/test_motor_interface.py +++ b/Testing_Library/test_motor_interface.py @@ -1,36 +1,41 @@ import sys import serial +import time + sys.path.append('/home/solarcar/solarcar/HiL_Testing/Server') THROTTLE_ADDR = 0x2F -REGEN_ADDR = 0x2E - - +REGEN_ADDR = 0x2E - -''' -Both the throttle and regen are 9 it bit values, send them both at once in -a 3 byte value and then just read based on there -''' -# TODO: Serial Configuration +# Serial Configuration ser = serial.Serial( - port = '/dev/ttyAMA0', - baudrate =115200, - timeout = 1 + port='/dev/ttyAMA0', + baudrate=115200, + timeout=1 ) +print("Waiting for Arduino to start...") +time.sleep(2) # Give Arduino time to boot while True: byte_read = ser.read(1) if byte_read == b'\xFF': - data = ser.read(2) - if len(data) == 2: - throttle_data = data[0] - regen_data = data[1] - print(f"Regen: {regen_data} Throttle: {throttle_data}") - else: - print("looping") - print(f"byte_read{byte_read}") + data = ser.read(3) # Read 3 bytes for two 9-bit values + if len(data) == 3: + # Unpack two 9-bit values from 3 bytes + # Byte 0: throttle[7:0] + # Byte 1: throttle[8] in bit 0, regen[6:0] in bits 7:1 + # Byte 2: regen[8:7] in bits 1:0 + + # Extracting throttle and regen + throttle = data[0] | ((data[1] & 0x01) << 8) + regen = ((data[1] >> 1) & 0x7F) | ((data[2] & 0x03) << 7) + + print(f"Throttle: {throttle}%) | Regen: {regen}}%)") + else: + print(f"Incomplete data: got {len(data)} bytes instead of 3") + elif byte_read: + print(f"Unexpected byte: {byte_read}") ''' API for test Moto interface From c35156df3ac50bc0d4cb6d4f42f25141c4c14ed7 Mon Sep 17 00:00:00 2001 From: Colby Wise Date: Mon, 20 Oct 2025 01:00:44 -0400 Subject: [PATCH 04/27] Working serial communication with pi and arduino --- Testing_Library/test_motor_interface.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Testing_Library/test_motor_interface.py b/Testing_Library/test_motor_interface.py index 230967c..e046d2f 100644 --- a/Testing_Library/test_motor_interface.py +++ b/Testing_Library/test_motor_interface.py @@ -9,8 +9,8 @@ # Serial Configuration ser = serial.Serial( - port='/dev/ttyAMA0', - baudrate=115200, + port='/dev/ttyACM0', + baudrate=9600, timeout=1 ) @@ -31,11 +31,11 @@ throttle = data[0] | ((data[1] & 0x01) << 8) regen = ((data[1] >> 1) & 0x7F) | ((data[2] & 0x03) << 7) - print(f"Throttle: {throttle}%) | Regen: {regen}}%)") + print(f"Throttle: {throttle}%) | Regen: {regen})") else: print(f"Incomplete data: got {len(data)} bytes instead of 3") elif byte_read: - print(f"Unexpected byte: {byte_read}") + print(f"Unexpected byte:{byte_read}") ''' API for test Moto interface From 8fcfa438bbc58fdbcf3c5ba884d23037406b55cf Mon Sep 17 00:00:00 2001 From: abhinavp5 Date: Sun, 9 Nov 2025 14:32:15 -0500 Subject: [PATCH 05/27] implementation of MotorInterfaceClass --- Testing_Library/MotorInterface.py | 122 ++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 Testing_Library/MotorInterface.py diff --git a/Testing_Library/MotorInterface.py b/Testing_Library/MotorInterface.py new file mode 100644 index 0000000..00dde40 --- /dev/null +++ b/Testing_Library/MotorInterface.py @@ -0,0 +1,122 @@ +import sys +import serial +import time +import threading + + +sys.path.append('/home/solarcar/solarcar/HiL_Testing/Server') + +THROTTLE_ADDR = 0x2F +REGEN_ADDR = 0x2E + +# Serial Configuration +ser = serial.Serial( + port='/dev/ttyACM0', + baudrate=9600, + timeout=1 +) + +print("Waiting for Arduino to start...") +time.sleep(2) # Give Arduino time to boot +''' +while True: + byte_read = ser.read(1) + if byte_read == b'\xFF': + data = ser.read(3) # Read 3 bytes for two 9-bit values + if len(data) == 3: + # Unpack two 9-bit values from 3 bytes + # Byte 0: throttle[7:0] + # Byte 1: throttle[8] in bit 0, regen[6:0] in bits 7:1 + # Byte 2: regen[8:7] in bits 1:0 + + # Extracting throttle and regen + throttle = data[0] | ((data[1] & 0x01) << 8) + regen = ((data[1] >> 1) & 0x7F) | ((data[2] & 0x03) << 7) + + print(f"Throttle: {throttle}%) | Regen: {regen})") + else: + print(f"Incomplete data: got {len(data)} bytes instead of 3") + elif byte_read: + print(f"Unexpected byte:{byte_read}") +''' +ser = serial.Serial( + port='/dev/ttyACM0', + baudrate=9600, + timeout=1 + ) +''' +API for test Motor interface +''' +class MotorInterface: + def __init__(self): + self.stop_thread = False + self.mru_throttle = None + self.mru_regen = None + self.THROTTLE_ADDR = 0x2F + self.REGEN_ADDR = 0x2E + self.startReadThread() + + + #Run infinite thread to read in Throttle and Regen Values + def startReadThread(self): + read_thread = threading.Thread(target=self.readThrottleAndRegen) + read_thread.daemon = True + try: + read_thread.start() + except ValueError as e: + print(f"Exception") + def readThrottleAndRegen(self): + while(not self.stop_thread): + with self.lock: + byte_read = ser.read(1) + if byte_read == b'\xFF': # Start Byte + data = ser.read(3) # Read 3 bytes for two 9-bit values + if len(data) == 3: + # two 9-bit values from 3 bytes + # Byte 0: throttle[7:0] + # Byte 1: throttle[8] in bit 0, regen[6:0] in bits 7:1 + # Byte 2: regen[8:7] in bits 1:0 + + # Extracting throttle and regen + self.mru_throttle = data[0] | ((data[1] & 0x01) << 8) + self.mru_regen = ((data[1] >> 1) & 0x7F) | ((data[2] & 0x03) << 7) + + def get_throttle(self) -> float: + # Normalized between [0-1.0] + if self.mru_throttle is None: + return None + return self.mru_throttle/256.0 + + def get_throttle_raw(self) -> int: + # raw between [0-256] + if self.mru_throttle is None: + return None + return self.mru_throttle + + def get_regen(self) -> float: + # Normalized between [0,1.0] + if self.mru_regen is None: + return None + return self.mru_regen/256.0 + + def get_regen_raw(self) -> int: + # raw between [0-256] + if self.mru_regen is None: + return None + return self.mru_regen + + +motor_interface = MotorInterface() + +while(True): + throttle_raw = motor_interface.get_throttle_raw() + throttle_norm = motor_interface.get_throttle() + regen_raw = motor_interface.get_regen_raw() + regen_norm = motor_interface.get_regen() + + + + + + + From efcdb30735ee4fdb55df9be4d940bfef7c297ad8 Mon Sep 17 00:00:00 2001 From: abhinavp5 Date: Sun, 9 Nov 2025 14:50:26 -0500 Subject: [PATCH 06/27] fixes to self.lock --- Testing_Library/MotorInterface.py | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/Testing_Library/MotorInterface.py b/Testing_Library/MotorInterface.py index 00dde40..1b5c4d1 100644 --- a/Testing_Library/MotorInterface.py +++ b/Testing_Library/MotorInterface.py @@ -54,6 +54,7 @@ def __init__(self): self.mru_regen = None self.THROTTLE_ADDR = 0x2F self.REGEN_ADDR = 0x2E + self.Lock = threading.lock() self.startReadThread() @@ -67,20 +68,20 @@ def startReadThread(self): print(f"Exception") def readThrottleAndRegen(self): while(not self.stop_thread): - with self.lock: - byte_read = ser.read(1) - if byte_read == b'\xFF': # Start Byte - data = ser.read(3) # Read 3 bytes for two 9-bit values - if len(data) == 3: - # two 9-bit values from 3 bytes - # Byte 0: throttle[7:0] - # Byte 1: throttle[8] in bit 0, regen[6:0] in bits 7:1 - # Byte 2: regen[8:7] in bits 1:0 - - # Extracting throttle and regen - self.mru_throttle = data[0] | ((data[1] & 0x01) << 8) - self.mru_regen = ((data[1] >> 1) & 0x7F) | ((data[2] & 0x03) << 7) - + with self.Lock: + byte_read = ser.read(1) + if byte_read == b'\xFF': # Start Byte + data = ser.read(3) # Read 3 bytes for two 9-bit values + if len(data) == 3: + # two 9-bit values from 3 bytes + # Byte 0: throttle[7:0] + # Byte 1: throttle[8] in bit 0, regen[6:0] in bits 7:1 + # Byte 2: regen[8:7] in bits 1:0 + + # Extracting throttle and regen + self.mru_throttle = data[0] | ((data[1] & 0x01) << 8) + self.mru_regen = ((data[1] >> 1) & 0x7F) | ((data[2] & 0x03) << 7) + def get_throttle(self) -> float: # Normalized between [0-1.0] if self.mru_throttle is None: From 1eb35c8aa05daef16f04e89150055fed4ab4eecf Mon Sep 17 00:00:00 2001 From: Colby Wise Date: Mon, 20 Oct 2025 01:29:44 -0400 Subject: [PATCH 07/27] confirmed working implemntatin of MotorInterface APi --- Testing_Library/MotorInterface.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Testing_Library/MotorInterface.py b/Testing_Library/MotorInterface.py index 1b5c4d1..425f554 100644 --- a/Testing_Library/MotorInterface.py +++ b/Testing_Library/MotorInterface.py @@ -54,7 +54,7 @@ def __init__(self): self.mru_regen = None self.THROTTLE_ADDR = 0x2F self.REGEN_ADDR = 0x2E - self.Lock = threading.lock() + self.Lock = threading.Lock() self.startReadThread() @@ -110,14 +110,15 @@ def get_regen_raw(self) -> int: motor_interface = MotorInterface() while(True): + throttle_raw = motor_interface.get_throttle_raw() throttle_norm = motor_interface.get_throttle() regen_raw = motor_interface.get_regen_raw() regen_norm = motor_interface.get_regen() - - - - - - + print("Details") + print(throttle_raw) + print(throttle_norm) + print(regen_raw) + print(regen_norm) + time.sleep(1) From 1dee8c58f0bf162d730ea2b66e190af5f81b1a40 Mon Sep 17 00:00:00 2001 From: abhinavp5 Date: Thu, 13 Nov 2025 18:39:09 -0500 Subject: [PATCH 08/27] updates to motor interface API --- Testing_Library/MotorInterfaceTest.py | 130 ++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 Testing_Library/MotorInterfaceTest.py diff --git a/Testing_Library/MotorInterfaceTest.py b/Testing_Library/MotorInterfaceTest.py new file mode 100644 index 0000000..ce14f93 --- /dev/null +++ b/Testing_Library/MotorInterfaceTest.py @@ -0,0 +1,130 @@ +import sys +import serial +import time +import threading + + +sys.path.append('/home/solarcar/solarcar/HiL_Testing/Server') + +THROTTLE_ADDR = 0x2F +REGEN_ADDR = 0x2E +THROTTLE_START_BYTE = b'\xFF' +REGEN_START_BYTE = b'\xFE' + +# Serial Configuration +ser = serial.Serial( + port='/dev/ttyACM0', + baudrate=9600, + timeout=1 +) + +print("Waiting for Arduino to start...") +time.sleep(2) # Give Arduino time to boot +''' +while True: + byte_read = ser.read(1) + if byte_read == b'\xFF': + data = ser.read(3) # Read 3 bytes for two 9-bit values + if len(data) == 3: + # Unpack two 9-bit values from 3 bytes + # Byte 0: throttle[7:0] + # Byte 1: throttle[8] in bit 0, regen[6:0] in bits 7:1 + # Byte 2: regen[8:7] in bits 1:0 + + # Extracting throttle and regen + throttle = data[0] | ((data[1] & 0x01) << 8) + regen = ((data[1] >> 1) & 0x7F) | ((data[2] & 0x03) << 7) + + print(f"Throttle: {throttle}%) | Regen: {regen})") + else: + print(f"Incomplete data: got {len(data)} bytes instead of 3") + elif byte_read: + print(f"Unexpected byte:{byte_read}") +''' +ser = serial.Serial( + port='/dev/ttyACM0', + baudrate=9600, + timeout=1 + ) +''' +API for test Motor interface +''' +class MotorInterfaceTest: + def __init__(self): + self.stop_thread = False + self.mru_throttle = None + self.mru_regen = None + self.THROTTLE_ADDR = 0x2F + self.REGEN_ADDR = 0x2E + self.Lock = threading.Lock() + self.startReadThread() + + + #Run infinite thread to read in Throttle and Regen Values + def startReadThread(self): + read_thread = threading.Thread(target=self.readThrottleAndRegen) + read_thread.daemon = True + try: + read_thread.start() + except ValueError as e: + print(f"Exception") + def readThrottleAndRegen(self): + while(not self.stop_thread): + with self.Lock: + byte_read = ser.read(1) + if byte_read == THROTTLE_START_BYTE: # Throttle start marker + data = ser.read(2) # Read 2 bytes for 9-bit value + if len(data) == 2: + # Extract 9-bit value from 2 bytes + # Byte 0: value[7:0] + # Byte 1: value[8] in bit 0 + # Range: 0-256 (9 bits), but normalized to 0-256 + self.mru_throttle = data[0] | ((data[1] & 0x01) << 8) + elif byte_read == REGEN_START_BYTE: + data = ser.read(2) # Read 2 bytes for 9-bit value + if len(data) == 2: + # Extract 9-bit value from 2 bytes + # Byte 0: value[7:0] + # Byte 1: value[8] in bit 0 + # Range: 0-256 (9 bits), but normalized to 0-256 + self.mru_regen = data[0] | ((data[1] & 0x01) << 8) + + def get_throttle(self) -> float: + # Normalized between [0-1.0] + if self.mru_throttle is None: + return None + return self.mru_throttle/256.0 + + def get_throttle_raw(self) -> int: + # raw between [0-256] + if self.mru_throttle is None: + return None + return self.mru_throttle + + def get_regen(self) -> float: + # Normalized between [0,1.0] + if self.mru_regen is None: + return None + return self.mru_regen/256.0 + + def get_regen_raw(self) -> int: + # raw between [0-256] + if self.mru_regen is None: + return None + return self.mru_regen + + +motor_interface = MotorInterfaceTest() + +while(True): + throttle_raw = motor_interface.get_throttle_raw() + throttle_norm = motor_interface.get_throttle() + regen_raw = motor_interface.get_regen_raw() + regen_norm = motor_interface.get_regen() + + + + + + + From c33d43834c9d70c197ee21616e297b1017be8ad4 Mon Sep 17 00:00:00 2001 From: abhinavp5 Date: Thu, 13 Nov 2025 19:25:02 -0500 Subject: [PATCH 09/27] remove unused instance of MotorInterfaceTest and related test loop --- Testing_Library/MotorInterfaceTest.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/Testing_Library/MotorInterfaceTest.py b/Testing_Library/MotorInterfaceTest.py index ce14f93..40313fc 100644 --- a/Testing_Library/MotorInterfaceTest.py +++ b/Testing_Library/MotorInterfaceTest.py @@ -114,17 +114,6 @@ def get_regen_raw(self) -> int: return self.mru_regen -motor_interface = MotorInterfaceTest() - -while(True): - throttle_raw = motor_interface.get_throttle_raw() - throttle_norm = motor_interface.get_throttle() - regen_raw = motor_interface.get_regen_raw() - regen_norm = motor_interface.get_regen() - - - - From de7f1affae0a44ac81e43cf4dcf9f0d1b9a4fc8d Mon Sep 17 00:00:00 2001 From: abhinavp5 Date: Thu, 13 Nov 2025 20:25:33 -0500 Subject: [PATCH 10/27] Adding serial port as member of class --- Testing_Library/MotorInterfaceTest.py | 35 ++++++++++----------------- 1 file changed, 13 insertions(+), 22 deletions(-) diff --git a/Testing_Library/MotorInterfaceTest.py b/Testing_Library/MotorInterfaceTest.py index 40313fc..3059e09 100644 --- a/Testing_Library/MotorInterfaceTest.py +++ b/Testing_Library/MotorInterfaceTest.py @@ -10,16 +10,6 @@ REGEN_ADDR = 0x2E THROTTLE_START_BYTE = b'\xFF' REGEN_START_BYTE = b'\xFE' - -# Serial Configuration -ser = serial.Serial( - port='/dev/ttyACM0', - baudrate=9600, - timeout=1 -) - -print("Waiting for Arduino to start...") -time.sleep(2) # Give Arduino time to boot ''' while True: byte_read = ser.read(1) @@ -41,22 +31,23 @@ elif byte_read: print(f"Unexpected byte:{byte_read}") ''' -ser = serial.Serial( - port='/dev/ttyACM0', - baudrate=9600, - timeout=1 - ) -''' -API for test Motor interface -''' + class MotorInterfaceTest: - def __init__(self): + """API for test Motor interface""" + def __init__(self, port='/dev/ttyACM0', baudrate=9600): self.stop_thread = False self.mru_throttle = None self.mru_regen = None self.THROTTLE_ADDR = 0x2F self.REGEN_ADDR = 0x2E self.Lock = threading.Lock() + self.ser = serial.Serial( + port=port, + baudrate=baudrate, + timeout=1 + ) + print("Waiting for Arduino to start...") + time.sleep(2) # Give Arduino time to boot self.startReadThread() @@ -71,9 +62,9 @@ def startReadThread(self): def readThrottleAndRegen(self): while(not self.stop_thread): with self.Lock: - byte_read = ser.read(1) + byte_read = self.ser.read(1) if byte_read == THROTTLE_START_BYTE: # Throttle start marker - data = ser.read(2) # Read 2 bytes for 9-bit value + data = self.ser.read(2) # Read 2 bytes for 9-bit value if len(data) == 2: # Extract 9-bit value from 2 bytes # Byte 0: value[7:0] @@ -81,7 +72,7 @@ def readThrottleAndRegen(self): # Range: 0-256 (9 bits), but normalized to 0-256 self.mru_throttle = data[0] | ((data[1] & 0x01) << 8) elif byte_read == REGEN_START_BYTE: - data = ser.read(2) # Read 2 bytes for 9-bit value + data = self.ser.read(2) # Read 2 bytes for 9-bit value if len(data) == 2: # Extract 9-bit value from 2 bytes # Byte 0: value[7:0] From 2cbfa673d75213345448658cedcdc3c9bb9ea8fd Mon Sep 17 00:00:00 2001 From: abhinavp5 Date: Thu, 22 Jan 2026 17:40:21 -0500 Subject: [PATCH 11/27] fix: debug output --- Testing_Library/MotorInterfaceTest.py | 7 +-- Testing_Library/test_motor_interface.py | 62 ------------------------- 2 files changed, 2 insertions(+), 67 deletions(-) delete mode 100644 Testing_Library/test_motor_interface.py diff --git a/Testing_Library/MotorInterfaceTest.py b/Testing_Library/MotorInterfaceTest.py index 3059e09..45d36d4 100644 --- a/Testing_Library/MotorInterfaceTest.py +++ b/Testing_Library/MotorInterfaceTest.py @@ -71,6 +71,7 @@ def readThrottleAndRegen(self): # Byte 1: value[8] in bit 0 # Range: 0-256 (9 bits), but normalized to 0-256 self.mru_throttle = data[0] | ((data[1] & 0x01) << 8) + print(f"Throttle: {self.mru_throttle}") elif byte_read == REGEN_START_BYTE: data = self.ser.read(2) # Read 2 bytes for 9-bit value if len(data) == 2: @@ -79,6 +80,7 @@ def readThrottleAndRegen(self): # Byte 1: value[8] in bit 0 # Range: 0-256 (9 bits), but normalized to 0-256 self.mru_regen = data[0] | ((data[1] & 0x01) << 8) + print(f"Regen: {self.mru_regen}") def get_throttle(self) -> float: # Normalized between [0-1.0] @@ -103,8 +105,3 @@ def get_regen_raw(self) -> int: if self.mru_regen is None: return None return self.mru_regen - - - - - diff --git a/Testing_Library/test_motor_interface.py b/Testing_Library/test_motor_interface.py deleted file mode 100644 index e046d2f..0000000 --- a/Testing_Library/test_motor_interface.py +++ /dev/null @@ -1,62 +0,0 @@ -import sys -import serial -import time - -sys.path.append('/home/solarcar/solarcar/HiL_Testing/Server') - -THROTTLE_ADDR = 0x2F -REGEN_ADDR = 0x2E - -# Serial Configuration -ser = serial.Serial( - port='/dev/ttyACM0', - baudrate=9600, - timeout=1 -) - -print("Waiting for Arduino to start...") -time.sleep(2) # Give Arduino time to boot - -while True: - byte_read = ser.read(1) - if byte_read == b'\xFF': - data = ser.read(3) # Read 3 bytes for two 9-bit values - if len(data) == 3: - # Unpack two 9-bit values from 3 bytes - # Byte 0: throttle[7:0] - # Byte 1: throttle[8] in bit 0, regen[6:0] in bits 7:1 - # Byte 2: regen[8:7] in bits 1:0 - - # Extracting throttle and regen - throttle = data[0] | ((data[1] & 0x01) << 8) - regen = ((data[1] >> 1) & 0x7F) | ((data[2] & 0x03) << 7) - - print(f"Throttle: {throttle}%) | Regen: {regen})") - else: - print(f"Incomplete data: got {len(data)} bytes instead of 3") - elif byte_read: - print(f"Unexpected byte:{byte_read}") - -''' -API for test Moto interface -''' -class Test_Motor_Interface: - def __init__(self): - self.mru_throttle = None - self.mru_regen = None - - def get_throttle() -> float: - # TODO Return throttle value as a double between [0-1] - - pass - def get_throttle_raw() -> int: - # TODO Return throttle value between [0-255] - pass - def get_regen() -> float: - # TODO Return throttle value between [0-1] - pass - def get_regen_raw() -> int: - # TODO Return throttle value between [0-255] - pass - - From 70d59c93ebe24ee77f68dd68178f367a3602992a Mon Sep 17 00:00:00 2001 From: abhinavp5 Date: Sun, 1 Feb 2026 14:14:21 -0500 Subject: [PATCH 12/27] test: added the arduino testscript --- Testing_Library/MotorInterfaceTest.py | 18 ++--- .../motor_interface_test_script1.29.ino | 74 +++++++++++++++++++ 2 files changed, 82 insertions(+), 10 deletions(-) create mode 100644 Testing_Library/motor_interface_test_script1.29.ino diff --git a/Testing_Library/MotorInterfaceTest.py b/Testing_Library/MotorInterfaceTest.py index 45d36d4..e09b2dc 100644 --- a/Testing_Library/MotorInterfaceTest.py +++ b/Testing_Library/MotorInterfaceTest.py @@ -66,20 +66,18 @@ def readThrottleAndRegen(self): if byte_read == THROTTLE_START_BYTE: # Throttle start marker data = self.ser.read(2) # Read 2 bytes for 9-bit value if len(data) == 2: - # Extract 9-bit value from 2 bytes - # Byte 0: value[7:0] - # Byte 1: value[8] in bit 0 - # Range: 0-256 (9 bits), but normalized to 0-256 - self.mru_throttle = data[0] | ((data[1] & 0x01) << 8) + # motorinterface.cpp sends (0x100 - throttle) + # first byte contains lower 8 bits, second byte LSB is the 9th bit + raw_from_arduino = data[0] | ((data[1] & 0x01) << 8) + self.mru_throttle = 256 - raw_from_arduino # PowerBoard sends (0x100 - throttle) over I2C print(f"Throttle: {self.mru_throttle}") elif byte_read == REGEN_START_BYTE: data = self.ser.read(2) # Read 2 bytes for 9-bit value if len(data) == 2: - # Extract 9-bit value from 2 bytes - # Byte 0: value[7:0] - # Byte 1: value[8] in bit 0 - # Range: 0-256 (9 bits), but normalized to 0-256 - self.mru_regen = data[0] | ((data[1] & 0x01) << 8) + # motorinterface.cpp sends (0x100 - regen) + # first byte contains lower 8 bits, second byte LSB is the 9th bit + raw_from_arduino = data[0] | ((data[1] & 0x01) << 8) + self.mru_regen = 256 - raw_from_arduino # PowerBoard sends (0x100 - regen) over I2C print(f"Regen: {self.mru_regen}") def get_throttle(self) -> float: diff --git a/Testing_Library/motor_interface_test_script1.29.ino b/Testing_Library/motor_interface_test_script1.29.ino new file mode 100644 index 0000000..729edaf --- /dev/null +++ b/Testing_Library/motor_interface_test_script1.29.ino @@ -0,0 +1,74 @@ +#include + +#define THROTTLE_ADDR 0x2F +#define REGEN_ADDR 0x2E +#define THROTTLE_MARKER 0xFF +#define REGEN_MARKER 0xFE +#define SWAP_COMMAND 0x1FF + +// Test Mode: 0 = Throttle only (listen on 0x2F), 1 = Regen only (listen on 0x2E). +// Must match which test is run: test_throttle needs TEST_MODE 0, test_regen needs TEST_MODE 1. +#define TEST_MODE 0 + +int currentMode = 0; // 0 = Throttle, 1 = Regen + +void setup() { + Serial.begin(9600); + while (!Serial); + Serial.println("Arduino Starting..."); + + // Initialize based on test mode + if (TEST_MODE == 1) { + // Start in regen mode + Wire.begin(REGEN_ADDR); + currentMode = 1; + Serial.println("Initialized on REGEN_ADDR (0x2E)"); + } else { + // Start in throttle mode (default) + Wire.begin(THROTTLE_ADDR); + currentMode = 0; + Serial.println("Initialized on THROTTLE_ADDR (0x2F)"); + } + + Wire.onReceive(receiveEvent); + Serial.println("Ready to receive I2C data"); + Serial.print("TEST_MODE: "); + Serial.println(TEST_MODE); +} + +void loop() { + delay(100); +} + +void receiveEvent(int howMany) { + if (Wire.available() < 2) return; + + // Read 2 bytes (16-bit value) + int highByte = Wire.read(); + int lowByte = Wire.read(); + int value = ((highByte << 8) | lowByte) & 0x1FF; + + // Handle swap command + if (value == SWAP_COMMAND) { + //TODO: + return; + } + // Clamp value to valid range [0, 256] + if (value > 256) value = 256; + + + // Ensure value stays in valid range after inversion + if (value > 256) value = 256; + if (value < 0) value = 0; + + // Send based on current mode + if (currentMode == 0) { + Serial.write(THROTTLE_MARKER); + Serial.write(value & 0xFF); // Low byte + Serial.write((value >> 8) & 0x01); // Bit 8 + } else { + Serial.write(REGEN_MARKER); + Serial.write(value & 0xFF); // Low byte + Serial.write((value >> 8) & 0x01); // Bit 8 + } +} From cd0ed8be7704c22105e8ea1785ee389f9a48e06d Mon Sep 17 00:00:00 2001 From: abhinavp5 Date: Sun, 1 Feb 2026 14:24:40 -0500 Subject: [PATCH 13/27] added arudino script --- .../motor_interface_test_script1.29.ino | 90 +++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 Testing_Library/CAN-messages/motor_interface_test_script1.29.ino diff --git a/Testing_Library/CAN-messages/motor_interface_test_script1.29.ino b/Testing_Library/CAN-messages/motor_interface_test_script1.29.ino new file mode 100644 index 0000000..b676714 --- /dev/null +++ b/Testing_Library/CAN-messages/motor_interface_test_script1.29.ino @@ -0,0 +1,90 @@ +#include + +#define THROTTLE_ADDR 0x2F +#define REGEN_ADDR 0x2E +#define THROTTLE_MARKER 0xFF +#define REGEN_MARKER 0xFE +#define SWAP_COMMAND 0x1FF + +// Test Mode: 0 = Throttle only, 1 = Regen only +#define TEST_MODE 0 + +int currentMode = 0; // 0 = Throttle, 1 = Regen + +void setup() { + Serial.begin(9600); + while (!Serial); + Serial.println("Arduino Starting..."); + + // Initialize based on test mode + if (TEST_MODE == 1) { + // Start in regen mode + Wire.begin(REGEN_ADDR); + currentMode = 1; + Serial.println("Initialized on REGEN_ADDR (0x2E)"); + } else { + // Start in throttle mode (default) + Wire.begin(THROTTLE_ADDR); + currentMode = 0; + Serial.println("Initialized on THROTTLE_ADDR (0x2F)"); + } + + Wire.onReceive(receiveEvent); + Serial.println("Ready to receive I2C data"); + Serial.print("TEST_MODE: "); + Serial.println(TEST_MODE); +} + +void loop() { + delay(100); +} + +void swapMode() { + Wire.end(); + currentMode = !currentMode; + + if (currentMode == 0) { + Wire.begin(THROTTLE_ADDR); + Serial.println("Swapped to THROTTLE mode"); + } else { + Wire.begin(REGEN_ADDR); + Serial.println("Swapped to REGEN mode"); + } + Wire.onReceive(receiveEvent); +} + +void receiveEvent(int howMany) { + if (Wire.available() < 2) return; + + // Read 2 bytes (16-bit value) + int highByte = Wire.read(); + int lowByte = Wire.read(); + int value = ((highByte << 8) | lowByte) & 0x1FF; + + // Handle swap command + if (value == SWAP_COMMAND) { + if (TEST_MODE == 2) { // Only swap if in mode 2 (both) + swapMode(); + } + return; + } + + // Clamp value to valid range [0, 256] + if (value > 256) value = 256; + + + // Ensure value stays in valid range after inversion + if (value > 256) value = 256; + if (value < 0) value = 0; + + // Send based on current mode + if (currentMode == 0) { + Serial.write(THROTTLE_MARKER); + Serial.write(value & 0xFF); // Low byte + Serial.write((value >> 8) & 0x01); // Bit 8 + } else { + Serial.write(REGEN_MARKER); + Serial.write(value & 0xFF); // Low byte + Serial.write((value >> 8) & 0x01); // Bit 8 + } +} From 8f69c9830b2240ee69ad82fdc947339739a4469e Mon Sep 17 00:00:00 2001 From: Colby Wise Date: Thu, 29 Jan 2026 21:33:49 -0500 Subject: [PATCH 14/27] New File for System Wide Testing --- Testing_Library/test_all_comms.py | 51 +++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 Testing_Library/test_all_comms.py diff --git a/Testing_Library/test_all_comms.py b/Testing_Library/test_all_comms.py new file mode 100644 index 0000000..3a0d928 --- /dev/null +++ b/Testing_Library/test_all_comms.py @@ -0,0 +1,51 @@ +import sys +sys.path.append('/home/solarcar/solarcar/HiL_Testing/Server') + +from CANBus import CANBus +from CANMessage import CanMessage +from CANPi import readIn +import time +import serial +from gpioPins import DigitalInput, DigitalOutput + +# Testing CAN Comms +def testAddMethod(canBus : CANBus): + canBus.printCANBus() + + input("WAITING") + #Example CANMessage + name = "BPSError" + id = 0x106 + signals = {"internal_communications_fault" : 1} + timestamp = 1.0 + can_message = CanMessage(name, id, signals, timestamp) + canBus.addToCANBus(can_message) + canBus.printCANBus() + +def testReadThread(canBus : CANBus): + counter = 0 + while (counter < 10): + print(f'Reading number {counter}') + canBus.printCANBus() + time.sleep(0.5) + counter = counter + 1 + +#Send BPSError to Nucleo (currently turn LD2 off and on) +def testWrite(canBus : CANBus): + #Example CANMessage + name = "BPSError" + id = 0x106 + signals = {"internal_communications_fault" : 1} + timestamp = 1.0 + can_message = CanMessage(name, id, signals, timestamp) + counter = 0 + while (counter < 5): + print(f'Sending number {counter}') + canBus.sendMessage(can_message) + time.sleep(2) + counter = counter + 1 + +canBus = CANBus() +#testAddMethod(canBus) +testWrite(canBus) +#testReadThread(canBus) From 235dee9d2539523fd5a69c0e29338146b26bedce Mon Sep 17 00:00:00 2001 From: Andrew Amos Date: Sun, 1 Feb 2026 15:54:07 -0500 Subject: [PATCH 15/27] Developing Testing for all Comms --- Testing_Library/test_all_comms.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/Testing_Library/test_all_comms.py b/Testing_Library/test_all_comms.py index 3a0d928..5bd51be 100644 --- a/Testing_Library/test_all_comms.py +++ b/Testing_Library/test_all_comms.py @@ -45,7 +45,18 @@ def testWrite(canBus : CANBus): time.sleep(2) counter = counter + 1 -canBus = CANBus() -#testAddMethod(canBus) -testWrite(canBus) -#testReadThread(canBus) +def testCAN(): + canBus = CANBus() + testWrite(canBus) + testReadThread(canBus) + +def testAnalog(): +# def testDigital(): +# pinOut = DigitalOutput("GPIO2") +# pinIn = DigitalInput("GPIO11") +# for i in range(10): +# pinOut + +if __name__ == "__main__": + testCAN() + testAnalog() \ No newline at end of file From 79bb5d471278dacdb92c90e5e1192c69f2ca6b44 Mon Sep 17 00:00:00 2001 From: Andrew Amos Date: Sun, 8 Feb 2026 14:26:20 -0500 Subject: [PATCH 16/27] Added Test for Digital, Analog and CAN --- Testing_Library/test_all_comms.py | 70 +++++++++++++++++++++++++------ 1 file changed, 57 insertions(+), 13 deletions(-) diff --git a/Testing_Library/test_all_comms.py b/Testing_Library/test_all_comms.py index 5bd51be..cb1dbe9 100644 --- a/Testing_Library/test_all_comms.py +++ b/Testing_Library/test_all_comms.py @@ -7,8 +7,16 @@ import time import serial from gpioPins import DigitalInput, DigitalOutput +import random +import gpiozero +from gpioPins import AnalogOutput + +# Global Pins for Synchronization +pinOut = DigitalOutput("GPIO2") +pinIn = DigitalInput("GPIO11") + +# -----------Testing CAN Comms----------- -# Testing CAN Comms def testAddMethod(canBus : CANBus): canBus.printCANBus() @@ -23,8 +31,9 @@ def testAddMethod(canBus : CANBus): canBus.printCANBus() def testReadThread(canBus : CANBus): + time.sleep(1) # Wait for Nucleo to send CAN counter = 0 - while (counter < 10): + while (counter < 5): print(f'Reading number {counter}') canBus.printCANBus() time.sleep(0.5) @@ -39,24 +48,59 @@ def testWrite(canBus : CANBus): timestamp = 1.0 can_message = CanMessage(name, id, signals, timestamp) counter = 0 - while (counter < 5): - print(f'Sending number {counter}') - canBus.sendMessage(can_message) - time.sleep(2) - counter = counter + 1 + canBus.sendMessage(can_message) def testCAN(): canBus = CANBus() testWrite(canBus) + + pinOut.value = 0 # Block nucleo from proceeding + # Check that nucleo modified pin to 1 to switch to receiving + while pinIn.value == 0: + pinOut.value = 0 + time.sleep(0.1) + testReadThread(canBus) +#----------------Testing Analog Comms--------------- + +#Test case without the wrapper api +def send_to_nucleo(): + pinOut = gpiozero.PWMOutputDevice("GPIO5", frequency=5000) + + #Sending analog value to nucleo + while True: + pinOut.value = 0.64 + print(f"SENDING {pinOut.value}") + #pinOut.pulse() #Useful function to sweep all ranges + + time.sleep(2) + +#Test the wrapper api +def test_analog_class(pinOut): + value = round(random.uniform(1.0, 100.0), 2) # Generate random 2 d.p. float + + pinOut.write_voltage(value) + print(f'READING IN PIN VALUE: {pinOut.read()}') + def testAnalog(): -# def testDigital(): -# pinOut = DigitalOutput("GPIO2") -# pinIn = DigitalInput("GPIO11") -# for i in range(10): -# pinOut + pinOut = AnalogOutput("5") + test_analog_class(pinOut) if __name__ == "__main__": - testCAN() + # Wait for nucleo + while pinIn.value == 1: + pinOut.value = 0 + time.sleep(0.1) + + pinOut.value = 1 + testCAN() + + # Make sure finished sending + while pinIn.value == 1: + pinOut.value = 0 + print("THERE WAS A SYNC ERROR WITH RECEIVING CAN") + time.sleep(0.1) + + pinOut.value = 1 testAnalog() \ No newline at end of file From 018c0bd677202c1de668b31076d06c0444287853 Mon Sep 17 00:00:00 2001 From: Colby Wise Date: Thu, 29 Jan 2026 23:14:42 -0500 Subject: [PATCH 17/27] Changing Tabs --- Testing_Library/test_all_comms.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Testing_Library/test_all_comms.py b/Testing_Library/test_all_comms.py index cb1dbe9..9a9efba 100644 --- a/Testing_Library/test_all_comms.py +++ b/Testing_Library/test_all_comms.py @@ -12,8 +12,8 @@ from gpioPins import AnalogOutput # Global Pins for Synchronization -pinOut = DigitalOutput("GPIO2") -pinIn = DigitalInput("GPIO11") +pinOut = DigitalOutput("2") +pinIn = DigitalInput("11") # -----------Testing CAN Comms----------- @@ -89,7 +89,7 @@ def testAnalog(): if __name__ == "__main__": # Wait for nucleo - while pinIn.value == 1: + while pinIn.value == 1: pinOut.value = 0 time.sleep(0.1) @@ -103,4 +103,4 @@ def testAnalog(): time.sleep(0.1) pinOut.value = 1 - testAnalog() \ No newline at end of file + testAnalog() From e69acd1c575ba7c4b0976f73600b6546605d9620 Mon Sep 17 00:00:00 2001 From: abhinavp5 Date: Thu, 12 Feb 2026 16:38:00 -0500 Subject: [PATCH 18/27] fix: exception when stop reading for less error-y exit from test --- Testing_Library/MotorInterfaceTest.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Testing_Library/MotorInterfaceTest.py b/Testing_Library/MotorInterfaceTest.py index e09b2dc..641b586 100644 --- a/Testing_Library/MotorInterfaceTest.py +++ b/Testing_Library/MotorInterfaceTest.py @@ -62,7 +62,10 @@ def startReadThread(self): def readThrottleAndRegen(self): while(not self.stop_thread): with self.Lock: - byte_read = self.ser.read(1) + try: + byte_read = self.ser.read(1) + except serial.SerialException as e: + return if byte_read == THROTTLE_START_BYTE: # Throttle start marker data = self.ser.read(2) # Read 2 bytes for 9-bit value if len(data) == 2: From 6301b450383a590b3086b596d6bf50198d7f2ec0 Mon Sep 17 00:00:00 2001 From: abhinavp5 Date: Thu, 12 Feb 2026 17:15:07 -0500 Subject: [PATCH 19/27] feat: add motor interface test script for throttle and regen modes --- Testing_Library/MotorInterface.py | 124 ------------------ .../motor_interface_test_script1.29.ino | 6 +- 2 files changed, 3 insertions(+), 127 deletions(-) delete mode 100644 Testing_Library/MotorInterface.py rename Testing_Library/{ => motor_interface_test_script1.29}/motor_interface_test_script1.29.ino (91%) diff --git a/Testing_Library/MotorInterface.py b/Testing_Library/MotorInterface.py deleted file mode 100644 index 425f554..0000000 --- a/Testing_Library/MotorInterface.py +++ /dev/null @@ -1,124 +0,0 @@ -import sys -import serial -import time -import threading - - -sys.path.append('/home/solarcar/solarcar/HiL_Testing/Server') - -THROTTLE_ADDR = 0x2F -REGEN_ADDR = 0x2E - -# Serial Configuration -ser = serial.Serial( - port='/dev/ttyACM0', - baudrate=9600, - timeout=1 -) - -print("Waiting for Arduino to start...") -time.sleep(2) # Give Arduino time to boot -''' -while True: - byte_read = ser.read(1) - if byte_read == b'\xFF': - data = ser.read(3) # Read 3 bytes for two 9-bit values - if len(data) == 3: - # Unpack two 9-bit values from 3 bytes - # Byte 0: throttle[7:0] - # Byte 1: throttle[8] in bit 0, regen[6:0] in bits 7:1 - # Byte 2: regen[8:7] in bits 1:0 - - # Extracting throttle and regen - throttle = data[0] | ((data[1] & 0x01) << 8) - regen = ((data[1] >> 1) & 0x7F) | ((data[2] & 0x03) << 7) - - print(f"Throttle: {throttle}%) | Regen: {regen})") - else: - print(f"Incomplete data: got {len(data)} bytes instead of 3") - elif byte_read: - print(f"Unexpected byte:{byte_read}") -''' -ser = serial.Serial( - port='/dev/ttyACM0', - baudrate=9600, - timeout=1 - ) -''' -API for test Motor interface -''' -class MotorInterface: - def __init__(self): - self.stop_thread = False - self.mru_throttle = None - self.mru_regen = None - self.THROTTLE_ADDR = 0x2F - self.REGEN_ADDR = 0x2E - self.Lock = threading.Lock() - self.startReadThread() - - - #Run infinite thread to read in Throttle and Regen Values - def startReadThread(self): - read_thread = threading.Thread(target=self.readThrottleAndRegen) - read_thread.daemon = True - try: - read_thread.start() - except ValueError as e: - print(f"Exception") - def readThrottleAndRegen(self): - while(not self.stop_thread): - with self.Lock: - byte_read = ser.read(1) - if byte_read == b'\xFF': # Start Byte - data = ser.read(3) # Read 3 bytes for two 9-bit values - if len(data) == 3: - # two 9-bit values from 3 bytes - # Byte 0: throttle[7:0] - # Byte 1: throttle[8] in bit 0, regen[6:0] in bits 7:1 - # Byte 2: regen[8:7] in bits 1:0 - - # Extracting throttle and regen - self.mru_throttle = data[0] | ((data[1] & 0x01) << 8) - self.mru_regen = ((data[1] >> 1) & 0x7F) | ((data[2] & 0x03) << 7) - - def get_throttle(self) -> float: - # Normalized between [0-1.0] - if self.mru_throttle is None: - return None - return self.mru_throttle/256.0 - - def get_throttle_raw(self) -> int: - # raw between [0-256] - if self.mru_throttle is None: - return None - return self.mru_throttle - - def get_regen(self) -> float: - # Normalized between [0,1.0] - if self.mru_regen is None: - return None - return self.mru_regen/256.0 - - def get_regen_raw(self) -> int: - # raw between [0-256] - if self.mru_regen is None: - return None - return self.mru_regen - - -motor_interface = MotorInterface() - -while(True): - - throttle_raw = motor_interface.get_throttle_raw() - throttle_norm = motor_interface.get_throttle() - regen_raw = motor_interface.get_regen_raw() - regen_norm = motor_interface.get_regen() - print("Details") - print(throttle_raw) - print(throttle_norm) - print(regen_raw) - print(regen_norm) - time.sleep(1) - diff --git a/Testing_Library/motor_interface_test_script1.29.ino b/Testing_Library/motor_interface_test_script1.29/motor_interface_test_script1.29.ino similarity index 91% rename from Testing_Library/motor_interface_test_script1.29.ino rename to Testing_Library/motor_interface_test_script1.29/motor_interface_test_script1.29.ino index 729edaf..80d0dfa 100644 --- a/Testing_Library/motor_interface_test_script1.29.ino +++ b/Testing_Library/motor_interface_test_script1.29/motor_interface_test_script1.29.ino @@ -7,10 +7,10 @@ #define SWAP_COMMAND 0x1FF // Test Mode: 0 = Throttle only (listen on 0x2F), 1 = Regen only (listen on 0x2E). -// Must match which test is run: test_throttle needs TEST_MODE 0, test_regen needs TEST_MODE 1. -#define TEST_MODE 0 +// Must match PowerBoard main.cpp I2C_TEST_MODE: test_throttle → 0, test_regen → 1. +#define TEST_MODE 1 -int currentMode = 0; // 0 = Throttle, 1 = Regen +int currentMode = 1; // 0 = Throttle, 1 = Regen void setup() { Serial.begin(9600); From cab4a3a2e45af05f6e6f613c315eb78eb4e947fe Mon Sep 17 00:00:00 2001 From: Andrew Amos Date: Sun, 15 Feb 2026 14:39:36 -0500 Subject: [PATCH 20/27] Changed Syntax in Test_All_Comms --- Testing_Library/test_all_comms.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Testing_Library/test_all_comms.py b/Testing_Library/test_all_comms.py index 9a9efba..0e1b3ea 100644 --- a/Testing_Library/test_all_comms.py +++ b/Testing_Library/test_all_comms.py @@ -54,11 +54,11 @@ def testCAN(): canBus = CANBus() testWrite(canBus) - pinOut.value = 0 # Block nucleo from proceeding + pinOut.write(0) # Block nucleo from proceeding # Check that nucleo modified pin to 1 to switch to receiving - while pinIn.value == 0: - pinOut.value = 0 - time.sleep(0.1) + while pinIn.read() == 0: + pinOut.write(0) + time.sleep(0.5) testReadThread(canBus) @@ -89,18 +89,18 @@ def testAnalog(): if __name__ == "__main__": # Wait for nucleo - while pinIn.value == 1: - pinOut.value = 0 - time.sleep(0.1) + while pinIn.read() == 1: + pinOut.write(0) + time.sleep(0.5) - pinOut.value = 1 + pinOut.write(1) testCAN() # Make sure finished sending - while pinIn.value == 1: - pinOut.value = 0 + while pinIn.read() == 1: + pinOut.write(0) print("THERE WAS A SYNC ERROR WITH RECEIVING CAN") - time.sleep(0.1) + time.sleep(0.5) - pinOut.value = 1 + pinOut.write(1) testAnalog() From b60fea089f6339e8763193f6aa6b9d437df6e97d Mon Sep 17 00:00:00 2001 From: abhinavp5 Date: Sun, 15 Feb 2026 15:16:16 -0500 Subject: [PATCH 21/27] fix: added locks, and updated exceptions --- Testing_Library/MotorInterfaceTest.py | 55 +++++++++++++++------------ 1 file changed, 31 insertions(+), 24 deletions(-) diff --git a/Testing_Library/MotorInterfaceTest.py b/Testing_Library/MotorInterfaceTest.py index 641b586..e22ce48 100644 --- a/Testing_Library/MotorInterfaceTest.py +++ b/Testing_Library/MotorInterfaceTest.py @@ -36,10 +36,8 @@ class MotorInterfaceTest: """API for test Motor interface""" def __init__(self, port='/dev/ttyACM0', baudrate=9600): self.stop_thread = False - self.mru_throttle = None - self.mru_regen = None - self.THROTTLE_ADDR = 0x2F - self.REGEN_ADDR = 0x2E + self.mru_throttle = None # Most Recently Used Throttle Value + self.mru_regen = None # Most Recently Used Regen Value self.Lock = threading.Lock() self.ser = serial.Serial( port=port, @@ -55,17 +53,23 @@ def __init__(self, port='/dev/ttyACM0', baudrate=9600): def startReadThread(self): read_thread = threading.Thread(target=self.readThrottleAndRegen) read_thread.daemon = True - try: + try: read_thread.start() - except ValueError as e: - print(f"Exception") + except RuntimeError as e: + raise RuntimeError("Thread Was not Started") + def readThrottleAndRegen(self): while(not self.stop_thread): + # set the MRU Values to None so that that an excpetion is thrown not in the thread with self.Lock: try: byte_read = self.ser.read(1) except serial.SerialException as e: - return + print("Byte was not able to be read, check your connections and ensure that test modes are synced!!!") + # setting Both to None so that Exception is triggered within the getter methods + self.mru_throttle = None + self.mru_regen = None + if byte_read == THROTTLE_START_BYTE: # Throttle start marker data = self.ser.read(2) # Read 2 bytes for 9-bit value if len(data) == 2: @@ -73,7 +77,7 @@ def readThrottleAndRegen(self): # first byte contains lower 8 bits, second byte LSB is the 9th bit raw_from_arduino = data[0] | ((data[1] & 0x01) << 8) self.mru_throttle = 256 - raw_from_arduino # PowerBoard sends (0x100 - throttle) over I2C - print(f"Throttle: {self.mru_throttle}") + elif byte_read == REGEN_START_BYTE: data = self.ser.read(2) # Read 2 bytes for 9-bit value if len(data) == 2: @@ -81,28 +85,31 @@ def readThrottleAndRegen(self): # first byte contains lower 8 bits, second byte LSB is the 9th bit raw_from_arduino = data[0] | ((data[1] & 0x01) << 8) self.mru_regen = 256 - raw_from_arduino # PowerBoard sends (0x100 - regen) over I2C - print(f"Regen: {self.mru_regen}") def get_throttle(self) -> float: # Normalized between [0-1.0] - if self.mru_throttle is None: - return None - return self.mru_throttle/256.0 - + with self.lock: + if self.mru_throttle is None: + raise ValueError('MRU-Throttle Value is None') + return self.mru_throttle / 256.0 + def get_throttle_raw(self) -> int: # raw between [0-256] - if self.mru_throttle is None: - return None - return self.mru_throttle + with self.lock: + if self.mru_throttle is None: + raise ValueError('MRU-Raw-Throttle Value is None') + return self.mru_throttle def get_regen(self) -> float: # Normalized between [0,1.0] - if self.mru_regen is None: - return None - return self.mru_regen/256.0 - + with self.lock: + if self.mru_regen is None: + raise ValueError('MRU-Regen Value is None') + return self.mru_regen / 256.0 + def get_regen_raw(self) -> int: # raw between [0-256] - if self.mru_regen is None: - return None - return self.mru_regen + with self.lock: + if self.mru_regen is None: + raise ValueError('MRU-Regen-Raw Value is None') + return self.mru_regen From 3ae1439d8896980e80ff8ce4fa4a20d121ce5c3b Mon Sep 17 00:00:00 2001 From: abhinavp5 Date: Sun, 15 Feb 2026 15:24:12 -0500 Subject: [PATCH 22/27] fix: deleted dupe ard script# --- .DS_Store | Bin 6148 -> 6148 bytes .../motor_interface_test_script1.29.ino | 90 ------------------ 2 files changed, 90 deletions(-) delete mode 100644 Testing_Library/CAN-messages/motor_interface_test_script1.29.ino diff --git a/.DS_Store b/.DS_Store index 46ef8b11e9c97229d5f33c121682b41aa0f27341..7a45687aec548366c9185eea7c88b5a01c78139e 100644 GIT binary patch delta 78 zcmZoMXfc=|#>B)qu~2NHo+2ab#DLw4m>3yZC-X4gpS+e)o+B-#I5{alKWFnt#;+`! f**W+*fU1F<@640=MJzcO85n?wfnjri$QEV*hVvDE delta 66 zcmZoMXfc=|#>B`mu~2NHo+2a5#DLw5ER%Vd?r&COe#o-1!J2V1I|n}pP{n3N=I_jt U`9&-_7=VD0fq`jrfXEhR08NJv;{X5v diff --git a/Testing_Library/CAN-messages/motor_interface_test_script1.29.ino b/Testing_Library/CAN-messages/motor_interface_test_script1.29.ino deleted file mode 100644 index b676714..0000000 --- a/Testing_Library/CAN-messages/motor_interface_test_script1.29.ino +++ /dev/null @@ -1,90 +0,0 @@ -#include - -#define THROTTLE_ADDR 0x2F -#define REGEN_ADDR 0x2E -#define THROTTLE_MARKER 0xFF -#define REGEN_MARKER 0xFE -#define SWAP_COMMAND 0x1FF - -// Test Mode: 0 = Throttle only, 1 = Regen only -#define TEST_MODE 0 - -int currentMode = 0; // 0 = Throttle, 1 = Regen - -void setup() { - Serial.begin(9600); - while (!Serial); - Serial.println("Arduino Starting..."); - - // Initialize based on test mode - if (TEST_MODE == 1) { - // Start in regen mode - Wire.begin(REGEN_ADDR); - currentMode = 1; - Serial.println("Initialized on REGEN_ADDR (0x2E)"); - } else { - // Start in throttle mode (default) - Wire.begin(THROTTLE_ADDR); - currentMode = 0; - Serial.println("Initialized on THROTTLE_ADDR (0x2F)"); - } - - Wire.onReceive(receiveEvent); - Serial.println("Ready to receive I2C data"); - Serial.print("TEST_MODE: "); - Serial.println(TEST_MODE); -} - -void loop() { - delay(100); -} - -void swapMode() { - Wire.end(); - currentMode = !currentMode; - - if (currentMode == 0) { - Wire.begin(THROTTLE_ADDR); - Serial.println("Swapped to THROTTLE mode"); - } else { - Wire.begin(REGEN_ADDR); - Serial.println("Swapped to REGEN mode"); - } - Wire.onReceive(receiveEvent); -} - -void receiveEvent(int howMany) { - if (Wire.available() < 2) return; - - // Read 2 bytes (16-bit value) - int highByte = Wire.read(); - int lowByte = Wire.read(); - int value = ((highByte << 8) | lowByte) & 0x1FF; - - // Handle swap command - if (value == SWAP_COMMAND) { - if (TEST_MODE == 2) { // Only swap if in mode 2 (both) - swapMode(); - } - return; - } - - // Clamp value to valid range [0, 256] - if (value > 256) value = 256; - - - // Ensure value stays in valid range after inversion - if (value > 256) value = 256; - if (value < 0) value = 0; - - // Send based on current mode - if (currentMode == 0) { - Serial.write(THROTTLE_MARKER); - Serial.write(value & 0xFF); // Low byte - Serial.write((value >> 8) & 0x01); // Bit 8 - } else { - Serial.write(REGEN_MARKER); - Serial.write(value & 0xFF); // Low byte - Serial.write((value >> 8) & 0x01); // Bit 8 - } -} From 67a8334cae0eceaaac9e01f5d9826cf35d5fb191 Mon Sep 17 00:00:00 2001 From: abhinavp5 Date: Sun, 15 Feb 2026 15:41:50 -0500 Subject: [PATCH 23/27] fix: capitalized lock --- Testing_Library/MotorInterfaceTest.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Testing_Library/MotorInterfaceTest.py b/Testing_Library/MotorInterfaceTest.py index e22ce48..7b18617 100644 --- a/Testing_Library/MotorInterfaceTest.py +++ b/Testing_Library/MotorInterfaceTest.py @@ -88,28 +88,28 @@ def readThrottleAndRegen(self): def get_throttle(self) -> float: # Normalized between [0-1.0] - with self.lock: + with self.Lock: if self.mru_throttle is None: raise ValueError('MRU-Throttle Value is None') return self.mru_throttle / 256.0 def get_throttle_raw(self) -> int: # raw between [0-256] - with self.lock: + with self.Lock: if self.mru_throttle is None: raise ValueError('MRU-Raw-Throttle Value is None') return self.mru_throttle def get_regen(self) -> float: # Normalized between [0,1.0] - with self.lock: + with self.Lock: if self.mru_regen is None: raise ValueError('MRU-Regen Value is None') return self.mru_regen / 256.0 def get_regen_raw(self) -> int: # raw between [0-256] - with self.lock: + with self.Lock: if self.mru_regen is None: raise ValueError('MRU-Regen-Raw Value is None') return self.mru_regen From 2529569bec7ba6db89b68c556d59af1227652c68 Mon Sep 17 00:00:00 2001 From: abhinavp5 Date: Sun, 15 Feb 2026 15:59:35 -0500 Subject: [PATCH 24/27] fix: fixed deadlock issue --- Testing_Library/MotorInterfaceTest.py | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/Testing_Library/MotorInterfaceTest.py b/Testing_Library/MotorInterfaceTest.py index 7b18617..3daab24 100644 --- a/Testing_Library/MotorInterfaceTest.py +++ b/Testing_Library/MotorInterfaceTest.py @@ -61,22 +61,17 @@ def startReadThread(self): def readThrottleAndRegen(self): while(not self.stop_thread): # set the MRU Values to None so that that an excpetion is thrown not in the thread - with self.Lock: - try: - byte_read = self.ser.read(1) - except serial.SerialException as e: - print("Byte was not able to be read, check your connections and ensure that test modes are synced!!!") - # setting Both to None so that Exception is triggered within the getter methods - self.mru_throttle = None - self.mru_regen = None - + try: + byte_read = self.ser.read(1) + if byte_read == THROTTLE_START_BYTE: # Throttle start marker data = self.ser.read(2) # Read 2 bytes for 9-bit value if len(data) == 2: # motorinterface.cpp sends (0x100 - throttle) # first byte contains lower 8 bits, second byte LSB is the 9th bit raw_from_arduino = data[0] | ((data[1] & 0x01) << 8) - self.mru_throttle = 256 - raw_from_arduino # PowerBoard sends (0x100 - throttle) over I2C + with self.Lock: + self.mru_throttle = 256 - raw_from_arduino # PowerBoard sends (0x100 - throttle) over I2C elif byte_read == REGEN_START_BYTE: data = self.ser.read(2) # Read 2 bytes for 9-bit value @@ -84,8 +79,15 @@ def readThrottleAndRegen(self): # motorinterface.cpp sends (0x100 - regen) # first byte contains lower 8 bits, second byte LSB is the 9th bit raw_from_arduino = data[0] | ((data[1] & 0x01) << 8) - self.mru_regen = 256 - raw_from_arduino # PowerBoard sends (0x100 - regen) over I2C - + with self.Lock: + self.mru_regen = 256 - raw_from_arduino # PowerBoard sends (0x100 - regen) over I2C + + except serial.SerialException as e: + print("Byte was not able to be read, check your connections and ensure that test modes are synced!!!") + # setting Both to None so that Exception is triggered within the getter methods + self.mru_throttle = None + self.mru_regen = None + def get_throttle(self) -> float: # Normalized between [0-1.0] with self.Lock: From 5afb92b9a1cde58a752f32e38f0b9ae1dffc6f97 Mon Sep 17 00:00:00 2001 From: abhinavp5 Date: Sun, 15 Feb 2026 18:00:24 -0500 Subject: [PATCH 25/27] fix: added close method to closer serial port --- Testing_Library/MotorInterfaceTest.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Testing_Library/MotorInterfaceTest.py b/Testing_Library/MotorInterfaceTest.py index 3daab24..9a0743e 100644 --- a/Testing_Library/MotorInterfaceTest.py +++ b/Testing_Library/MotorInterfaceTest.py @@ -115,3 +115,9 @@ def get_regen_raw(self) -> int: if self.mru_regen is None: raise ValueError('MRU-Regen-Raw Value is None') return self.mru_regen + + def close(self): + self.stop_thread = True + if self.ser and self.ser.is_open: + self.ser.close() + From dc6911a42196adaf6b67e2daa0d25bac7a082840 Mon Sep 17 00:00:00 2001 From: abhinavp5 Date: Sun, 15 Feb 2026 18:04:17 -0500 Subject: [PATCH 26/27] fix: reverted TestRunner --- Server/TestRunner.py | 26 +++++++------------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/Server/TestRunner.py b/Server/TestRunner.py index fa2aa42..765ce8a 100644 --- a/Server/TestRunner.py +++ b/Server/TestRunner.py @@ -10,23 +10,15 @@ sys.path.append(new_root) sys.path.append(os.getcwd()) -from upload_wrapper import upload_firmware - -# Custom TestSuite that overrides the run method to run setup and teardown code; added code to maek firmware upload once per board, not test +# Custom TestSuite that overrides the run method to run setup and teardown code class CustomTestSuite(unittest.TestSuite): - def __init__(self, tests, board_name): - super().__init__(tests) - self.board_name = board_name - def run(self, result, debug=False): - setup_suite(self.board_name) + setup_suite() super().run(result, debug) - # teardown_suite(self.board_name) # optional - -def setup_suite(board_name): - print(f"[SUITE] Uploading firmware for {board_name}") - upload_firmware(board_name) + teardown_suite() +def setup_suite(): + print("Setting up suite...") # replace with setup code def teardown_suite(): print("Tearing down suite...") # replace with teardown code @@ -44,8 +36,6 @@ def make_suite(board_folder): def run_tests() -> None: board_names = config.REPO_CONFIG["boards"].keys() - print("DEBUG: Boards in config:", board_names) - all_suites = unittest.TestSuite() @@ -55,13 +45,11 @@ def run_tests() -> None: if os.path.isdir(board_folder): suite = make_suite(board_folder) - custom_suite = CustomTestSuite([suite], board) - all_suites.addTest(custom_suite) - print("Added test to custom suite") + custom_suite = CustomTestSuite([suite]) + all_suites.addTests(custom_suite) else: print(f"Warning: Folder for board '{board}' not found at path: {board_folder}") with open("test-results.xml", "wb") as output: runner = xmlrunner.XMLTestRunner(output=output, buffer=True) runner.run(all_suites) - From 45bcb162481aa7c56e479d748aad99b6c3dc2008 Mon Sep 17 00:00:00 2001 From: abhinavp5 Date: Sun, 15 Feb 2026 18:18:16 -0500 Subject: [PATCH 27/27] fix: final changes to review --- Testing_Library/MotorInterfaceTest.py | 9 ++++----- .../motor_interface_test_script1.29.ino | 11 ----------- 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/Testing_Library/MotorInterfaceTest.py b/Testing_Library/MotorInterfaceTest.py index 9a0743e..9ba17a6 100644 --- a/Testing_Library/MotorInterfaceTest.py +++ b/Testing_Library/MotorInterfaceTest.py @@ -3,7 +3,6 @@ import time import threading - sys.path.append('/home/solarcar/solarcar/HiL_Testing/Server') THROTTLE_ADDR = 0x2F @@ -49,7 +48,7 @@ def __init__(self, port='/dev/ttyACM0', baudrate=9600): self.startReadThread() - #Run infinite thread to read in Throttle and Regen Values + # Run infinite thread to read in Throttle and Regen Values def startReadThread(self): read_thread = threading.Thread(target=self.readThrottleAndRegen) read_thread.daemon = True @@ -57,10 +56,10 @@ def startReadThread(self): read_thread.start() except RuntimeError as e: raise RuntimeError("Thread Was not Started") - + + # Constantly read thorttle and regen values def readThrottleAndRegen(self): while(not self.stop_thread): - # set the MRU Values to None so that that an excpetion is thrown not in the thread try: byte_read = self.ser.read(1) @@ -83,7 +82,7 @@ def readThrottleAndRegen(self): self.mru_regen = 256 - raw_from_arduino # PowerBoard sends (0x100 - regen) over I2C except serial.SerialException as e: - print("Byte was not able to be read, check your connections and ensure that test modes are synced!!!") + print(e) # setting Both to None so that Exception is triggered within the getter methods self.mru_throttle = None self.mru_regen = None diff --git a/Testing_Library/motor_interface_test_script1.29/motor_interface_test_script1.29.ino b/Testing_Library/motor_interface_test_script1.29/motor_interface_test_script1.29.ino index 80d0dfa..1a32eeb 100644 --- a/Testing_Library/motor_interface_test_script1.29/motor_interface_test_script1.29.ino +++ b/Testing_Library/motor_interface_test_script1.29/motor_interface_test_script1.29.ino @@ -15,25 +15,19 @@ int currentMode = 1; // 0 = Throttle, 1 = Regen void setup() { Serial.begin(9600); while (!Serial); - Serial.println("Arduino Starting..."); // Initialize based on test mode if (TEST_MODE == 1) { // Start in regen mode Wire.begin(REGEN_ADDR); currentMode = 1; - Serial.println("Initialized on REGEN_ADDR (0x2E)"); } else { // Start in throttle mode (default) Wire.begin(THROTTLE_ADDR); currentMode = 0; - Serial.println("Initialized on THROTTLE_ADDR (0x2F)"); } Wire.onReceive(receiveEvent); - Serial.println("Ready to receive I2C data"); - Serial.print("TEST_MODE: "); - Serial.println(TEST_MODE); } void loop() { @@ -48,11 +42,6 @@ void receiveEvent(int howMany) { int lowByte = Wire.read(); int value = ((highByte << 8) | lowByte) & 0x1FF; - // Handle swap command - if (value == SWAP_COMMAND) { - //TODO: - return; - } // Clamp value to valid range [0, 256] if (value > 256) value = 256;