diff --git a/XRPExamples/gamepad_example.blocks b/XRPExamples/gamepad_example.blocks new file mode 100644 index 0000000..d5e3795 --- /dev/null +++ b/XRPExamples/gamepad_example.blocks @@ -0,0 +1,15 @@ +from XRPLib.gamepad import * +from XRPLib.differential_drive import DifferentialDrive + +gp = Gamepad.get_default_gamepad() + +differentialDrive = DifferentialDrive.get_default_differential_drive() + + +while not (gp.is_button_pressed(gp.BACK)): + differentialDrive.arcade((gp.get_value(gp.Y1)), (gp.get_value(gp.X1))) + + + +## [2025-07-13 01:36:24] +##XRPBLOCKS {"blocks":{"languageVersion":0,"blocks":[{"type":"controls_whileUntil","id":"#/DDnbE2JxVsLQo`C`e^","x":-363,"y":16,"fields":{"MODE":"UNTIL"},"inputs":{"BOOL":{"block":{"type":"xrp_gp_button_pressed","id":"DtLypJc;SU2xT4A~S3nV","fields":{"GPBUTTON":"BACK"}}},"DO":{"block":{"type":"xrp_arcade","id":"(|]Iln#JYa9hzgEz+(4g","inputs":{"STRAIGHT":{"shadow":{"type":"math_number","id":"FOj1uvC$:~x/+cX}d(:|","fields":{"NUM":0.8}},"block":{"type":"xrp_gp_get_value","id":"s#GS_Dt)B8Cb9+4ctpwk","fields":{"GPVALUE":"Y1"}}},"TURN":{"shadow":{"type":"math_number","id":"n1jAKrjST!+3#bfChh$d","fields":{"NUM":0.2}},"block":{"type":"xrp_gp_get_value","id":"*]`U9XHyYnA?Gp%U;$NF","fields":{"GPVALUE":"X1"}}}}}}}}]}} \ No newline at end of file diff --git a/XRPLib/gamepad.py b/XRPLib/gamepad.py new file mode 100644 index 0000000..9a6e661 --- /dev/null +++ b/XRPLib/gamepad.py @@ -0,0 +1,97 @@ +from ble.blerepl import uart +import sys +from micropython import const + +class Gamepad: + + _DEFAULT_GAMEPAD_INSTANCE = None + + X1 = const(0) + Y1 = const(1) + X2 = const(2) + Y2 = const(3) + BUTTON_A = const(4) + BUTTON_B = const(5) + BUTTON_X = const(6) + BUTTON_Y = const(7) + BUMPER_L = const(8) + BUMPER_R = const(9) + TRIGGER_L = const(10) + TRIGGER_R = const(11) + BACK = const(12) + START = const(13) + DPAD_UP = const(14) + DPAD_DN = const(15) + DPAD_L = const(16) + DPAD_R = const(17) + + _joyData = [ + 0.0, + 0.0, + 0.0, + 0.0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0] + + @classmethod + def get_default_gamepad(cls): + """ + Get the default XRP bluetooth joystick instance. This is a singleton, so only one instance of the gamepad sensor will ever exist. + """ + if cls._DEFAULT_GAMEPAD_INSTANCE is None: + cls._DEFAULT_GAMEPAD_INSTANCE = cls() + cls._DEFAULT_GAMEPAD_INSTANCE.start() + return cls._DEFAULT_GAMEPAD_INSTANCE + + def __init__(self): + """ + Manages communication with gamepad data coming from a remote computer via bluetooth + + """ + def start(self): + """ + Signals the remote computer to begin sending gamepad data packets. + """ + for i in range(len(self._joyData)): + self._joyData[i] = 0.0 + uart.set_data_callback(self._data_callback) + sys.stdout.write(chr(27)) + sys.stdout.write(chr(101)) + + + def stop(self): + """ + Signals the remote computer to stop sending gamepad data packets. + """ + sys.stdout.write(chr(27)) + sys.stdout.write(chr(102)) + + def get_value(self, index:int) -> float: + """ + Get the current value of a joystick axis + + :param index: The joystick axis index + Gamepad.X1, Gamepad.Y1, Gamepad.X2, Gamepad.Y2 + :type int + :returns: The value of the joystick between -1 and 1 + :rtype: float + """ + return -self._joyData[index] #returning the negative to make normal for user + + def is_button_pressed(self, index:int) -> bool: + """ + Checks if a specific button is currently pressed. + + :param index: The button index + Gamepad.BUTTON_A, Gamepad.TRIGGER_L, Gamepad.DPAD_UP, etc + :type int + :returns: The value of the button 1 or 0 + :rtype: bool + """ + return self._joyData[index] > 0 + + def _data_callback(self, data): + if(data[0] == 0x55 and len(data) == data[1] + 2): + for i in range(2, data[1] + 2, 2): + self._joyData[data[i]] = round(data[i + 1]/127.5 - 1, 2) + + diff --git a/XRPLib/resetbot.py b/XRPLib/resetbot.py index de80714..7c9d321 100644 --- a/XRPLib/resetbot.py +++ b/XRPLib/resetbot.py @@ -33,12 +33,21 @@ def reset_webserver(): # Shut off the webserver and close network connections Webserver.get_default_webserver().stop_server() +def reset_gamepad(): + from XRPLib.gamepad import Gamepad + # Stop the browser from sending more gamepad data + Gamepad.get_default_gamepad().stop() + def reset_hard(): + reset_gamepad() reset_motors() reset_led() reset_servos() reset_webserver() +if "XRPLib.gamepad" in sys.modules: + reset_gamepad() + if "XRPLib.encoded_motor" in sys.modules: reset_motors() @@ -50,3 +59,4 @@ def reset_hard(): if "XRPLib.webserver" in sys.modules: reset_webserver() + diff --git a/package.json b/package.json index 02bf4e9..fb9c107 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ ["XRPLib/differential_drive.py", "github:Open-STEM/XRP_Micropython/XRPLib/differential_drive.py"], ["XRPLib/encoded_motor.py", "github:Open-STEM/XRP_Micropython/XRPLib/encoded_motor.py"], ["XRPLib/encoder.py", "github:Open-STEM/XRP_Micropython/XRPLib/encoder.py"], + ["XRPLib/gamepad.py", "github:Open-STEM/XRP_Micropython/XRPLib/gamepad.py"], ["XRPLib/imu_defs.py", "github:Open-STEM/XRP_Micropython/XRPLib/imu_defs.py"], ["XRPLib/imu.py", "github:Open-STEM/XRP_Micropython/XRPLib/imu.py"], ["XRPLib/motor_group.py", "github:Open-STEM/XRP_Micropython/XRPLib/motor_group.py"], @@ -20,6 +21,7 @@ ["XRPLib/webserver.py", "github:Open-STEM/XRP_Micropython/XRPLib/webserver.py"], ["XRPExamples/__init__.py", "github:Open-STEM/XRP_Micropython/XRPExamples/__init__.py"], ["XRPExamples/drive_examples.py", "github:Open-STEM/XRP_Micropython/XRPExamples/drive_examples.py"], + ["XRPExamples/gamepad_example.blocks", "github:Open-STEM/XRP_Micropython/XRPExamples/gamepad_example.blocks"], ["XRPExamples/installation_verification.py", "github:Open-STEM/XRP_Micropython/XRPExamples/installation_verification.py"], ["XRPExamples/led_example.py", "github:Open-STEM/XRP_Micropython/XRPExamples/led_example.py"], ["XRPExamples/sensor_examples.py", "github:Open-STEM/XRP_Micropython/XRPExamples/sensor_examples.py"], @@ -28,5 +30,5 @@ "deps": [ ["github:pimoroni/phew", "latest"] ], - "version": "2.0.1" + "version": "2.1.3" }