From e02c0a55919bca376e157de5c93b0b125684813c Mon Sep 17 00:00:00 2001 From: Atharv Nagavarapu <30nagava@elmbrookstudents.org> Date: Mon, 15 Dec 2025 13:35:06 +0000 Subject: [PATCH 01/45] Add diagnostics/ColorSensorDiagnostics.py --- diagnostics/ColorSensorDiagnostics.py | 49 +++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 diagnostics/ColorSensorDiagnostics.py diff --git a/diagnostics/ColorSensorDiagnostics.py b/diagnostics/ColorSensorDiagnostics.py new file mode 100644 index 0000000..a10e8be --- /dev/null +++ b/diagnostics/ColorSensorDiagnostics.py @@ -0,0 +1,49 @@ +from pybricks.hubs import PrimeHub +from pybricks.pupdevices import Motor, ColorSensor, UltrasonicSensor, ForceSensor +from pybricks.parameters import Button, Color, Direction, Port, Side, Stop +from pybricks.robotics import DriveBase +from pybricks.tools import wait, StopWatch + +hub = PrimeHub() +class ColorSensorDiagnostics: + def __init__(self): + self.colorsensor = None + self.port_map = { + "A": Port.A, + "B": Port.B, + "C": Port.C, + "D": Port.D, + "E": Port.E, + "F": Port.F, + } + def initializeColorSensor(self): + valid_ports = {"A", "B", "C", "D", "E", "F"} + while True: + colorinput = input( + "This will test your color sensor.\n" + "Enter the port for the color sensor you would like to test (A, B, C, D, E, or F): " + ).strip().upper() + if colorinput not in valid_ports: + print("Invalid port. Please enter A-F.") + continue + try: + if self.colorsensor is None: + self.colorsensor = ColorSensor(self.port_map[colorinput]) + print(f"Color Sensor initialized on port {colorinput}.") + else: + print(f"Reusing existing color sensor on port {colorinput}.") + break + + except OSError as e: + if e.errno == 16: # EBUSY + print(f"Port {colorinput} is already in use. Reusing existing color sensor.") + break + else: + print(f"Error initializing color sensor on port {colorinput}: {e}") + print("Make sure a color sensor is actually connected to this port.") + self.colorsensor = None + self.colorsensor.detectable_colors(Color.RED, Color.YELLOW, Color.GREEN, Color.BLUE, Color.WHITE, Color.NONE) + def printOutput(self): + while True: + print("HSV output:", self.colorsensor.hsv) + print("Detected color:", self.colorsensor.color()) From 979278d43798110e69cb4db7c323e942b55b0b58 Mon Sep 17 00:00:00 2001 From: Atharv Nagavarapu <30nagava@elmbrookstudents.org> Date: Wed, 17 Dec 2025 13:29:57 +0000 Subject: [PATCH 02/45] Update diagnostics/MotorDiagnostics.py --- diagnostics/MotorDiagnostics.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/diagnostics/MotorDiagnostics.py b/diagnostics/MotorDiagnostics.py index e8299c2..8501f3d 100644 --- a/diagnostics/MotorDiagnostics.py +++ b/diagnostics/MotorDiagnostics.py @@ -131,11 +131,11 @@ class MotorDiagnostics: print("\n FINAL MOTOR STATISTICS") final = (test180 + test540 + test1000) / 3 print("Final motor health score:", str(final) + "%") - if final < 80: + if final < 65: print("Your motor is in need of attention. Make sure to clean it regularly and charge the Prime Hub.") - elif final < 90: + elif final < 85: print("Your motor is in OK condition. Make sure to clean it regularly and charge the Prime Hub.") - elif final < 97: + elif final < 95: print("Your motor is in great condition!") else: print("Your motor is in AMAZING condition!!!") From 2500f3e09bb13f6fe1e8d872df592f8c066dc6cd Mon Sep 17 00:00:00 2001 From: Atharv Nagavarapu <30nagava@elmbrookstudents.org> Date: Wed, 17 Dec 2025 15:17:47 +0000 Subject: [PATCH 03/45] Add diagnostics/DriveBaseDiagnostics.py --- diagnostics/DriveBaseDiagnostics.py | 49 +++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 diagnostics/DriveBaseDiagnostics.py diff --git a/diagnostics/DriveBaseDiagnostics.py b/diagnostics/DriveBaseDiagnostics.py new file mode 100644 index 0000000..13d9487 --- /dev/null +++ b/diagnostics/DriveBaseDiagnostics.py @@ -0,0 +1,49 @@ +from pybricks.hubs import PrimeHub +from pybricks.pupdevices import Motor +from pybricks.parameters import Button, Color, Direction, Port, Side, Stop +from pybricks.robotics import DriveBase +from pybricks.tools import wait, StopWatch + +from usys import stdin +from uselect import poll + +hub = PrimeHub() +port_map = { + "A": Port.A, + "B": Port.B, + "C": Port.C, + "D": Port.D, + "E": Port.E, + "F": Port.F, + } +leftmotorport = input("Enter the left motor port: ") +left_motor = Motor(port_map[leftmotorport], Direction.COUNTERCLOCKWISE) +rightmotorport = input("Enter the right motor port: ") +right_motor = Motor(port_map[rightmotorport],Direction.CLOCKWISE) # Specify default direction + +# DriveBase configuration +WHEEL_DIAMETER = 68.8 # mm (adjust for your wheels) +AXLE_TRACK = 180 # mm (distance between wheels) +drive_base = DriveBase(left_motor, right_motor, WHEEL_DIAMETER, AXLE_TRACK) +drive_base.settings(600, 500, 300, 200) +drive_base.use_gyro(True) +# Register the standard input so we can read keyboard presses. +keyboard = poll() +keyboard.register(stdin) + +while True: + # Check if a key has been pressed. + if keyboard.poll(0): + # Read the key and print it. + key = stdin.read(1) + print("You pressed:", key) + if(key == "W"): + print("Insert robot go forward code here") + elif(key == "A"): + print("Insert robot go left code here") + elif(key == "S"): + print("Insert robot go backwards code here") + elif(key == "D"): + print("Insert robot go right code here") + else: + print("That key doesn't do anything.") \ No newline at end of file From e6a2c24338dca0a346e4d364834d0d5b795f10bb Mon Sep 17 00:00:00 2001 From: Atharv Nagavarapu <30nagava@elmbrookstudents.org> Date: Wed, 17 Dec 2025 15:18:12 +0000 Subject: [PATCH 04/45] Add diagnostics/HubDiagnostics.py --- diagnostics/HubDiagnostics.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 diagnostics/HubDiagnostics.py diff --git a/diagnostics/HubDiagnostics.py b/diagnostics/HubDiagnostics.py new file mode 100644 index 0000000..9f806fa --- /dev/null +++ b/diagnostics/HubDiagnostics.py @@ -0,0 +1,12 @@ +from pybricks.hubs import PrimeHub +from pybricks.pupdevices import Motor, ColorSensor, UltrasonicSensor, ForceSensor +from pybricks.parameters import Button, Color, Direction, Port, Side, Stop +from pybricks.robotics import DriveBase +from pybricks.tools import wait, StopWatch +from pybricks import version +import usys +hub = PrimeHub() + +print("Pybricks version information:", version) +print("MicroPython information:", usys.implementation) +print("MicroPython version:", usys.version) \ No newline at end of file From c1856220b180e527b739c8375115037dc4297e8a Mon Sep 17 00:00:00 2001 From: Atharv Nagavarapu <30nagava@elmbrookstudents.org> Date: Wed, 17 Dec 2025 15:20:45 +0000 Subject: [PATCH 05/45] Update diagnostics/HubDiagnostics.py --- diagnostics/HubDiagnostics.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/diagnostics/HubDiagnostics.py b/diagnostics/HubDiagnostics.py index 9f806fa..6e1831c 100644 --- a/diagnostics/HubDiagnostics.py +++ b/diagnostics/HubDiagnostics.py @@ -9,4 +9,7 @@ hub = PrimeHub() print("Pybricks version information:", version) print("MicroPython information:", usys.implementation) -print("MicroPython version:", usys.version) \ No newline at end of file +print("MicroPython version:", usys.version) +while True: + print(str(hub.imu.heading())) + # Insert display test here, with input to either test hub gyro stats or display or info. \ No newline at end of file From f28ec4994d085a431637db7b7d179ebf6025d84c Mon Sep 17 00:00:00 2001 From: Atharv Nagavarapu <30nagava@elmbrookstudents.org> Date: Wed, 17 Dec 2025 17:04:25 +0000 Subject: [PATCH 06/45] Update diagnostics/HubDiagnostics.py --- diagnostics/HubDiagnostics.py | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/diagnostics/HubDiagnostics.py b/diagnostics/HubDiagnostics.py index 6e1831c..add8727 100644 --- a/diagnostics/HubDiagnostics.py +++ b/diagnostics/HubDiagnostics.py @@ -4,12 +4,30 @@ from pybricks.parameters import Button, Color, Direction, Port, Side, Stop from pybricks.robotics import DriveBase from pybricks.tools import wait, StopWatch from pybricks import version +from OtherFunctions import vprint import usys -hub = PrimeHub() print("Pybricks version information:", version) print("MicroPython information:", usys.implementation) print("MicroPython version:", usys.version) -while True: - print(str(hub.imu.heading())) - # Insert display test here, with input to either test hub gyro stats or display or info. \ No newline at end of file +class HubDiagnostics: + def __init__(self): + self.hub = PrimeHub() + self.port_map = { + "A": Port.A, + "B": Port.B, + "C": Port.C, + "D": Port.D, + "E": Port.E, + "F": Port.F, + } + def testLightSources(self, verbose): + v = verbose + hub.display.off() + for x in range(5): + for y in range(5): + vprint(f"Turning on pixel at position {x}, {y}...", v) + display.pixel(x, y, brightness=100) + wait(100) + vprint(f"Turning off pixel at position {x}, {y}...", v) + display.pixel(x, y, brightness=0) From 4a6cfc99742ca28d0fda9cd1f50dd76502080da1 Mon Sep 17 00:00:00 2001 From: Atharv Nagavarapu <30nagava@elmbrookstudents.org> Date: Wed, 17 Dec 2025 17:04:45 +0000 Subject: [PATCH 07/45] Update diagnostics-old/BatteryDiagnostics.py --- {diagnostics => diagnostics-old}/BatteryDiagnostics.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {diagnostics => diagnostics-old}/BatteryDiagnostics.py (100%) diff --git a/diagnostics/BatteryDiagnostics.py b/diagnostics-old/BatteryDiagnostics.py similarity index 100% rename from diagnostics/BatteryDiagnostics.py rename to diagnostics-old/BatteryDiagnostics.py From f916a02d5a4aaa5daefce1fbe363d5736f6e2f97 Mon Sep 17 00:00:00 2001 From: Atharv Nagavarapu <30nagava@elmbrookstudents.org> Date: Wed, 17 Dec 2025 17:04:52 +0000 Subject: [PATCH 08/45] Update diagnostics-oldColorSensorDiagnostics.py --- ...nsorDiagnostics.py => diagnostics-oldColorSensorDiagnostics.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename diagnostics/ColorSensorDiagnostics.py => diagnostics-oldColorSensorDiagnostics.py (100%) diff --git a/diagnostics/ColorSensorDiagnostics.py b/diagnostics-oldColorSensorDiagnostics.py similarity index 100% rename from diagnostics/ColorSensorDiagnostics.py rename to diagnostics-oldColorSensorDiagnostics.py From 148d7a1c4353712c900ea661e9c10bd9ab33f251 Mon Sep 17 00:00:00 2001 From: Atharv Nagavarapu <30nagava@elmbrookstudents.org> Date: Wed, 17 Dec 2025 17:04:58 +0000 Subject: [PATCH 09/45] Update diagnostics-old/ColorSensorDiagnostics.py --- .../ColorSensorDiagnostics.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename diagnostics-oldColorSensorDiagnostics.py => diagnostics-old/ColorSensorDiagnostics.py (100%) diff --git a/diagnostics-oldColorSensorDiagnostics.py b/diagnostics-old/ColorSensorDiagnostics.py similarity index 100% rename from diagnostics-oldColorSensorDiagnostics.py rename to diagnostics-old/ColorSensorDiagnostics.py From 35b3726f46777a91f1f3fdab083e6aaef47feaba Mon Sep 17 00:00:00 2001 From: Atharv Nagavarapu <30nagava@elmbrookstudents.org> Date: Wed, 17 Dec 2025 17:05:05 +0000 Subject: [PATCH 10/45] Update diagnostics-old/DriveBaseDiagnostics.py --- {diagnostics => diagnostics-old}/DriveBaseDiagnostics.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {diagnostics => diagnostics-old}/DriveBaseDiagnostics.py (100%) diff --git a/diagnostics/DriveBaseDiagnostics.py b/diagnostics-old/DriveBaseDiagnostics.py similarity index 100% rename from diagnostics/DriveBaseDiagnostics.py rename to diagnostics-old/DriveBaseDiagnostics.py From 93d17833d15979178ce8a0142f424849eaf3180a Mon Sep 17 00:00:00 2001 From: Atharv Nagavarapu <30nagava@elmbrookstudents.org> Date: Wed, 17 Dec 2025 17:05:13 +0000 Subject: [PATCH 11/45] Update diagnostics-old/FullDiagnostics.py --- {diagnostics => diagnostics-old}/FullDiagnostics.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {diagnostics => diagnostics-old}/FullDiagnostics.py (100%) diff --git a/diagnostics/FullDiagnostics.py b/diagnostics-old/FullDiagnostics.py similarity index 100% rename from diagnostics/FullDiagnostics.py rename to diagnostics-old/FullDiagnostics.py From acbe26eb50350bab8991c416edfa7b6b12400b0f Mon Sep 17 00:00:00 2001 From: Atharv Nagavarapu <30nagava@elmbrookstudents.org> Date: Wed, 17 Dec 2025 17:05:21 +0000 Subject: [PATCH 12/45] Update diagnostics-old/HubDiagnostics.py --- {diagnostics => diagnostics-old}/HubDiagnostics.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {diagnostics => diagnostics-old}/HubDiagnostics.py (100%) diff --git a/diagnostics/HubDiagnostics.py b/diagnostics-old/HubDiagnostics.py similarity index 100% rename from diagnostics/HubDiagnostics.py rename to diagnostics-old/HubDiagnostics.py From 3efeafc55e9c8b3bfd6c5e91b51b379f9fb02eeb Mon Sep 17 00:00:00 2001 From: Atharv Nagavarapu <30nagava@elmbrookstudents.org> Date: Wed, 17 Dec 2025 17:05:29 +0000 Subject: [PATCH 13/45] Update diagnostics-old/MotorDiagnostics.py --- {diagnostics => diagnostics-old}/MotorDiagnostics.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {diagnostics => diagnostics-old}/MotorDiagnostics.py (100%) diff --git a/diagnostics/MotorDiagnostics.py b/diagnostics-old/MotorDiagnostics.py similarity index 100% rename from diagnostics/MotorDiagnostics.py rename to diagnostics-old/MotorDiagnostics.py From 8441446e44f45a9de4b1cdfaa9515ef61348e0b6 Mon Sep 17 00:00:00 2001 From: Atharv Nagavarapu <30nagava@elmbrookstudents.org> Date: Wed, 17 Dec 2025 17:25:07 +0000 Subject: [PATCH 14/45] Add diagnostics-old/OtherFunctions.py --- diagnostics-old/OtherFunctions.py | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 diagnostics-old/OtherFunctions.py diff --git a/diagnostics-old/OtherFunctions.py b/diagnostics-old/OtherFunctions.py new file mode 100644 index 0000000..2acb729 --- /dev/null +++ b/diagnostics-old/OtherFunctions.py @@ -0,0 +1,2 @@ +def vprint(string, verbose): + print("[LOG (verbose enabled)]", string) \ No newline at end of file From 95adf1de8455f712995283e9ea6474917282363c Mon Sep 17 00:00:00 2001 From: Atharv Nagavarapu <30nagava@elmbrookstudents.org> Date: Thu, 18 Dec 2025 16:42:55 +0000 Subject: [PATCH 15/45] Upload files to "diagnostics" --- diagnostics/BatteryDiagnostics.py | 49 ++++++++++++++++++++++ diagnostics/ColorSensorDiagnostics.py | 45 ++++++++++++++++++++ diagnostics/DriveBaseDiagnostics.py | 51 +++++++++++++++++++++++ diagnostics/FullDiagnostics.py | 59 +++++++++++++++++++++++++++ diagnostics/HubDiagnostics.py | 28 +++++++++++++ 5 files changed, 232 insertions(+) create mode 100644 diagnostics/BatteryDiagnostics.py create mode 100644 diagnostics/ColorSensorDiagnostics.py create mode 100644 diagnostics/DriveBaseDiagnostics.py create mode 100644 diagnostics/FullDiagnostics.py create mode 100644 diagnostics/HubDiagnostics.py diff --git a/diagnostics/BatteryDiagnostics.py b/diagnostics/BatteryDiagnostics.py new file mode 100644 index 0000000..682793f --- /dev/null +++ b/diagnostics/BatteryDiagnostics.py @@ -0,0 +1,49 @@ +from pybricks.tools import wait +import umath +class BatteryDiagnostics: + def __init__(self, hub): + self.voltage = 0 + self.current = 0 + self.hub = hub + def printVoltage(self): + self.voltage = self.hub.battery.voltage() + if self.voltage > 7800: + print(f"Battery voltage is sufficient: {self.voltage}") + elif self.voltage < 7800 : + print(f"Charging needed: {self.voltage}") + def printCurrent(self): + self.current = self.hub.battery.current() + print("Battery current:", self.current) + def printAll(self): + timeelapsed = 0 + voltageList = [] + currentList = [] + while True: + print("\n\n\n\n\n") + self.printVoltage() + voltageList.append(self.voltage) + self.printCurrent() + currentList.append(self.current) + wait(50) + timeelapsed += 50 + + if(timeelapsed >= 3000): + break + print("--------------FINAL RESULTS OF BATTERY DIAGNOSTICS---------------") + print("Voltage deviation:", self.stdev(voltageList)) + print("Current deviation:", self.stdev(currentList)) + def stdev(self, vals): + DATA = vals + if len(DATA) < 2: + return 0 + # Calculate the mean + MEAN = sum(DATA) / len(DATA) + + # Calculate the variance (sum of squared differences from the mean, divided by n-1 for sample standard deviation) + VARIANCE = sum([(x - MEAN) ** 2 for x in DATA]) / float(len(DATA) - 1) + + # Calculate the standard deviation (square root of the variance) + STD_DEV_MANUAL = umath.sqrt(VARIANCE) + + + return (STD_DEV_MANUAL) \ No newline at end of file diff --git a/diagnostics/ColorSensorDiagnostics.py b/diagnostics/ColorSensorDiagnostics.py new file mode 100644 index 0000000..6b14971 --- /dev/null +++ b/diagnostics/ColorSensorDiagnostics.py @@ -0,0 +1,45 @@ +from pybricks.parameters import Color, Port, Stop +from pybricks.tools import wait, StopWatch + +class ColorSensorDiagnostics: + def __init__(self, hub, colorsensorclass): + self.colorsensor = None + self.PORT_MAP = { + "A": Port.A, + "B": Port.B, + "C": Port.C, + "D": Port.D, + "E": Port.E, + "F": Port.F, + } + def initializeColorSensor(self): + VALID_PORTS = {"A", "B", "C", "D", "E", "F"} + while True: + colorinput = input( + "This will test your color sensor.\n" + "Enter the port for the color sensor you would like to test (A, B, C, D, E, or F): " + ).strip().upper() + if colorinput not in VALID_PORTS: + print("Invalid port. Please enter A-F.") + continue + try: + if self.colorsensor is None: + self.colorsensor = self.colorsensorclass(self.PORT_MAP[colorinput]) + print(f"Color Sensor initialized on port {colorinput}.") + else: + print(f"Reusing existing color sensor on port {colorinput}.") + break + + except OSError as e: + if e.errno == 16: # EBUSY + print(f"Port {colorinput} is already in use. Reusing existing color sensor.") + break + else: + print(f"Error initializing color sensor on port {colorinput}: {e}") + print("Make sure a color sensor is actually connected to this port.") + self.colorsensor = None + self.colorsensor.detectable_colors(Color.RED, Color.YELLOW, Color.GREEN, Color.BLUE, Color.WHITE, Color.NONE) + def printAll(self): + while True: + print("HSV output:", self.colorsensor.hsv) + print("Detected color:", self.colorsensor.color()) diff --git a/diagnostics/DriveBaseDiagnostics.py b/diagnostics/DriveBaseDiagnostics.py new file mode 100644 index 0000000..64a1e51 --- /dev/null +++ b/diagnostics/DriveBaseDiagnostics.py @@ -0,0 +1,51 @@ +from pybricks.parameters import Direction, Port, Side, Stop +from pybricks.robotics import DriveBase +from pybricks.tools import wait, StopWatch + +from usys import stdin +from uselect import poll + +class DriveBaseDiagnostics: + def __init__(self, hub, motorclass, dbclass): + self.PORT_MAP = { + "A": Port.A, + "B": Port.B, + "C": Port.C, + "D": Port.D, + "E": Port.E, + "F": Port.F, + } + def initializeDriveBase(): + leftmotorport = input("Enter the left motor port: ") + left_motor = self.motorclass(port_map[leftmotorport], Direction.COUNTERCLOCKWISE) + rightmotorport = input("Enter the right motor port: ") + right_motor = self.motorclass(port_map[rightmotorport],Direction.CLOCKWISE) + # DriveBase configuration + WHEEL_DIAMETER = 68.8 # mm + AXLE_TRACK = 180 # mm + drive_base = self.dbclass(left_motor, right_motor, WHEEL_DIAMETER, AXLE_TRACK) + drive_base.settings(600, 500, 300, 200) + drive_base.use_gyro(True) + def driveRobot(): + # Register the standard input so we can read keyboard presses. + keyboard = poll() + keyboard.register(stdin) + + while True: + # Check if a key has been pressed. + if keyboard.poll(0): + # Read the key and print it. + key = stdin.read(1) + print("You pressed:", key) + if(key == "W"): + print("Insert robot go forward code here") + elif(key == "A"): + print("Insert robot go left code here") + elif(key == "S"): + print("Insert robot go backwards code here") + elif(key == "D"): + print("Insert robot go right code here") + elif(key == "X") + break + else: + print("That key doesn't do anything.") \ No newline at end of file diff --git a/diagnostics/FullDiagnostics.py b/diagnostics/FullDiagnostics.py new file mode 100644 index 0000000..c6a1c10 --- /dev/null +++ b/diagnostics/FullDiagnostics.py @@ -0,0 +1,59 @@ +from pybricks.hubs import PrimeHub +from pybricks.pupdevices import Motor, ColorSensor, UltrasonicSensor, ForceSensor +from pybricks.parameters import Button, Color, Direction, Port, Side, Stop +from pybricks.robotics import DriveBase +from pybricks.tools import wait, StopWatch +HUB = PrimeHub() +from BatteryDiagnostics import BatteryDiagnostics +from MotorDiagnostics import MotorDiagnostics +from ColorSensorDiagnostics import ColorSensorDiagnostics +battery = BatteryDiagnostics(HUB) +motor = MotorDiagnostics(HUB, Motor) +colorsensor = ColorSensorDiagnostics(HUB, ColorSensor) +CLEARCONFIRM = input("Clear the console before proceeding? Y/N (default: yes): ") +if(CLEARCONFIRM == "Y" or CLEARCONFIRM == "y" or CLEARCONFIRM == "yes" or CLEARCONFIRM == ""): + print("Clearing console... \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n") +print(""" + ███████████ █████ █████ ██████ █████ █████████ ██████ ██████ █████ █████████ █████████ +▒▒███▒▒▒▒▒███▒▒███ ▒▒███ ▒▒██████ ▒▒███ ███▒▒▒▒▒███ ▒▒██████ ██████ ▒▒███ ███▒▒▒▒▒███ ███▒▒▒▒▒███ + ▒███ ▒███ ▒▒███ ███ ▒███▒███ ▒███ ▒███ ▒███ ▒███▒█████▒███ ▒███ ███ ▒▒▒ ▒███ ▒▒▒ + ▒██████████ ▒▒█████ ▒███▒▒███▒███ ▒███████████ ▒███▒▒███ ▒███ ▒███ ▒███ ▒▒█████████ + ▒███▒▒▒▒▒▒ ▒▒███ ▒███ ▒▒██████ ▒███▒▒▒▒▒███ ▒███ ▒▒▒ ▒███ ▒███ ▒███ ▒▒▒▒▒▒▒▒███ + ▒███ ▒███ ▒███ ▒▒█████ ▒███ ▒███ ▒███ ▒███ ▒███ ▒▒███ ███ ███ ▒███ + █████ █████ █████ ▒▒█████ █████ █████ █████ █████ █████ ▒▒█████████ ▒▒█████████ +▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒▒▒▒▒ ▒▒▒▒▒▒▒▒▒ + +The free and open source diagnostics tool for the LEGO® Education SPIKE™ Prime robots, designed for FIRST Lego League. +Developed by Team 65266, Lego Dynamics. + """ + ) +while True: + + print("\nWhich diagnostic do you want to perform?") + print("Enter 'b' for battery diagnostics") + print("Enter 'm' for motor diagnostics") + print("Enter 'cs' for color sensor diagnostics") + print("Enter 'q' to quit") + + choice = input("Your choice: ").strip().lower() + + if choice == "b": + print("-----------------------BATTERY DIAGNOSTICS-----------------------") + print("This test will check the battery voltage and current. It will measure these over a period of 3 seconds and provide average and deviation values. Your voltage should be above 7800 mV for optimal performance.") + input("Press Enter to begin the battery diagnostics.") + battery.printAll() + print("Battery diagnostics completed.") + + elif choice == "m": + print("------------------------MOTOR DIAGNOSTICS------------------------") + motor.fullTest() + print("Motor diagnostics completed.") + + elif choice == "q": + print("Diagnostics completed successfully. Exiting with code 0. Good luck in the robot game!") + break + elif choice == "cs": + print("---------------------COLOR SENSOR DIAGNOSTICS---------------------") + break + else: + print("Invalid choice. Please enter 'b', 'm', or 'q'.") \ No newline at end of file diff --git a/diagnostics/HubDiagnostics.py b/diagnostics/HubDiagnostics.py new file mode 100644 index 0000000..acb8906 --- /dev/null +++ b/diagnostics/HubDiagnostics.py @@ -0,0 +1,28 @@ +from pybricks.tools import wait, StopWatch +from pybricks import version +from OtherFunctions import vprint +import usys + +print("Pybricks version information:", version) +print("MicroPython information:", usys.implementation) +print("MicroPython version:", usys.version) +class HubDiagnostics: + def __init__(self, hub): + self.port_map = { + "A": Port.A, + "B": Port.B, + "C": Port.C, + "D": Port.D, + "E": Port.E, + "F": Port.F, + } + def testLightSources(self, verbose): + v = verbose + self.hub.display.off() + for x in range(5): + for y in range(5): + vprint(f"Turning on pixel at position {x}, {y}...", v) + display.pixel(x, y, brightness=100) + wait(100) + vprint(f"Turning off pixel at position {x}, {y}...", v) + display.pixel(x, y, brightness=0) From 9620a0d93598c5bc91ef2302db27dae7c399d08f Mon Sep 17 00:00:00 2001 From: Atharv Nagavarapu <30nagava@elmbrookstudents.org> Date: Thu, 18 Dec 2025 17:09:21 +0000 Subject: [PATCH 16/45] Upload files to "diagnostics" --- diagnostics/MotorDiagnostics.py | 135 ++++++++++++++++++++++++++++++++ diagnostics/OtherFunctions.py | 2 + 2 files changed, 137 insertions(+) create mode 100644 diagnostics/MotorDiagnostics.py create mode 100644 diagnostics/OtherFunctions.py diff --git a/diagnostics/MotorDiagnostics.py b/diagnostics/MotorDiagnostics.py new file mode 100644 index 0000000..8cc879e --- /dev/null +++ b/diagnostics/MotorDiagnostics.py @@ -0,0 +1,135 @@ +from pybricks.parameters import Direction, Port, Stop +from pybricks.tools import wait, StopWatch +import umath +class MotorDiagnostics: + def __init__(self, hub, motorclass): + self.testmotor = None + self.port_map = { + "A": Port.A, + "B": Port.B, + "C": Port.C, + "D": Port.D, + "E": Port.E, + "F": Port.F, + } + + def stdev(self, vals): + DATA = vals + if len(DATA) < 2: + return 0 + # Calculate the mean + MEAN = sum(DATA) / len(DATA) + + # Calculate the variance (sum of squared differences from the mean, divided by n-1 for sample standard deviation) + VARIANCE = sum([(x - MEAN) ** 2 for x in DATA]) / float(len(DATA) - 1) + + # Calculate the standard deviation (square root of the variance) + STD_DEV_MANUAL = umath.sqrt(VARIANCE) + + + return (STD_DEV_MANUAL) + def health_score(self, desired, avg_speed, stdev_speed, avg_load): + # Speed accuracy: penalize % error + ACCURACY = max(0, 100 - abs(avg_speed - desired) / desired * 100) + + # Stability: penalize deviation relative to desired + STABILITY = max(0, 100 - (stdev_speed / desired) * 100) + + # Normalize load: map 10 to 20 as baseline (around 0%), 200 as max (around 100%) + BASELINE = 15 # midpoint of idle range + MAX_OBSERVED = 200 # heavy load/stall + NORMALIZED_LOAD = max(0, avg_load - BASELINE) + LOAD_PCT = min(100, (NORMALIZED_LOAD / (MAX_OBSERVED - BASELINE)) * 100) + + LOAD_SCORE = max(0, 100 - LOAD_PCT) + + # Final score: average of the three + return (ACCURACY + STABILITY + LOAD_SCORE) / 3 + + def initializeMotor(self): + VALID_PORTS = {"A", "B", "C", "D", "E", "F"} + while True: + motorinput = input( + "This test will run your motor at 3 speeds: 180, 540, and 1000 degrees per second.\n" + "Please make sure your motor is not under any load (for example, your hand) during the test.\n" + "If you want to test the wheel's load, note that this will affect the load measurements.\n" + "Enter the port for the motor you would like to test (A, B, C, D, E, or F): " + ).strip().upper() + if motorinput not in VALID_PORTS: + print("That is not a valid port. Please enter A-F.") + continue + try: + # Only create a new Motor if we don't already have one + if self.testmotor is None: + self.testmotor = self.motorclass(self.port_map[motorinput]) + print(f"Motor initialized on port {motorinput}.") + else: + print(f"Reusing existing motor on port {motorinput}.") + break + + except OSError as e: + if e.errno == 16: # EBUSY + print(f"Port {motorinput} is already in use. Reusing existing motor.") + # Do not overwrite self.testmotor here — keep the existing reference + break + else: + print(f"Error initializing motor on port {motorinput}: {e}") + print("Make sure a motor is actually connected to this port.") + self.testmotor = None + + def testSpeed(self, speed): + self.testmotor.reset_angle(0) + + motorspeeds = [] + motorloads = [] + TARGET_ANGLE = speed * 3 + print("\n", speed, "DEGREES PER SECOND TEST") + self.testmotor.run_angle(speed, TARGET_ANGLE, Stop.HOLD, False) + stopwatchmotor = StopWatch() + while stopwatchmotor.time() < 3000: + wait(20) + motorspeeds.append(self.testmotor.speed()) + motorloads.append(self.testmotor.load()) + + MAX_SPEED, MAX_ACCEL, MAX_TORQUE = self.testmotor.control.limits() + + + print("Desired motor speed: ", str(speed)) + if motorspeeds: + avg = sum(motorspeeds) / len(motorspeeds) + print("Average motor speed:", avg) + print("Motor speed deviation:", str(self.stdev(motorspeeds))) + else: + print("No speed samples collected.") + avg = 0 + if motorloads: + avgload = sum(motorloads) / len(motorloads) + + print("Average motor load:", avgload) + print("Motor load deviation:", str(self.stdev(motorloads))) + else: + print("No load samples collected.") + avgload = 0 + SCORE = self.health_score(speed, avg, self.stdev(motorspeeds), avgload) + print("Health score for this test:", str(SCORE) + "%") + return SCORE + def fullTest(self): + self.initializeMotor() + print("Load measurements are in mNm. Speed measurements are in degrees per second.") + MAX_SPEED, MAX_ACCEL, MAX_TORQUE = self.testmotor.control.limits() + print("Maximum motor speed:", MAX_SPEED) + test180 = self.testSpeed(180) + test540 = self.testSpeed(540) + test1000 = self.testSpeed(1000) + print("\n FINAL MOTOR STATISTICS") + final = (test180 + test540 + test1000) / 3 + print("Final motor health score:", str(final) + "%") + if final < 65: + print("Your motor is in need of attention. Make sure to clean it regularly and charge the Prime Hub.") + elif final < 85: + print("Your motor is in OK condition. Make sure to clean it regularly and charge the Prime Hub.") + elif final < 95: + print("Your motor is in great condition!") + else: + print("Your motor is in AMAZING condition!!!") + self.testmotor.stop() \ No newline at end of file diff --git a/diagnostics/OtherFunctions.py b/diagnostics/OtherFunctions.py new file mode 100644 index 0000000..c3290a5 --- /dev/null +++ b/diagnostics/OtherFunctions.py @@ -0,0 +1,2 @@ +def vprint(string, verbose): + print("[LOG (verbose)]", string) \ No newline at end of file From 6884969cc1d3485ca12a1baf097cb73312c49746 Mon Sep 17 00:00:00 2001 From: Atharv Nagavarapu <30nagava@elmbrookstudents.org> Date: Thu, 18 Dec 2025 17:11:51 +0000 Subject: [PATCH 17/45] Update diagnostics/ColorSensorDiagnostics.py --- diagnostics/ColorSensorDiagnostics.py | 1 + 1 file changed, 1 insertion(+) diff --git a/diagnostics/ColorSensorDiagnostics.py b/diagnostics/ColorSensorDiagnostics.py index 6b14971..1f97108 100644 --- a/diagnostics/ColorSensorDiagnostics.py +++ b/diagnostics/ColorSensorDiagnostics.py @@ -40,6 +40,7 @@ class ColorSensorDiagnostics: self.colorsensor = None self.colorsensor.detectable_colors(Color.RED, Color.YELLOW, Color.GREEN, Color.BLUE, Color.WHITE, Color.NONE) def printAll(self): + self.initializeColorSensor() while True: print("HSV output:", self.colorsensor.hsv) print("Detected color:", self.colorsensor.color()) From 4473d794bda64427f0d273d5d85113e8b44b5514 Mon Sep 17 00:00:00 2001 From: Atharv Nagavarapu <30nagava@elmbrookstudents.org> Date: Thu, 18 Dec 2025 17:12:44 +0000 Subject: [PATCH 18/45] Update diagnostics/FullDiagnostics.py --- diagnostics/FullDiagnostics.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/diagnostics/FullDiagnostics.py b/diagnostics/FullDiagnostics.py index c6a1c10..3f2946b 100644 --- a/diagnostics/FullDiagnostics.py +++ b/diagnostics/FullDiagnostics.py @@ -54,6 +54,8 @@ while True: break elif choice == "cs": print("---------------------COLOR SENSOR DIAGNOSTICS---------------------") + colorsensor.printAll() + print("Color sensor diagnostics completed.") break else: print("Invalid choice. Please enter 'b', 'm', or 'q'.") \ No newline at end of file From 5b9ab7daed3f98d93663d83b0f5a749c97e3f6e3 Mon Sep 17 00:00:00 2001 From: Atharv Nagavarapu <30nagava@elmbrookstudents.org> Date: Thu, 18 Dec 2025 17:14:13 +0000 Subject: [PATCH 19/45] Update diagnostics/ColorSensorDiagnostics.py --- diagnostics/ColorSensorDiagnostics.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/diagnostics/ColorSensorDiagnostics.py b/diagnostics/ColorSensorDiagnostics.py index 1f97108..a3fdf1a 100644 --- a/diagnostics/ColorSensorDiagnostics.py +++ b/diagnostics/ColorSensorDiagnostics.py @@ -41,6 +41,7 @@ class ColorSensorDiagnostics: self.colorsensor.detectable_colors(Color.RED, Color.YELLOW, Color.GREEN, Color.BLUE, Color.WHITE, Color.NONE) def printAll(self): self.initializeColorSensor() - while True: + stopwatch = StopWatch() + while stopwatch.time < 5000: print("HSV output:", self.colorsensor.hsv) - print("Detected color:", self.colorsensor.color()) + print("Detected color:", self.colorsensor.color()) \ No newline at end of file From ae9cbac032acd0679018f1280cefad64aa8aa691 Mon Sep 17 00:00:00 2001 From: Atharv Nagavarapu <30nagava@elmbrookstudents.org> Date: Thu, 18 Dec 2025 19:09:39 +0000 Subject: [PATCH 20/45] Update diagnostics/FullDiagnostics.py --- diagnostics/FullDiagnostics.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/diagnostics/FullDiagnostics.py b/diagnostics/FullDiagnostics.py index 3f2946b..3d7bcfc 100644 --- a/diagnostics/FullDiagnostics.py +++ b/diagnostics/FullDiagnostics.py @@ -23,7 +23,7 @@ print(""" █████ █████ █████ ▒▒█████ █████ █████ █████ █████ █████ ▒▒█████████ ▒▒█████████ ▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒▒▒▒▒ ▒▒▒▒▒▒▒▒▒ -The free and open source diagnostics tool for the LEGO® Education SPIKE™ Prime robots, designed for FIRST Lego League. +The free and open source diagnostics tool for LEGO® Education SPIKE™ Prime robots, designed for FIRST Lego League. Developed by Team 65266, Lego Dynamics. """ ) From c8b72eff515434dafd41e9528cd7b916422fb444 Mon Sep 17 00:00:00 2001 From: Atharv Nagavarapu <30nagava@elmbrookstudents.org> Date: Thu, 18 Dec 2025 19:22:25 +0000 Subject: [PATCH 21/45] Update diagnostics/HubDiagnostics.py --- diagnostics/HubDiagnostics.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/diagnostics/HubDiagnostics.py b/diagnostics/HubDiagnostics.py index acb8906..ee5113f 100644 --- a/diagnostics/HubDiagnostics.py +++ b/diagnostics/HubDiagnostics.py @@ -1,6 +1,6 @@ from pybricks.tools import wait, StopWatch from pybricks import version -from OtherFunctions import vprint +import OtherFunctions as debug import usys print("Pybricks version information:", version) @@ -21,8 +21,8 @@ class HubDiagnostics: self.hub.display.off() for x in range(5): for y in range(5): - vprint(f"Turning on pixel at position {x}, {y}...", v) + debug.log(f"Turning on pixel at position {x}, {y}...", v) display.pixel(x, y, brightness=100) wait(100) - vprint(f"Turning off pixel at position {x}, {y}...", v) + debug.log(f"Turning off pixel at position {x}, {y}...", v) display.pixel(x, y, brightness=0) From 3408ed576037ae287138fb4052c68228f87751b7 Mon Sep 17 00:00:00 2001 From: Atharv Nagavarapu <30nagava@elmbrookstudents.org> Date: Thu, 18 Dec 2025 19:22:39 +0000 Subject: [PATCH 22/45] Update diagnostics/OtherFunctions.py --- diagnostics/OtherFunctions.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/diagnostics/OtherFunctions.py b/diagnostics/OtherFunctions.py index c3290a5..a7638a9 100644 --- a/diagnostics/OtherFunctions.py +++ b/diagnostics/OtherFunctions.py @@ -1,2 +1,4 @@ -def vprint(string, verbose): - print("[LOG (verbose)]", string) \ No newline at end of file +verbose = True +def log(string): + if(verbose): + print("[LOG (verbose)]", string) From 2b44be0315f11c7fc198f5b505cd28e3462c4220 Mon Sep 17 00:00:00 2001 From: Atharv Nagavarapu <30nagava@elmbrookstudents.org> Date: Fri, 19 Dec 2025 22:40:10 +0000 Subject: [PATCH 23/45] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 1fde449..464515d 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ Included utilities: - Diagnostics - a program that allows you to diagnose issues and test parts of your robot, such as battery, motor, and color sensor. Open each program in the ```diagnostics``` folder in Pybricks, connect your robot, switch to the ```FullDiagnostics.py``` file and press run. - Color Sensor Tests (UPCOMING) - a program that identifies what color the sensor is detecting. If you'd like, you can use our color ranges in your own programs. +Please set your window size to 90% on small screens for best results with the ASCII art. This code is licensed under the Creative Commons Attribution 4.0 International License (CC BY 4.0). Without the confusing legal speak, this means that you are free to: From 2967456390a876553b2a19d773053e788eb9b5d6 Mon Sep 17 00:00:00 2001 From: Atharv Nagavarapu <30nagava@elmbrookstudents.org> Date: Fri, 19 Dec 2025 22:40:17 +0000 Subject: [PATCH 24/45] Delete diagnostics-old/BatteryDiagnostics.py --- diagnostics-old/BatteryDiagnostics.py | 55 --------------------------- 1 file changed, 55 deletions(-) delete mode 100644 diagnostics-old/BatteryDiagnostics.py diff --git a/diagnostics-old/BatteryDiagnostics.py b/diagnostics-old/BatteryDiagnostics.py deleted file mode 100644 index 0369701..0000000 --- a/diagnostics-old/BatteryDiagnostics.py +++ /dev/null @@ -1,55 +0,0 @@ -from pybricks.pupdevices import Motor, ColorSensor, UltrasonicSensor, ForceSensor -from pybricks.parameters import Button, Color, Direction, Port, Side, Stop -from pybricks.tools import run_task, multitask -from pybricks.tools import wait, StopWatch -from pybricks.robotics import DriveBase -from pybricks.hubs import PrimeHub -import umath -# Initialize hub and devices -hub = PrimeHub() -class BatteryDiagnostics: - def __init__(self): - self.voltage = 0 - self.current = 0 - def printVoltage(self): - self.voltage = hub.battery.voltage() - if self.voltage > 7800: - print(f"Battery voltage is sufficient: {self.voltage}") - elif self.voltage < 7800 : - print(f"Charging needed: {self.voltage}") - def printCurrent(self): - self.current = hub.battery.current() - print("Battery current:", self.current) - def printAll(self): - timeelapsed = 0 - voltageList = [] - currentList = [] - while True: - print("\n\n\n\n\n") - self.printVoltage() - voltageList.append(self.voltage) - self.printCurrent() - currentList.append(self.current) - wait(50) - timeelapsed += 50 - - if(timeelapsed >= 3000): - break - print("--------------FINAL RESULTS OF BATTERY DIAGNOSTICS---------------") - print("Voltage deviation:", self.stdev(voltageList)) - print("Current deviation:", self.stdev(currentList)) - def stdev(self, vals): - data = vals - if len(data) < 2: - return 0 - # Calculate the mean - mean = sum(data) / len(data) - - # Calculate the variance (sum of squared differences from the mean, divided by n-1 for sample standard deviation) - variance = sum([(x - mean) ** 2 for x in data]) / float(len(data) - 1) - - # Calculate the standard deviation (square root of the variance) - std_dev_manual = umath.sqrt(variance) - - - return (std_dev_manual) \ No newline at end of file From 657d706d82f6206fa4ab3cc22c6b58d51d2a8142 Mon Sep 17 00:00:00 2001 From: Atharv Nagavarapu <30nagava@elmbrookstudents.org> Date: Fri, 19 Dec 2025 22:40:21 +0000 Subject: [PATCH 25/45] Delete diagnostics-old/ColorSensorDiagnostics.py --- diagnostics-old/ColorSensorDiagnostics.py | 49 ----------------------- 1 file changed, 49 deletions(-) delete mode 100644 diagnostics-old/ColorSensorDiagnostics.py diff --git a/diagnostics-old/ColorSensorDiagnostics.py b/diagnostics-old/ColorSensorDiagnostics.py deleted file mode 100644 index a10e8be..0000000 --- a/diagnostics-old/ColorSensorDiagnostics.py +++ /dev/null @@ -1,49 +0,0 @@ -from pybricks.hubs import PrimeHub -from pybricks.pupdevices import Motor, ColorSensor, UltrasonicSensor, ForceSensor -from pybricks.parameters import Button, Color, Direction, Port, Side, Stop -from pybricks.robotics import DriveBase -from pybricks.tools import wait, StopWatch - -hub = PrimeHub() -class ColorSensorDiagnostics: - def __init__(self): - self.colorsensor = None - self.port_map = { - "A": Port.A, - "B": Port.B, - "C": Port.C, - "D": Port.D, - "E": Port.E, - "F": Port.F, - } - def initializeColorSensor(self): - valid_ports = {"A", "B", "C", "D", "E", "F"} - while True: - colorinput = input( - "This will test your color sensor.\n" - "Enter the port for the color sensor you would like to test (A, B, C, D, E, or F): " - ).strip().upper() - if colorinput not in valid_ports: - print("Invalid port. Please enter A-F.") - continue - try: - if self.colorsensor is None: - self.colorsensor = ColorSensor(self.port_map[colorinput]) - print(f"Color Sensor initialized on port {colorinput}.") - else: - print(f"Reusing existing color sensor on port {colorinput}.") - break - - except OSError as e: - if e.errno == 16: # EBUSY - print(f"Port {colorinput} is already in use. Reusing existing color sensor.") - break - else: - print(f"Error initializing color sensor on port {colorinput}: {e}") - print("Make sure a color sensor is actually connected to this port.") - self.colorsensor = None - self.colorsensor.detectable_colors(Color.RED, Color.YELLOW, Color.GREEN, Color.BLUE, Color.WHITE, Color.NONE) - def printOutput(self): - while True: - print("HSV output:", self.colorsensor.hsv) - print("Detected color:", self.colorsensor.color()) From 46cf6dd3f3e864d3ca3252ceb0a724676f6c62f0 Mon Sep 17 00:00:00 2001 From: Atharv Nagavarapu <30nagava@elmbrookstudents.org> Date: Fri, 19 Dec 2025 22:40:25 +0000 Subject: [PATCH 26/45] Delete diagnostics-old/DriveBaseDiagnostics.py --- diagnostics-old/DriveBaseDiagnostics.py | 49 ------------------------- 1 file changed, 49 deletions(-) delete mode 100644 diagnostics-old/DriveBaseDiagnostics.py diff --git a/diagnostics-old/DriveBaseDiagnostics.py b/diagnostics-old/DriveBaseDiagnostics.py deleted file mode 100644 index 13d9487..0000000 --- a/diagnostics-old/DriveBaseDiagnostics.py +++ /dev/null @@ -1,49 +0,0 @@ -from pybricks.hubs import PrimeHub -from pybricks.pupdevices import Motor -from pybricks.parameters import Button, Color, Direction, Port, Side, Stop -from pybricks.robotics import DriveBase -from pybricks.tools import wait, StopWatch - -from usys import stdin -from uselect import poll - -hub = PrimeHub() -port_map = { - "A": Port.A, - "B": Port.B, - "C": Port.C, - "D": Port.D, - "E": Port.E, - "F": Port.F, - } -leftmotorport = input("Enter the left motor port: ") -left_motor = Motor(port_map[leftmotorport], Direction.COUNTERCLOCKWISE) -rightmotorport = input("Enter the right motor port: ") -right_motor = Motor(port_map[rightmotorport],Direction.CLOCKWISE) # Specify default direction - -# DriveBase configuration -WHEEL_DIAMETER = 68.8 # mm (adjust for your wheels) -AXLE_TRACK = 180 # mm (distance between wheels) -drive_base = DriveBase(left_motor, right_motor, WHEEL_DIAMETER, AXLE_TRACK) -drive_base.settings(600, 500, 300, 200) -drive_base.use_gyro(True) -# Register the standard input so we can read keyboard presses. -keyboard = poll() -keyboard.register(stdin) - -while True: - # Check if a key has been pressed. - if keyboard.poll(0): - # Read the key and print it. - key = stdin.read(1) - print("You pressed:", key) - if(key == "W"): - print("Insert robot go forward code here") - elif(key == "A"): - print("Insert robot go left code here") - elif(key == "S"): - print("Insert robot go backwards code here") - elif(key == "D"): - print("Insert robot go right code here") - else: - print("That key doesn't do anything.") \ No newline at end of file From 5ab2af15fb5c087bfb1ac9320f08eb35da8345bc Mon Sep 17 00:00:00 2001 From: Atharv Nagavarapu <30nagava@elmbrookstudents.org> Date: Fri, 19 Dec 2025 22:40:29 +0000 Subject: [PATCH 27/45] Delete diagnostics-old/FullDiagnostics.py --- diagnostics-old/FullDiagnostics.py | 54 ------------------------------ 1 file changed, 54 deletions(-) delete mode 100644 diagnostics-old/FullDiagnostics.py diff --git a/diagnostics-old/FullDiagnostics.py b/diagnostics-old/FullDiagnostics.py deleted file mode 100644 index 591c00b..0000000 --- a/diagnostics-old/FullDiagnostics.py +++ /dev/null @@ -1,54 +0,0 @@ -from pybricks.hubs import PrimeHub -from pybricks.pupdevices import Motor, ColorSensor, UltrasonicSensor, ForceSensor -from pybricks.parameters import Button, Color, Direction, Port, Side, Stop -from pybricks.robotics import DriveBase -from pybricks.tools import wait, StopWatch -hub = PrimeHub() -from BatteryDiagnostics import BatteryDiagnostics -from MotorDiagnostics import MotorDiagnostics -battery = BatteryDiagnostics() -motor = MotorDiagnostics() -clearConfirmation = input("Do you want to clear the console before proceeding? Y/N (default: yes): ") -if(clearConfirmation == "Y" or clearConfirmation == "y" or clearConfirmation == "yes" or clearConfirmation == ""): - print("Clearing console... \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n") -print(""" - ███████████ █████ █████ ██████ █████ █████████ ██████ ██████ █████ █████████ █████████ -▒▒███▒▒▒▒▒███▒▒███ ▒▒███ ▒▒██████ ▒▒███ ███▒▒▒▒▒███ ▒▒██████ ██████ ▒▒███ ███▒▒▒▒▒███ ███▒▒▒▒▒███ - ▒███ ▒███ ▒▒███ ███ ▒███▒███ ▒███ ▒███ ▒███ ▒███▒█████▒███ ▒███ ███ ▒▒▒ ▒███ ▒▒▒ - ▒██████████ ▒▒█████ ▒███▒▒███▒███ ▒███████████ ▒███▒▒███ ▒███ ▒███ ▒███ ▒▒█████████ - ▒███▒▒▒▒▒▒ ▒▒███ ▒███ ▒▒██████ ▒███▒▒▒▒▒███ ▒███ ▒▒▒ ▒███ ▒███ ▒███ ▒▒▒▒▒▒▒▒███ - ▒███ ▒███ ▒███ ▒▒█████ ▒███ ▒███ ▒███ ▒███ ▒███ ▒▒███ ███ ███ ▒███ - █████ █████ █████ ▒▒█████ █████ █████ █████ █████ █████ ▒▒█████████ ▒▒█████████ -▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒▒▒▒▒ ▒▒▒▒▒▒▒▒▒ - -The free and open source diagnostics tool for the LEGO® Education SPIKE™ Prime robots, designed for FIRST Lego League. -Developed by Team 65266, Lego Dynamics. - """ - ) -while True: - - print("\nWhat diagnostic do you want to perform?") - print("Enter 'b' for Battery diagnostics") - print("Enter 'm' for Motor diagnostics") - print("Enter 'q' to Quit") - - choice = input("Your choice: ").strip().lower() - - if choice == "b": - print("-----------------------BATTERY DIAGNOSTICS-----------------------") - print("This test will check the battery voltage and current. It will measure the voltage and current over a period of 3 seconds and provide average values and deviation values. Your voltage should be above 7800 mV for optimal performance.") - input("Press Enter to begin the battery diagnostics.") - battery.printAll() - print("Battery diagnostics completed.") - - elif choice == "m": - print("------------------------MOTOR DIAGNOSTICS------------------------") - motor.fullTest() - print("Motor diagnostics completed.") - - elif choice == "q": - print("Diagnostics completed successfully. Exiting with code 0. Good luck in the robot game!") - break - - else: - print("Invalid choice. Please enter 'b', 'm', or 'q'.") \ No newline at end of file From 5724bd9b81dc21f18fc9c3cdf2c58be0a89a407b Mon Sep 17 00:00:00 2001 From: Atharv Nagavarapu <30nagava@elmbrookstudents.org> Date: Fri, 19 Dec 2025 22:40:33 +0000 Subject: [PATCH 28/45] Delete diagnostics-old/HubDiagnostics.py --- diagnostics-old/HubDiagnostics.py | 33 ------------------------------- 1 file changed, 33 deletions(-) delete mode 100644 diagnostics-old/HubDiagnostics.py diff --git a/diagnostics-old/HubDiagnostics.py b/diagnostics-old/HubDiagnostics.py deleted file mode 100644 index add8727..0000000 --- a/diagnostics-old/HubDiagnostics.py +++ /dev/null @@ -1,33 +0,0 @@ -from pybricks.hubs import PrimeHub -from pybricks.pupdevices import Motor, ColorSensor, UltrasonicSensor, ForceSensor -from pybricks.parameters import Button, Color, Direction, Port, Side, Stop -from pybricks.robotics import DriveBase -from pybricks.tools import wait, StopWatch -from pybricks import version -from OtherFunctions import vprint -import usys - -print("Pybricks version information:", version) -print("MicroPython information:", usys.implementation) -print("MicroPython version:", usys.version) -class HubDiagnostics: - def __init__(self): - self.hub = PrimeHub() - self.port_map = { - "A": Port.A, - "B": Port.B, - "C": Port.C, - "D": Port.D, - "E": Port.E, - "F": Port.F, - } - def testLightSources(self, verbose): - v = verbose - hub.display.off() - for x in range(5): - for y in range(5): - vprint(f"Turning on pixel at position {x}, {y}...", v) - display.pixel(x, y, brightness=100) - wait(100) - vprint(f"Turning off pixel at position {x}, {y}...", v) - display.pixel(x, y, brightness=0) From 06c7cf6e9f43a0186c035031f36756fd6eb9bad3 Mon Sep 17 00:00:00 2001 From: Atharv Nagavarapu <30nagava@elmbrookstudents.org> Date: Fri, 19 Dec 2025 22:40:38 +0000 Subject: [PATCH 29/45] Delete diagnostics-old/MotorDiagnostics.py --- diagnostics-old/MotorDiagnostics.py | 142 ---------------------------- 1 file changed, 142 deletions(-) delete mode 100644 diagnostics-old/MotorDiagnostics.py diff --git a/diagnostics-old/MotorDiagnostics.py b/diagnostics-old/MotorDiagnostics.py deleted file mode 100644 index 8501f3d..0000000 --- a/diagnostics-old/MotorDiagnostics.py +++ /dev/null @@ -1,142 +0,0 @@ -from pybricks.hubs import PrimeHub -from pybricks.pupdevices import Motor, ColorSensor, UltrasonicSensor, ForceSensor -from pybricks.parameters import Button, Color, Direction, Port, Side, Stop -from pybricks.robotics import DriveBase -from pybricks.tools import wait, StopWatch -import umath - -hub = PrimeHub() -class MotorDiagnostics: - def __init__(self): - self.testmotor = None - self.port_map = { - "A": Port.A, - "B": Port.B, - "C": Port.C, - "D": Port.D, - "E": Port.E, - "F": Port.F, - } - - def stdev(self, vals): - data = vals - if len(data) < 2: - return 0 - # Calculate the mean - mean = sum(data) / len(data) - - # Calculate the variance (sum of squared differences from the mean, divided by n-1 for sample standard deviation) - variance = sum([(x - mean) ** 2 for x in data]) / float(len(data) - 1) - - # Calculate the standard deviation (square root of the variance) - - std_dev_manual = umath.sqrt(variance) - - - return (std_dev_manual) - def health_score(self, desired, avg_speed, stdev_speed, avg_load): - # Speed accuracy: penalize % error - accuracy = max(0, 100 - abs(avg_speed - desired) / desired * 100) - - # Stability: penalize deviation relative to desired - stability = max(0, 100 - (stdev_speed / desired) * 100) - - # Normalize load: map 10 to 20 as baseline (around 0%), 200 as max (around 100%) - baseline = 15 # midpoint of idle range - max_observed = 200 # heavy load/stall - normalized_load = max(0, avg_load - baseline) - load_pct = min(100, (normalized_load / (max_observed - baseline)) * 100) - - load_score = max(0, 100 - load_pct) - - # Final score: average of the three - return (accuracy + stability + load_score) / 3 - - - # Final score: average of the three - return (accuracy + stability + load_score) / 3 - def initializeMotor(self): - valid_ports = {"A", "B", "C", "D", "E", "F"} - while True: - motorinput = input( - "This test will run your motor at 3 speeds: 180, 540, and 1000 degrees per second.\n" - "Please make sure your motor is not under any load (for example, your hand) during the test.\n" - "If you want to test the wheel's load, note that this will affect the load measurements.\n" - "Enter the port for the motor you would like to test (A, B, C, D, E, or F): " - ).strip().upper() - - try: - # Only create a new Motor if we don't already have one - if self.testmotor is None: - self.testmotor = Motor(self.port_map[motorinput]) - print(f"Motor initialized on port {motorinput}.") - else: - print(f"Reusing existing motor on port {motorinput}.") - break - - except OSError as e: - if e.errno == 16: # EBUSY - print(f"Port {motorinput} is already in use. Reusing existing motor.") - # Do not overwrite self.testmotor here — keep the existing reference - break - else: - print(f"Error initializing motor on port {motorinput}: {e}") - print("Make sure a motor is actually connected to this port.") - self.testmotor = None - - def testSpeed(self, speed): - self.testmotor.reset_angle(0) - - motorspeeds = [] - motorloads = [] - target_angle = speed * 3 - print("\n", speed, "DEGREES PER SECOND TEST") - self.testmotor.run_angle(speed, target_angle, Stop.HOLD, False) - stopwatchmotor = StopWatch() - while stopwatchmotor.time() < 3000: - wait(20) - motorspeeds.append(self.testmotor.speed()) - motorloads.append(self.testmotor.load()) - - max_speed, max_accel, max_torque = self.testmotor.control.limits() - - - print("Desired motor speed: ", str(speed)) - if motorspeeds: - avg = sum(motorspeeds) / len(motorspeeds) - print("Average motor speed:", avg) - print("Motor speed deviation:", str(self.stdev(motorspeeds))) - else: - print("No speed samples collected.") - avg = 0 - if motorloads: - avgload = sum(motorloads) / len(motorloads) - - print("Average motor load:", avgload) - print("Motor load deviation:", str(self.stdev(motorloads))) - else: - print("No load samples collected.") - avgload = 0 - score = self.health_score(speed, avg, self.stdev(motorspeeds), avgload) - print("Health score for this test:", str(score) + "%") - return score - def fullTest(self): - self.initializeMotor() - print("Load measurements are in mNm. Speed measurements are in degrees per second.") - max_speed, max_accel, max_torque = self.testmotor.control.limits() - print("Maximum motor speed:", max_speed) - test180 = self.testSpeed(180) - test540 = self.testSpeed(540) - test1000 = self.testSpeed(1000) - print("\n FINAL MOTOR STATISTICS") - final = (test180 + test540 + test1000) / 3 - print("Final motor health score:", str(final) + "%") - if final < 65: - print("Your motor is in need of attention. Make sure to clean it regularly and charge the Prime Hub.") - elif final < 85: - print("Your motor is in OK condition. Make sure to clean it regularly and charge the Prime Hub.") - elif final < 95: - print("Your motor is in great condition!") - else: - print("Your motor is in AMAZING condition!!!") - self.testmotor.stop() \ No newline at end of file From 557b02531690fcae1769cf7fff44148be0c7720a Mon Sep 17 00:00:00 2001 From: Atharv Nagavarapu <30nagava@elmbrookstudents.org> Date: Fri, 19 Dec 2025 22:40:41 +0000 Subject: [PATCH 30/45] Delete diagnostics-old/OtherFunctions.py --- diagnostics-old/OtherFunctions.py | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 diagnostics-old/OtherFunctions.py diff --git a/diagnostics-old/OtherFunctions.py b/diagnostics-old/OtherFunctions.py deleted file mode 100644 index 2acb729..0000000 --- a/diagnostics-old/OtherFunctions.py +++ /dev/null @@ -1,2 +0,0 @@ -def vprint(string, verbose): - print("[LOG (verbose enabled)]", string) \ No newline at end of file From 541006041bf2e213a7c273ec3212438df010dd50 Mon Sep 17 00:00:00 2001 From: Atharv Nagavarapu <30nagava@elmbrookstudents.org> Date: Fri, 19 Dec 2025 22:40:48 +0000 Subject: [PATCH 31/45] Delete diagnostics/BatteryDiagnostics.py --- diagnostics/BatteryDiagnostics.py | 49 ------------------------------- 1 file changed, 49 deletions(-) delete mode 100644 diagnostics/BatteryDiagnostics.py diff --git a/diagnostics/BatteryDiagnostics.py b/diagnostics/BatteryDiagnostics.py deleted file mode 100644 index 682793f..0000000 --- a/diagnostics/BatteryDiagnostics.py +++ /dev/null @@ -1,49 +0,0 @@ -from pybricks.tools import wait -import umath -class BatteryDiagnostics: - def __init__(self, hub): - self.voltage = 0 - self.current = 0 - self.hub = hub - def printVoltage(self): - self.voltage = self.hub.battery.voltage() - if self.voltage > 7800: - print(f"Battery voltage is sufficient: {self.voltage}") - elif self.voltage < 7800 : - print(f"Charging needed: {self.voltage}") - def printCurrent(self): - self.current = self.hub.battery.current() - print("Battery current:", self.current) - def printAll(self): - timeelapsed = 0 - voltageList = [] - currentList = [] - while True: - print("\n\n\n\n\n") - self.printVoltage() - voltageList.append(self.voltage) - self.printCurrent() - currentList.append(self.current) - wait(50) - timeelapsed += 50 - - if(timeelapsed >= 3000): - break - print("--------------FINAL RESULTS OF BATTERY DIAGNOSTICS---------------") - print("Voltage deviation:", self.stdev(voltageList)) - print("Current deviation:", self.stdev(currentList)) - def stdev(self, vals): - DATA = vals - if len(DATA) < 2: - return 0 - # Calculate the mean - MEAN = sum(DATA) / len(DATA) - - # Calculate the variance (sum of squared differences from the mean, divided by n-1 for sample standard deviation) - VARIANCE = sum([(x - MEAN) ** 2 for x in DATA]) / float(len(DATA) - 1) - - # Calculate the standard deviation (square root of the variance) - STD_DEV_MANUAL = umath.sqrt(VARIANCE) - - - return (STD_DEV_MANUAL) \ No newline at end of file From ef015647059034e9357d9bde7a6d6f34f827ffd2 Mon Sep 17 00:00:00 2001 From: Atharv Nagavarapu <30nagava@elmbrookstudents.org> Date: Fri, 19 Dec 2025 22:40:51 +0000 Subject: [PATCH 32/45] Delete diagnostics/ColorSensorDiagnostics.py --- diagnostics/ColorSensorDiagnostics.py | 47 --------------------------- 1 file changed, 47 deletions(-) delete mode 100644 diagnostics/ColorSensorDiagnostics.py diff --git a/diagnostics/ColorSensorDiagnostics.py b/diagnostics/ColorSensorDiagnostics.py deleted file mode 100644 index a3fdf1a..0000000 --- a/diagnostics/ColorSensorDiagnostics.py +++ /dev/null @@ -1,47 +0,0 @@ -from pybricks.parameters import Color, Port, Stop -from pybricks.tools import wait, StopWatch - -class ColorSensorDiagnostics: - def __init__(self, hub, colorsensorclass): - self.colorsensor = None - self.PORT_MAP = { - "A": Port.A, - "B": Port.B, - "C": Port.C, - "D": Port.D, - "E": Port.E, - "F": Port.F, - } - def initializeColorSensor(self): - VALID_PORTS = {"A", "B", "C", "D", "E", "F"} - while True: - colorinput = input( - "This will test your color sensor.\n" - "Enter the port for the color sensor you would like to test (A, B, C, D, E, or F): " - ).strip().upper() - if colorinput not in VALID_PORTS: - print("Invalid port. Please enter A-F.") - continue - try: - if self.colorsensor is None: - self.colorsensor = self.colorsensorclass(self.PORT_MAP[colorinput]) - print(f"Color Sensor initialized on port {colorinput}.") - else: - print(f"Reusing existing color sensor on port {colorinput}.") - break - - except OSError as e: - if e.errno == 16: # EBUSY - print(f"Port {colorinput} is already in use. Reusing existing color sensor.") - break - else: - print(f"Error initializing color sensor on port {colorinput}: {e}") - print("Make sure a color sensor is actually connected to this port.") - self.colorsensor = None - self.colorsensor.detectable_colors(Color.RED, Color.YELLOW, Color.GREEN, Color.BLUE, Color.WHITE, Color.NONE) - def printAll(self): - self.initializeColorSensor() - stopwatch = StopWatch() - while stopwatch.time < 5000: - print("HSV output:", self.colorsensor.hsv) - print("Detected color:", self.colorsensor.color()) \ No newline at end of file From 666247b33ff8dda774cfd6ed3bca5eab0baabf8b Mon Sep 17 00:00:00 2001 From: Atharv Nagavarapu <30nagava@elmbrookstudents.org> Date: Fri, 19 Dec 2025 22:40:55 +0000 Subject: [PATCH 33/45] Delete diagnostics/DriveBaseDiagnostics.py --- diagnostics/DriveBaseDiagnostics.py | 51 ----------------------------- 1 file changed, 51 deletions(-) delete mode 100644 diagnostics/DriveBaseDiagnostics.py diff --git a/diagnostics/DriveBaseDiagnostics.py b/diagnostics/DriveBaseDiagnostics.py deleted file mode 100644 index 64a1e51..0000000 --- a/diagnostics/DriveBaseDiagnostics.py +++ /dev/null @@ -1,51 +0,0 @@ -from pybricks.parameters import Direction, Port, Side, Stop -from pybricks.robotics import DriveBase -from pybricks.tools import wait, StopWatch - -from usys import stdin -from uselect import poll - -class DriveBaseDiagnostics: - def __init__(self, hub, motorclass, dbclass): - self.PORT_MAP = { - "A": Port.A, - "B": Port.B, - "C": Port.C, - "D": Port.D, - "E": Port.E, - "F": Port.F, - } - def initializeDriveBase(): - leftmotorport = input("Enter the left motor port: ") - left_motor = self.motorclass(port_map[leftmotorport], Direction.COUNTERCLOCKWISE) - rightmotorport = input("Enter the right motor port: ") - right_motor = self.motorclass(port_map[rightmotorport],Direction.CLOCKWISE) - # DriveBase configuration - WHEEL_DIAMETER = 68.8 # mm - AXLE_TRACK = 180 # mm - drive_base = self.dbclass(left_motor, right_motor, WHEEL_DIAMETER, AXLE_TRACK) - drive_base.settings(600, 500, 300, 200) - drive_base.use_gyro(True) - def driveRobot(): - # Register the standard input so we can read keyboard presses. - keyboard = poll() - keyboard.register(stdin) - - while True: - # Check if a key has been pressed. - if keyboard.poll(0): - # Read the key and print it. - key = stdin.read(1) - print("You pressed:", key) - if(key == "W"): - print("Insert robot go forward code here") - elif(key == "A"): - print("Insert robot go left code here") - elif(key == "S"): - print("Insert robot go backwards code here") - elif(key == "D"): - print("Insert robot go right code here") - elif(key == "X") - break - else: - print("That key doesn't do anything.") \ No newline at end of file From 694d4af5bdf62c76d03d39f36982126fb9f26677 Mon Sep 17 00:00:00 2001 From: Atharv Nagavarapu <30nagava@elmbrookstudents.org> Date: Fri, 19 Dec 2025 22:40:59 +0000 Subject: [PATCH 34/45] Delete diagnostics/FullDiagnostics.py --- diagnostics/FullDiagnostics.py | 61 ---------------------------------- 1 file changed, 61 deletions(-) delete mode 100644 diagnostics/FullDiagnostics.py diff --git a/diagnostics/FullDiagnostics.py b/diagnostics/FullDiagnostics.py deleted file mode 100644 index 3d7bcfc..0000000 --- a/diagnostics/FullDiagnostics.py +++ /dev/null @@ -1,61 +0,0 @@ -from pybricks.hubs import PrimeHub -from pybricks.pupdevices import Motor, ColorSensor, UltrasonicSensor, ForceSensor -from pybricks.parameters import Button, Color, Direction, Port, Side, Stop -from pybricks.robotics import DriveBase -from pybricks.tools import wait, StopWatch -HUB = PrimeHub() -from BatteryDiagnostics import BatteryDiagnostics -from MotorDiagnostics import MotorDiagnostics -from ColorSensorDiagnostics import ColorSensorDiagnostics -battery = BatteryDiagnostics(HUB) -motor = MotorDiagnostics(HUB, Motor) -colorsensor = ColorSensorDiagnostics(HUB, ColorSensor) -CLEARCONFIRM = input("Clear the console before proceeding? Y/N (default: yes): ") -if(CLEARCONFIRM == "Y" or CLEARCONFIRM == "y" or CLEARCONFIRM == "yes" or CLEARCONFIRM == ""): - print("Clearing console... \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n") -print(""" - ███████████ █████ █████ ██████ █████ █████████ ██████ ██████ █████ █████████ █████████ -▒▒███▒▒▒▒▒███▒▒███ ▒▒███ ▒▒██████ ▒▒███ ███▒▒▒▒▒███ ▒▒██████ ██████ ▒▒███ ███▒▒▒▒▒███ ███▒▒▒▒▒███ - ▒███ ▒███ ▒▒███ ███ ▒███▒███ ▒███ ▒███ ▒███ ▒███▒█████▒███ ▒███ ███ ▒▒▒ ▒███ ▒▒▒ - ▒██████████ ▒▒█████ ▒███▒▒███▒███ ▒███████████ ▒███▒▒███ ▒███ ▒███ ▒███ ▒▒█████████ - ▒███▒▒▒▒▒▒ ▒▒███ ▒███ ▒▒██████ ▒███▒▒▒▒▒███ ▒███ ▒▒▒ ▒███ ▒███ ▒███ ▒▒▒▒▒▒▒▒███ - ▒███ ▒███ ▒███ ▒▒█████ ▒███ ▒███ ▒███ ▒███ ▒███ ▒▒███ ███ ███ ▒███ - █████ █████ █████ ▒▒█████ █████ █████ █████ █████ █████ ▒▒█████████ ▒▒█████████ -▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒▒▒▒▒ ▒▒▒▒▒▒▒▒▒ - -The free and open source diagnostics tool for LEGO® Education SPIKE™ Prime robots, designed for FIRST Lego League. -Developed by Team 65266, Lego Dynamics. - """ - ) -while True: - - print("\nWhich diagnostic do you want to perform?") - print("Enter 'b' for battery diagnostics") - print("Enter 'm' for motor diagnostics") - print("Enter 'cs' for color sensor diagnostics") - print("Enter 'q' to quit") - - choice = input("Your choice: ").strip().lower() - - if choice == "b": - print("-----------------------BATTERY DIAGNOSTICS-----------------------") - print("This test will check the battery voltage and current. It will measure these over a period of 3 seconds and provide average and deviation values. Your voltage should be above 7800 mV for optimal performance.") - input("Press Enter to begin the battery diagnostics.") - battery.printAll() - print("Battery diagnostics completed.") - - elif choice == "m": - print("------------------------MOTOR DIAGNOSTICS------------------------") - motor.fullTest() - print("Motor diagnostics completed.") - - elif choice == "q": - print("Diagnostics completed successfully. Exiting with code 0. Good luck in the robot game!") - break - elif choice == "cs": - print("---------------------COLOR SENSOR DIAGNOSTICS---------------------") - colorsensor.printAll() - print("Color sensor diagnostics completed.") - break - else: - print("Invalid choice. Please enter 'b', 'm', or 'q'.") \ No newline at end of file From 5b29d631ca7ddbdb287bd009c554998ef4f5df67 Mon Sep 17 00:00:00 2001 From: Atharv Nagavarapu <30nagava@elmbrookstudents.org> Date: Fri, 19 Dec 2025 22:41:03 +0000 Subject: [PATCH 35/45] Delete diagnostics/HubDiagnostics.py --- diagnostics/HubDiagnostics.py | 28 ---------------------------- 1 file changed, 28 deletions(-) delete mode 100644 diagnostics/HubDiagnostics.py diff --git a/diagnostics/HubDiagnostics.py b/diagnostics/HubDiagnostics.py deleted file mode 100644 index ee5113f..0000000 --- a/diagnostics/HubDiagnostics.py +++ /dev/null @@ -1,28 +0,0 @@ -from pybricks.tools import wait, StopWatch -from pybricks import version -import OtherFunctions as debug -import usys - -print("Pybricks version information:", version) -print("MicroPython information:", usys.implementation) -print("MicroPython version:", usys.version) -class HubDiagnostics: - def __init__(self, hub): - self.port_map = { - "A": Port.A, - "B": Port.B, - "C": Port.C, - "D": Port.D, - "E": Port.E, - "F": Port.F, - } - def testLightSources(self, verbose): - v = verbose - self.hub.display.off() - for x in range(5): - for y in range(5): - debug.log(f"Turning on pixel at position {x}, {y}...", v) - display.pixel(x, y, brightness=100) - wait(100) - debug.log(f"Turning off pixel at position {x}, {y}...", v) - display.pixel(x, y, brightness=0) From 4621a3ee7526b417cd57e6986a8d96f94686babc Mon Sep 17 00:00:00 2001 From: Atharv Nagavarapu <30nagava@elmbrookstudents.org> Date: Fri, 19 Dec 2025 22:41:07 +0000 Subject: [PATCH 36/45] Delete diagnostics/MotorDiagnostics.py --- diagnostics/MotorDiagnostics.py | 135 -------------------------------- 1 file changed, 135 deletions(-) delete mode 100644 diagnostics/MotorDiagnostics.py diff --git a/diagnostics/MotorDiagnostics.py b/diagnostics/MotorDiagnostics.py deleted file mode 100644 index 8cc879e..0000000 --- a/diagnostics/MotorDiagnostics.py +++ /dev/null @@ -1,135 +0,0 @@ -from pybricks.parameters import Direction, Port, Stop -from pybricks.tools import wait, StopWatch -import umath -class MotorDiagnostics: - def __init__(self, hub, motorclass): - self.testmotor = None - self.port_map = { - "A": Port.A, - "B": Port.B, - "C": Port.C, - "D": Port.D, - "E": Port.E, - "F": Port.F, - } - - def stdev(self, vals): - DATA = vals - if len(DATA) < 2: - return 0 - # Calculate the mean - MEAN = sum(DATA) / len(DATA) - - # Calculate the variance (sum of squared differences from the mean, divided by n-1 for sample standard deviation) - VARIANCE = sum([(x - MEAN) ** 2 for x in DATA]) / float(len(DATA) - 1) - - # Calculate the standard deviation (square root of the variance) - STD_DEV_MANUAL = umath.sqrt(VARIANCE) - - - return (STD_DEV_MANUAL) - def health_score(self, desired, avg_speed, stdev_speed, avg_load): - # Speed accuracy: penalize % error - ACCURACY = max(0, 100 - abs(avg_speed - desired) / desired * 100) - - # Stability: penalize deviation relative to desired - STABILITY = max(0, 100 - (stdev_speed / desired) * 100) - - # Normalize load: map 10 to 20 as baseline (around 0%), 200 as max (around 100%) - BASELINE = 15 # midpoint of idle range - MAX_OBSERVED = 200 # heavy load/stall - NORMALIZED_LOAD = max(0, avg_load - BASELINE) - LOAD_PCT = min(100, (NORMALIZED_LOAD / (MAX_OBSERVED - BASELINE)) * 100) - - LOAD_SCORE = max(0, 100 - LOAD_PCT) - - # Final score: average of the three - return (ACCURACY + STABILITY + LOAD_SCORE) / 3 - - def initializeMotor(self): - VALID_PORTS = {"A", "B", "C", "D", "E", "F"} - while True: - motorinput = input( - "This test will run your motor at 3 speeds: 180, 540, and 1000 degrees per second.\n" - "Please make sure your motor is not under any load (for example, your hand) during the test.\n" - "If you want to test the wheel's load, note that this will affect the load measurements.\n" - "Enter the port for the motor you would like to test (A, B, C, D, E, or F): " - ).strip().upper() - if motorinput not in VALID_PORTS: - print("That is not a valid port. Please enter A-F.") - continue - try: - # Only create a new Motor if we don't already have one - if self.testmotor is None: - self.testmotor = self.motorclass(self.port_map[motorinput]) - print(f"Motor initialized on port {motorinput}.") - else: - print(f"Reusing existing motor on port {motorinput}.") - break - - except OSError as e: - if e.errno == 16: # EBUSY - print(f"Port {motorinput} is already in use. Reusing existing motor.") - # Do not overwrite self.testmotor here — keep the existing reference - break - else: - print(f"Error initializing motor on port {motorinput}: {e}") - print("Make sure a motor is actually connected to this port.") - self.testmotor = None - - def testSpeed(self, speed): - self.testmotor.reset_angle(0) - - motorspeeds = [] - motorloads = [] - TARGET_ANGLE = speed * 3 - print("\n", speed, "DEGREES PER SECOND TEST") - self.testmotor.run_angle(speed, TARGET_ANGLE, Stop.HOLD, False) - stopwatchmotor = StopWatch() - while stopwatchmotor.time() < 3000: - wait(20) - motorspeeds.append(self.testmotor.speed()) - motorloads.append(self.testmotor.load()) - - MAX_SPEED, MAX_ACCEL, MAX_TORQUE = self.testmotor.control.limits() - - - print("Desired motor speed: ", str(speed)) - if motorspeeds: - avg = sum(motorspeeds) / len(motorspeeds) - print("Average motor speed:", avg) - print("Motor speed deviation:", str(self.stdev(motorspeeds))) - else: - print("No speed samples collected.") - avg = 0 - if motorloads: - avgload = sum(motorloads) / len(motorloads) - - print("Average motor load:", avgload) - print("Motor load deviation:", str(self.stdev(motorloads))) - else: - print("No load samples collected.") - avgload = 0 - SCORE = self.health_score(speed, avg, self.stdev(motorspeeds), avgload) - print("Health score for this test:", str(SCORE) + "%") - return SCORE - def fullTest(self): - self.initializeMotor() - print("Load measurements are in mNm. Speed measurements are in degrees per second.") - MAX_SPEED, MAX_ACCEL, MAX_TORQUE = self.testmotor.control.limits() - print("Maximum motor speed:", MAX_SPEED) - test180 = self.testSpeed(180) - test540 = self.testSpeed(540) - test1000 = self.testSpeed(1000) - print("\n FINAL MOTOR STATISTICS") - final = (test180 + test540 + test1000) / 3 - print("Final motor health score:", str(final) + "%") - if final < 65: - print("Your motor is in need of attention. Make sure to clean it regularly and charge the Prime Hub.") - elif final < 85: - print("Your motor is in OK condition. Make sure to clean it regularly and charge the Prime Hub.") - elif final < 95: - print("Your motor is in great condition!") - else: - print("Your motor is in AMAZING condition!!!") - self.testmotor.stop() \ No newline at end of file From abb96d5ac6266c886c95c51b4c25b212f6b042cf Mon Sep 17 00:00:00 2001 From: Atharv Nagavarapu <30nagava@elmbrookstudents.org> Date: Fri, 19 Dec 2025 22:41:10 +0000 Subject: [PATCH 37/45] Delete diagnostics/OtherFunctions.py --- diagnostics/OtherFunctions.py | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 diagnostics/OtherFunctions.py diff --git a/diagnostics/OtherFunctions.py b/diagnostics/OtherFunctions.py deleted file mode 100644 index a7638a9..0000000 --- a/diagnostics/OtherFunctions.py +++ /dev/null @@ -1,4 +0,0 @@ -verbose = True -def log(string): - if(verbose): - print("[LOG (verbose)]", string) From e067ae0f991eb0e44ec25cabc88a07d6bec74187 Mon Sep 17 00:00:00 2001 From: Atharv Nagavarapu <30nagava@elmbrookstudents.org> Date: Fri, 19 Dec 2025 22:41:25 +0000 Subject: [PATCH 38/45] Upload files to "/" --- BatteryDiagnostics.py | 49 +++++++++++ ColorSensorDiagnostics.py | 48 +++++++++++ DriveBaseDiagnostics.py | 165 ++++++++++++++++++++++++++++++++++++++ FullDiagnostics.py | 74 +++++++++++++++++ HubDiagnostics.py | 35 ++++++++ 5 files changed, 371 insertions(+) create mode 100644 BatteryDiagnostics.py create mode 100644 ColorSensorDiagnostics.py create mode 100644 DriveBaseDiagnostics.py create mode 100644 FullDiagnostics.py create mode 100644 HubDiagnostics.py diff --git a/BatteryDiagnostics.py b/BatteryDiagnostics.py new file mode 100644 index 0000000..682793f --- /dev/null +++ b/BatteryDiagnostics.py @@ -0,0 +1,49 @@ +from pybricks.tools import wait +import umath +class BatteryDiagnostics: + def __init__(self, hub): + self.voltage = 0 + self.current = 0 + self.hub = hub + def printVoltage(self): + self.voltage = self.hub.battery.voltage() + if self.voltage > 7800: + print(f"Battery voltage is sufficient: {self.voltage}") + elif self.voltage < 7800 : + print(f"Charging needed: {self.voltage}") + def printCurrent(self): + self.current = self.hub.battery.current() + print("Battery current:", self.current) + def printAll(self): + timeelapsed = 0 + voltageList = [] + currentList = [] + while True: + print("\n\n\n\n\n") + self.printVoltage() + voltageList.append(self.voltage) + self.printCurrent() + currentList.append(self.current) + wait(50) + timeelapsed += 50 + + if(timeelapsed >= 3000): + break + print("--------------FINAL RESULTS OF BATTERY DIAGNOSTICS---------------") + print("Voltage deviation:", self.stdev(voltageList)) + print("Current deviation:", self.stdev(currentList)) + def stdev(self, vals): + DATA = vals + if len(DATA) < 2: + return 0 + # Calculate the mean + MEAN = sum(DATA) / len(DATA) + + # Calculate the variance (sum of squared differences from the mean, divided by n-1 for sample standard deviation) + VARIANCE = sum([(x - MEAN) ** 2 for x in DATA]) / float(len(DATA) - 1) + + # Calculate the standard deviation (square root of the variance) + STD_DEV_MANUAL = umath.sqrt(VARIANCE) + + + return (STD_DEV_MANUAL) \ No newline at end of file diff --git a/ColorSensorDiagnostics.py b/ColorSensorDiagnostics.py new file mode 100644 index 0000000..44fae16 --- /dev/null +++ b/ColorSensorDiagnostics.py @@ -0,0 +1,48 @@ +from pybricks.parameters import Color, Port, Stop +from pybricks.tools import wait, StopWatch + +class ColorSensorDiagnostics: + def __init__(self, hub, colorsensorclass): + self.colorsensor = None + self.colorsensorclass = colorsensorclass + self.PORT_MAP = { + "A": Port.A, + "B": Port.B, + "C": Port.C, + "D": Port.D, + "E": Port.E, + "F": Port.F, + } + def initializeColorSensor(self): + VALID_PORTS = {"A", "B", "C", "D", "E", "F"} + while True: + colorinput = input( + "This will test your color sensor.\n" + "Enter the port for the color sensor you would like to test (A, B, C, D, E, or F): " + ).strip().upper() + if colorinput not in VALID_PORTS: + print("Invalid port. Please enter A-F.") + continue + try: + if self.colorsensor is None: + self.colorsensor = self.colorsensorclass(self.PORT_MAP[colorinput]) + print(f"Color Sensor initialized on port {colorinput}.") + else: + print(f"Reusing existing color sensor on port {colorinput}.") + break + + except OSError as e: + if e.errno == 16: # EBUSY + print(f"Port {colorinput} is already in use. Reusing existing color sensor.") + break + else: + print(f"Error initializing color sensor on port {colorinput}: {e}") + print("Make sure a color sensor is actually connected to this port.") + self.colorsensor = None + self.colorsensor.detectable_colors([Color.RED, Color.YELLOW, Color.GREEN, Color.BLUE, Color.WHITE, Color.NONE]) + def printAll(self): + self.initializeColorSensor() + stopwatch = StopWatch() + while stopwatch.time() < 5000: + print("HSV output:", self.colorsensor.hsv()) + print("Detected color:", self.colorsensor.color()) \ No newline at end of file diff --git a/DriveBaseDiagnostics.py b/DriveBaseDiagnostics.py new file mode 100644 index 0000000..45e6e38 --- /dev/null +++ b/DriveBaseDiagnostics.py @@ -0,0 +1,165 @@ +from pybricks.parameters import Direction, Port, Side, Stop +from pybricks.robotics import DriveBase +from pybricks.tools import wait, StopWatch + +from usys import stdin +from uselect import poll + +class DriveBaseDiagnostics: + def __init__(self, hub, motorclass, dbclass): + self.hub = hub + self.motorclass = motorclass + self.dbclass = dbclass + self.drive_base = None + self.PORT_MAP = { + "A": Port.A, + "B": Port.B, + "C": Port.C, + "D": Port.D, + "E": Port.E, + "F": Port.F, + } + def initializeDriveBase(self): + + print("DriveBase setup:") + print("1 = Load from savefile (paste JSON)") + print("2 = Use defaults") + print("3 = Enter values manually") + + choice = input("Choose an option: ") + + # Default values + WHEEL_DIAMETER = 68.8 + AXLE_TRACK = 180 + DRIVE_SETTINGS = (600, 2000, 300, 2000) + + # Motor ports (None until set) + leftmotorport = Port.A + rightmotorport = Port.B + + # ----------------------------- + # OPTION 1: LOAD SAVEFILE + # ----------------------------- + if choice == "1": + print("Paste JSON:") + raw = input("> ") + + # --- wheel --- + if "\"wheel\"" in raw: + part = raw.split("\"wheel\"")[1] + part = part.split(":")[1] + part = part.split(",")[0] + WHEEL_DIAMETER = float(part) + + # --- axle --- + if "\"axle\"" in raw: + part = raw.split("\"axle\"")[1] + part = part.split(":")[1] + part = part.split(",")[0] + AXLE_TRACK = float(part) + + # --- settings --- + if "\"settings\"" in raw: + part = raw.split("\"settings\"")[1] + part = part.split("[")[1] + part = part.split("]")[0] + nums = part.split(",") + DRIVE_SETTINGS = (int(nums[0]), int(nums[1]), int(nums[2]), int(nums[3])) + + # --- left motor port --- + if "\"left_port\"" in raw: + part = raw.split("\"left_port\"")[1] + part = part.split("\"")[1] # first quoted value + leftmotorport = part + + # --- right motor port --- + if "\"right_port\"" in raw: + part = raw.split("\"right_port\"")[1] + part = part.split("\"")[1] + rightmotorport = part + + + print("Loaded config.") + + # ----------------------------- + # OPTION 3: MANUAL ENTRY + # ----------------------------- + elif choice == "3": + WHEEL_DIAMETER = float(input("Wheel diameter: ")) + AXLE_TRACK = float(input("Axle track: ")) + + print("Enter drive settings:") + s1 = int(input("Straight speed: ")) + s2 = int(input("Straight accel: ")) + s3 = int(input("Turn rate: ")) + s4 = int(input("Turn accel: ")) + DRIVE_SETTINGS = (s1, s2, s3, s4) + + # Ask for motor ports HERE (manual only) + leftmotorport = input("Left motor port: ") + rightmotorport = input("Right motor port: ") + + # ----------------------------- + # OPTION 2: DEFAULTS + # ----------------------------- + else: + pass + + # ----------------------------- + # CREATE MOTORS + # ----------------------------- + left_motor = self.motorclass(leftmotorport, Direction.COUNTERCLOCKWISE) + right_motor = self.motorclass(rightmotorport, Direction.CLOCKWISE) + + # ----------------------------- + # CREATE DRIVEBASE + # ----------------------------- + self.drive_base = self.dbclass(left_motor, right_motor, WHEEL_DIAMETER, AXLE_TRACK) + self.drive_base.settings(*DRIVE_SETTINGS) + self.drive_base.use_gyro(True) + + print("DriveBase initialized.") + return DRIVE_SETTINGS + + + + def printAll(self): + self.driveRobot() + def driveRobot(self): + drivesettings = self.initializeDriveBase() + print(drivesettings) + keyboard = poll() + keyboard.register(stdin) + + last_key_time = StopWatch() + last_key_time.reset() + + while True: + key = None + + # Check for keypress immediately + if keyboard.poll(0): + key = stdin.read(1).upper() + last_key_time.reset() + + # Process key + if key: + if key == "W": + self.drive_base.drive(drivesettings[0], 0) + elif key == "A": + self.drive_base.drive(drivesettings[0], -180) + elif key == "S": + self.drive_base.drive(-drivesettings[0], 0) + elif key == "D": + self.drive_base.drive(drivesettings[0], 180) + elif key == "X": + break + + # Auto-stop if no key for 120 ms + if last_key_time.time() > 120: + self.drive_base.stop() + + # Tiny manual delay to avoid 100% CPU + # (much faster than wait()) + for _ in range(200): + pass diff --git a/FullDiagnostics.py b/FullDiagnostics.py new file mode 100644 index 0000000..1e3f57a --- /dev/null +++ b/FullDiagnostics.py @@ -0,0 +1,74 @@ +from pybricks.hubs import PrimeHub +from pybricks.pupdevices import Motor, ColorSensor, UltrasonicSensor, ForceSensor +from pybricks.parameters import Button, Color, Direction, Port, Side, Stop +from pybricks.robotics import DriveBase +from pybricks.tools import wait, StopWatch +HUB = PrimeHub() +from BatteryDiagnostics import BatteryDiagnostics +from MotorDiagnostics import MotorDiagnostics +from ColorSensorDiagnostics import ColorSensorDiagnostics +from DriveBaseDiagnostics import DriveBaseDiagnostics +from HubDiagnostics import HubDiagnostics +battery = BatteryDiagnostics(HUB) +motor = MotorDiagnostics(HUB, Motor) +colorsensor = ColorSensorDiagnostics(HUB, ColorSensor) +hubdiags = HubDiagnostics(HUB) +drivebase = DriveBaseDiagnostics(HUB, Motor, DriveBase) +CLEARCONFIRM = input("Clear the console before proceeding? Y/N (default: yes): ") +if(CLEARCONFIRM == "Y" or CLEARCONFIRM == "y" or CLEARCONFIRM == "yes" or CLEARCONFIRM == ""): + print("Clearing console... \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n") +print(""" + ███████████ █████ █████ ██████ █████ █████████ ██████ ██████ █████ █████████ █████████ +▒▒███▒▒▒▒▒███▒▒███ ▒▒███ ▒▒██████ ▒▒███ ███▒▒▒▒▒███ ▒▒██████ ██████ ▒▒███ ███▒▒▒▒▒███ ███▒▒▒▒▒███ + ▒███ ▒███ ▒▒███ ███ ▒███▒███ ▒███ ▒███ ▒███ ▒███▒█████▒███ ▒███ ███ ▒▒▒ ▒███ ▒▒▒ + ▒██████████ ▒▒█████ ▒███▒▒███▒███ ▒███████████ ▒███▒▒███ ▒███ ▒███ ▒███ ▒▒█████████ + ▒███▒▒▒▒▒▒ ▒▒███ ▒███ ▒▒██████ ▒███▒▒▒▒▒███ ▒███ ▒▒▒ ▒███ ▒███ ▒███ ▒▒▒▒▒▒▒▒███ + ▒███ ▒███ ▒███ ▒▒█████ ▒███ ▒███ ▒███ ▒███ ▒███ ▒▒███ ███ ███ ▒███ + █████ █████ █████ ▒▒█████ █████ █████ █████ █████ █████ ▒▒█████████ ▒▒█████████ +▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒▒▒▒▒ ▒▒▒▒▒▒▒▒▒ + +The free and open source diagnostics tool for the LEGO® Education SPIKE™ Prime robots, designed for FIRST Lego League. +Developed by Team 65266, Lego Dynamics. +Please set your window size to 90% on small screens for best results with the ASCII art. + """ + ) +while True: + + print("\nWhich diagnostic do you want to perform?") + print("Enter 'b' for battery diagnostics") + print("Enter 'm' for motor diagnostics") + print("Enter 'cs' for color sensor diagnostics") + print("Enter 'h' for hub diagnostics") + print("Enter 'db' for drive base diagnostics") + print("Enter 'q' to quit") + + choice = input("Your choice: ").strip().lower() + + if choice == "b": + print("-----------------------BATTERY DIAGNOSTICS-----------------------") + print("This test will check the battery voltage and current. It will measure these over a period of 3 seconds and provide average and deviation values. Your voltage should be above 7800 mV for optimal performance.") + input("Press Enter to begin the battery diagnostics.") + battery.printAll() + print("Battery diagnostics completed.") + + elif choice == "m": + print("------------------------MOTOR DIAGNOSTICS------------------------") + motor.fullTest() + print("Motor diagnostics completed.") + elif choice == "h": + print("-------------------------HUB DIAGNOSTICS-------------------------") + hubdiags.printAll() + print("Hub diagnostics completed.") + elif choice == "q": + print("Diagnostics completed successfully. Exiting with code 0. Good luck in the robot game!") + break + elif choice == "db": + print("----------------------DRIVEBASE DIAGNOSTICS----------------------") + drivebase.printAll() + elif choice == "cs": + print("---------------------COLOR SENSOR DIAGNOSTICS---------------------") + colorsensor.printAll() + print("Color sensor diagnostics completed.") + + else: + print("Invalid choice. Please enter 'b', 'm', or 'q'.") \ No newline at end of file diff --git a/HubDiagnostics.py b/HubDiagnostics.py new file mode 100644 index 0000000..9bdf436 --- /dev/null +++ b/HubDiagnostics.py @@ -0,0 +1,35 @@ +from pybricks.tools import wait, StopWatch +from pybricks.parameters import Port +from pybricks import version +import OtherFunctions as debug +import usys + + +class HubDiagnostics: + def __init__(self, hub): + self.hub = hub + self.port_map = { + "A": Port.A, + "B": Port.B, + "C": Port.C, + "D": Port.D, + "E": Port.E, + "F": Port.F, + } + def printAbout(self): + print("Pybricks version information:", version) + print("MicroPython information:", usys.implementation) + print("MicroPython version:", usys.version) + def testLightSources(self, verbose): + v = verbose + self.hub.display.off() + for x in range(5): + for y in range(5): + debug.log(f"Turning on pixel at position {x}, {y}...", v) + self.hub.display.pixel(x, y, brightness=100) + wait(100) + debug.log(f"Turning off pixel at position {x}, {y}...", v) + self.hub.display.pixel(x, y, brightness=0) + def printAll(self): + self.printAbout() + self.testLightSources(False) From dd9c0910c18b627808f927d0b3cd4b47f195907d Mon Sep 17 00:00:00 2001 From: Atharv Nagavarapu <30nagava@elmbrookstudents.org> Date: Fri, 19 Dec 2025 22:41:40 +0000 Subject: [PATCH 39/45] Update diagnostics/BatteryDiagnostics.py --- BatteryDiagnostics.py => diagnostics/BatteryDiagnostics.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename BatteryDiagnostics.py => diagnostics/BatteryDiagnostics.py (100%) diff --git a/BatteryDiagnostics.py b/diagnostics/BatteryDiagnostics.py similarity index 100% rename from BatteryDiagnostics.py rename to diagnostics/BatteryDiagnostics.py From 8b2748d503394baa60059f9ab013dfe21ca08e08 Mon Sep 17 00:00:00 2001 From: Atharv Nagavarapu <30nagava@elmbrookstudents.org> Date: Fri, 19 Dec 2025 22:41:50 +0000 Subject: [PATCH 40/45] Update diagnostics/ColorSensorDiagnostics.py --- .../ColorSensorDiagnostics.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename ColorSensorDiagnostics.py => diagnostics/ColorSensorDiagnostics.py (100%) diff --git a/ColorSensorDiagnostics.py b/ diagnostics/ColorSensorDiagnostics.py similarity index 100% rename from ColorSensorDiagnostics.py rename to diagnostics/ColorSensorDiagnostics.py From 9c6b44a31ea3b506172c4b4322635f0846be43f1 Mon Sep 17 00:00:00 2001 From: Atharv Nagavarapu <30nagava@elmbrookstudents.org> Date: Fri, 19 Dec 2025 22:42:43 +0000 Subject: [PATCH 41/45] Update diagnostics/ColorSensorDiagnostics.py --- { diagnostics => diagnostics}/ColorSensorDiagnostics.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename { diagnostics => diagnostics}/ColorSensorDiagnostics.py (100%) diff --git a/ diagnostics/ColorSensorDiagnostics.py b/diagnostics/ColorSensorDiagnostics.py similarity index 100% rename from diagnostics/ColorSensorDiagnostics.py rename to diagnostics/ColorSensorDiagnostics.py From 1812f918370bf2d4abfb6c05c9afa1d9e7cb29eb Mon Sep 17 00:00:00 2001 From: Atharv Nagavarapu <30nagava@elmbrookstudents.org> Date: Fri, 19 Dec 2025 22:43:01 +0000 Subject: [PATCH 42/45] Update diagnostics/DriveBaseDiagnostics.py --- DriveBaseDiagnostics.py => diagnostics/DriveBaseDiagnostics.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename DriveBaseDiagnostics.py => diagnostics/DriveBaseDiagnostics.py (100%) diff --git a/DriveBaseDiagnostics.py b/diagnostics/DriveBaseDiagnostics.py similarity index 100% rename from DriveBaseDiagnostics.py rename to diagnostics/DriveBaseDiagnostics.py From 7095021001bfc7bec698c9cb0f0a8c0c1ed76afb Mon Sep 17 00:00:00 2001 From: Atharv Nagavarapu <30nagava@elmbrookstudents.org> Date: Fri, 19 Dec 2025 22:43:08 +0000 Subject: [PATCH 43/45] Update diagnostics/FullDiagnostics.py --- FullDiagnostics.py => diagnostics/FullDiagnostics.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename FullDiagnostics.py => diagnostics/FullDiagnostics.py (100%) diff --git a/FullDiagnostics.py b/diagnostics/FullDiagnostics.py similarity index 100% rename from FullDiagnostics.py rename to diagnostics/FullDiagnostics.py From a236032857e53daaebe0191072e5c73e3bcfa4f6 Mon Sep 17 00:00:00 2001 From: Atharv Nagavarapu <30nagava@elmbrookstudents.org> Date: Fri, 19 Dec 2025 22:43:14 +0000 Subject: [PATCH 44/45] Update diagnostics/HubDiagnostics.py --- HubDiagnostics.py => diagnostics/HubDiagnostics.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename HubDiagnostics.py => diagnostics/HubDiagnostics.py (100%) diff --git a/HubDiagnostics.py b/diagnostics/HubDiagnostics.py similarity index 100% rename from HubDiagnostics.py rename to diagnostics/HubDiagnostics.py From 78e31482d04610ee4475ab7121798c4c08513dc1 Mon Sep 17 00:00:00 2001 From: Atharv Nagavarapu <30nagava@elmbrookstudents.org> Date: Fri, 19 Dec 2025 22:44:53 +0000 Subject: [PATCH 45/45] Upload files to "diagnostics" --- diagnostics/MotorDiagnostics.py | 136 ++++++++++++++++++++++++++++++++ diagnostics/OtherFunctions.py | 3 + 2 files changed, 139 insertions(+) create mode 100644 diagnostics/MotorDiagnostics.py create mode 100644 diagnostics/OtherFunctions.py diff --git a/diagnostics/MotorDiagnostics.py b/diagnostics/MotorDiagnostics.py new file mode 100644 index 0000000..b8e762b --- /dev/null +++ b/diagnostics/MotorDiagnostics.py @@ -0,0 +1,136 @@ +from pybricks.parameters import Direction, Port, Stop +from pybricks.tools import wait, StopWatch +import umath +class MotorDiagnostics: + def __init__(self, hub, motorclass): + self.testmotor = None + self.motorclass = motorclass + self.port_map = { + "A": Port.A, + "B": Port.B, + "C": Port.C, + "D": Port.D, + "E": Port.E, + "F": Port.F, + } + + def stdev(self, vals): + DATA = vals + if len(DATA) < 2: + return 0 + # Calculate the mean + MEAN = sum(DATA) / len(DATA) + + # Calculate the variance (sum of squared differences from the mean, divided by n-1 for sample standard deviation) + VARIANCE = sum([(x - MEAN) ** 2 for x in DATA]) / float(len(DATA) - 1) + + # Calculate the standard deviation (square root of the variance) + STD_DEV_MANUAL = umath.sqrt(VARIANCE) + + + return (STD_DEV_MANUAL) + def health_score(self, desired, avg_speed, stdev_speed, avg_load): + # Speed accuracy: penalize % error + ACCURACY = max(0, 100 - abs(avg_speed - desired) / desired * 100) + + # Stability: penalize deviation relative to desired + STABILITY = max(0, 100 - (stdev_speed / desired) * 100) + + # Normalize load: map 10 to 20 as baseline (around 0%), 200 as max (around 100%) + BASELINE = 15 # midpoint of idle range + MAX_OBSERVED = 200 # heavy load/stall + NORMALIZED_LOAD = max(0, avg_load - BASELINE) + LOAD_PCT = min(100, (NORMALIZED_LOAD / (MAX_OBSERVED - BASELINE)) * 100) + + LOAD_SCORE = max(0, 100 - LOAD_PCT) + + # Final score: average of the three + return (ACCURACY + STABILITY + LOAD_SCORE) / 3 + + def initializeMotor(self): + VALID_PORTS = {"A", "B", "C", "D", "E", "F"} + while True: + motorinput = input( + "This test will run your motor at 3 speeds: 180, 540, and 1000 degrees per second.\n" + "Please make sure your motor is not under any load (for example, your hand) during the test.\n" + "If you want to test the wheel's load, note that this will affect the load measurements.\n" + "Enter the port for the motor you would like to test (A, B, C, D, E, or F): " + ).strip().upper() + if motorinput not in VALID_PORTS: + print("That is not a valid port. Please enter A-F.") + continue + try: + # Only create a new Motor if we don't already have one + if self.testmotor is None: + self.testmotor = self.motorclass(self.port_map[motorinput]) + print(f"Motor initialized on port {motorinput}.") + else: + print(f"Reusing existing motor on port {motorinput}.") + break + + except OSError as e: + if e.errno == 16: # EBUSY + print(f"Port {motorinput} is already in use. Reusing existing motor.") + # Do not overwrite self.testmotor here — keep the existing reference + break + else: + print(f"Error initializing motor on port {motorinput}: {e}") + print("Make sure a motor is actually connected to this port.") + self.testmotor = None + + def testSpeed(self, speed): + self.testmotor.reset_angle(0) + + motorspeeds = [] + motorloads = [] + TARGET_ANGLE = speed * 3 + print("\n", speed, "DEGREES PER SECOND TEST") + self.testmotor.run_angle(speed, TARGET_ANGLE, Stop.HOLD, False) + stopwatchmotor = StopWatch() + while stopwatchmotor.time() < 3000: + wait(20) + motorspeeds.append(self.testmotor.speed()) + motorloads.append(self.testmotor.load()) + + MAX_SPEED, MAX_ACCEL, MAX_TORQUE = self.testmotor.control.limits() + + + print("Desired motor speed: ", str(speed)) + if motorspeeds: + avg = sum(motorspeeds) / len(motorspeeds) + print("Average motor speed:", avg) + print("Motor speed deviation:", str(self.stdev(motorspeeds))) + else: + print("No speed samples collected.") + avg = 0 + if motorloads: + avgload = sum(motorloads) / len(motorloads) + + print("Average motor load:", avgload) + print("Motor load deviation:", str(self.stdev(motorloads))) + else: + print("No load samples collected.") + avgload = 0 + SCORE = self.health_score(speed, avg, self.stdev(motorspeeds), avgload) + print("Health score for this test:", str(SCORE) + "%") + return SCORE + def fullTest(self): + self.initializeMotor() + print("Load measurements are in mNm. Speed measurements are in degrees per second.") + MAX_SPEED, MAX_ACCEL, MAX_TORQUE = self.testmotor.control.limits() + print("Maximum motor speed:", MAX_SPEED) + test180 = self.testSpeed(180) + test540 = self.testSpeed(540) + test1000 = self.testSpeed(1000) + print("\n FINAL MOTOR STATISTICS") + final = (test180 + test540 + test1000) / 3 + print("Final motor health score:", str(final) + "%") + if final < 65: + print("Your motor is in need of attention. Make sure to clean it regularly and charge the Prime Hub.") + elif final < 85: + print("Your motor is in OK condition. Make sure to clean it regularly and charge the Prime Hub.") + elif final < 95: + print("Your motor is in great condition!") + else: + print("Your motor is in AMAZING condition!!!") + self.testmotor.stop() \ No newline at end of file diff --git a/diagnostics/OtherFunctions.py b/diagnostics/OtherFunctions.py new file mode 100644 index 0000000..e5670f6 --- /dev/null +++ b/diagnostics/OtherFunctions.py @@ -0,0 +1,3 @@ +def log(string, verbose): + if(verbose): + print("[LOG (verbose)]", string)