diff --git a/include/pros/misc.h b/include/pros/misc.h index eacff86d..af9c9d11 100644 --- a/include/pros/misc.h +++ b/include/pros/misc.h @@ -481,6 +481,45 @@ int32_t controller_get_digital(controller_id_e_t id, controller_digital_e_t butt */ int32_t controller_get_digital_new_press(controller_id_e_t id, controller_digital_e_t button); +/** + * Returns a falling-edge case for a controller button press. + * + * This function is not thread-safe. + * Multiple tasks polling a single button may return different results under + * the same circumstances, so only one task should call this function for any + * given button. E.g., Task A calls this function for buttons 1 and 2. + * Task B may call this function for button 3, but should not for buttons + * 1 or 2. A typical use-case for this function is to call inside opcontrol + * to detect new button releases, and not in any other tasks. + * + * This function uses the following values of errno when an error state is + * reached: + * EACCES - Another resource is currently trying to access the controller + * port. + * + * \param button + * The button to read. Must be one of + * DIGITAL_{RIGHT,DOWN,LEFT,UP,A,B,Y,X,R1,R2,L1,L2} + * + * \return 1 if the button on the controller is not pressed and had been + * pressed the last time this function was called, 0 otherwise. + * + * \b Example + * \code + * void opcontrol() { + * pros::Controller master(pros::E_CONTROLLER_MASTER); + * while (true) { + * if (master.get_digital_new_release(pros::E_CONTROLLER_DIGITAL_A)) { + * // Toggle pneumatics or other similar actions + * } + * + * delay(2); + * } + * } + * \endcode + */ +int32_t controller_get_digital_new_release(controller_id_e_t id, controller_digital_e_t button); + /** * Sets text to the controller LCD screen. * diff --git a/include/pros/misc.hpp b/include/pros/misc.hpp index 8df6a827..b0a115e4 100644 --- a/include/pros/misc.hpp +++ b/include/pros/misc.hpp @@ -211,6 +211,45 @@ class Controller { */ std::int32_t get_digital_new_press(controller_digital_e_t button); + /** + * Returns a falling-edge case for a controller button press. + * + * This function is not thread-safe. + * Multiple tasks polling a single button may return different results under + * the same circumstances, so only one task should call this function for any + * given button. E.g., Task A calls this function for buttons 1 and 2. + * Task B may call this function for button 3, but should not for buttons + * 1 or 2. A typical use-case for this function is to call inside opcontrol + * to detect new button releases, and not in any other tasks. + * + * This function uses the following values of errno when an error state is + * reached: + * EACCES - Another resource is currently trying to access the controller + * port. + * + * \param button + * The button to read. Must be one of + * DIGITAL_{RIGHT,DOWN,LEFT,UP,A,B,Y,X,R1,R2,L1,L2} + * + * \return 1 if the button on the controller is not pressed and had been + * pressed the last time this function was called, 0 otherwise. + * + * \b Example + * \code + * void opcontrol() { + * pros::Controller master(pros::E_CONTROLLER_MASTER); + * while (true) { + * if (master.get_digital_new_release(pros::E_CONTROLLER_DIGITAL_A)) { + * // Toggle pneumatics or other similar actions + * } + * + * delay(2); + * } + * } + * \endcode + */ + std::int32_t get_digital_new_release(controller_digital_e_t button); + #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-function" template diff --git a/src/devices/controller.c b/src/devices/controller.c index ad335c24..52278578 100644 --- a/src/devices/controller.c +++ b/src/devices/controller.c @@ -23,8 +23,10 @@ // From enum in misc.h #define NUM_BUTTONS 12 +// button_pressed is used for get_digital_new_press and button_released is used for get_digital_new_release typedef struct controller_data { bool button_pressed[NUM_BUTTONS]; + bool button_released[NUM_BUTTONS]; } controller_data_s_t; bool get_button_pressed(int port, int button) { @@ -36,6 +38,15 @@ void set_button_pressed(int port, int button, bool state) { data->button_pressed[button] = state; } +bool get_button_released(int port, int button) { + return ((controller_data_s_t*)registry_get_device_internal(port)->pad)->button_released[button]; +} + +void set_button_released(int port, int button, bool state) { + controller_data_s_t* data = (controller_data_s_t*)registry_get_device_internal(port)->pad; + data->button_released[button] = state; +} + int32_t controller_is_connected(controller_id_e_t id) { uint8_t port; CONTROLLER_PORT_MUTEX_TAKE(id, port) @@ -98,6 +109,27 @@ int32_t controller_get_digital_new_press(controller_id_e_t id, controller_digita } } +int32_t controller_get_digital_new_release(controller_id_e_t id, controller_digital_e_t button) { + int32_t pressed = controller_get_digital(id, button); + uint8_t port; + CONTROLLER_PORT_MUTEX_TAKE(id, port) + uint8_t button_num = button - E_CONTROLLER_DIGITAL_L1; + + if (pressed) { + set_button_released(port, button_num, false); + } + if (!pressed && !get_button_released(port, button_num)) { + // button is currently not pressed and was detected as being pressed during + // last check + set_button_released(port, button_num, true); + internal_port_mutex_give(port); + return true; + } else { + internal_port_mutex_give(port); + return false; // button is pressed or was already detected + } +} + int32_t controller_set_text(controller_id_e_t id, uint8_t line, uint8_t col, const char* str) { uint8_t port; CONTROLLER_PORT_MUTEX_TAKE(id, port) diff --git a/src/devices/controller.cpp b/src/devices/controller.cpp index 04bce52e..20736a70 100644 --- a/src/devices/controller.cpp +++ b/src/devices/controller.cpp @@ -44,6 +44,10 @@ std::int32_t Controller::get_digital_new_press(pros::controller_digital_e_t butt return controller_get_digital_new_press(_id, button); } +std::int32_t Controller::get_digital_new_release(pros::controller_digital_e_t button) { + return controller_get_digital_new_release(_id, button); +} + std::int32_t Controller::set_text(std::uint8_t line, std::uint8_t col, const char* str) { return controller_set_text(_id, line, col, str); }