Skip to content

Commit

Permalink
Add logic to detect whether the controller has received data since...
Browse files Browse the repository at this point in the history
...BP32.update was last called.

Mitigates ricardoquesada/bluepad32#42
  • Loading branch information
ricardoquesada committed Feb 8, 2024
1 parent c97190d commit a8d661a
Show file tree
Hide file tree
Showing 9 changed files with 118 additions and 70 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [4.0-beta1] - ???
- Bluepad32 v4.0-beta1 upstream
- Arduino API:
- `BP32.update()` returns `true` if data was received.
- `Controller.hasData()` return `true` if data was received since
`BP32.update()` was called.

## [4.0-beta0] - 2024-02-04
- Bluepad32 v4.0-beta0
- BTstack develop branch: 4b3f8617054370b0e96650ee65edea1c23591ed4
Expand Down
2 changes: 1 addition & 1 deletion components/bluepad32/parser/uni_hid_parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ void uni_hid_parse_input_report(struct uni_hid_device_s* d, const uint8_t* repor
rp->init_report(d);

// Certain devices like Nintendo Wii U Pro doesn't support HID descriptor.
// For those kind of devices, just send the raw report.
// For those kinds of devices, send the raw report.
if (rp->parse_input_report) {
rp->parse_input_report(d, report, report_len);
}
Expand Down
20 changes: 17 additions & 3 deletions components/bluepad32_arduino/ArduinoBluepad32.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,31 @@ const char* Bluepad32::firmwareVersion() const {
return "Bluepad32 for Arduino v" UNI_VERSION;
}

void Bluepad32::update() {
bool Bluepad32::update() {
bool data_updated = false;
int connectedControllers = 0;
int status;

for (int i = 0; i < BP32_MAX_GAMEPADS; i++) {
if (arduino_get_controller_data(i, &_controllers[i]._data) == -1)
status = arduino_get_controller_data(i, &_controllers[i]._data);
if (status == UNI_ARDUINO_ERROR_INVALID_DEVICE)
continue;

// If at least one controller has data, we return true.
if (status == UNI_ARDUINO_ERROR_SUCCESS)
data_updated = true;

// Update individual controller.
_controllers[i]._hasData = (status == UNI_ARDUINO_ERROR_SUCCESS);

// Update Idx in case it is the first time to get updated.
_controllers[i]._idx = i;
connectedControllers |= (1 << i);
}

// No changes in connected controllers. No need to call onConnected or onDisconnected.
if (connectedControllers == _prevConnectedControllers)
return;
return data_updated;

logi("connected in total: 0x%02x (flag)\n", connectedControllers);

Expand All @@ -54,6 +66,8 @@ void Bluepad32::update() {
}

_prevConnectedControllers = connectedControllers;

return data_updated;
}

void Bluepad32::forgetBluetoothKeys() {
Expand Down
4 changes: 3 additions & 1 deletion components/bluepad32_arduino/ArduinoController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ const struct Controller::controllerNames Controller::_controllerNames[] = {
{Controller::CONTROLLER_TYPE_GenericController, "Generic"},
{Controller::CONTROLLER_TYPE_NimbusController, "Nimbus"},
{Controller::CONTROLLER_TYPE_OUYAController, "OUYA"},
{Controller::CONTROLLER_TYPE_PSMoveController, "PSMove"},
{Controller::CONTROLLER_TYPE_AtariJoystick, "Atari Joystick"},

{Controller::CONTROLLER_TYPE_GenericKeyboard, "Keyboard"},
{Controller::CONTROLLER_TYPE_GenericMouse, "Mouse"},
Expand Down Expand Up @@ -142,7 +144,7 @@ bool Controller::isModifierPressed(KeyboardKey key) const {
void Controller::onConnected() {
_connected = true;
// Fetch properties, and have them cached.
if (arduino_get_controller_properties(_idx, &_properties) != UNI_ARDUINO_OK) {
if (arduino_get_controller_properties(_idx, &_properties) != UNI_ARDUINO_ERROR_SUCCESS) {
loge("failed to get controller properties");
}
}
Expand Down
63 changes: 39 additions & 24 deletions components/bluepad32_arduino/arduino_platform.c
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ static void arduino_on_controller_data(uni_hid_device_t* d, uni_controller_t* ct
// Populate gamepad data on shared struct.
xSemaphoreTake(_controller_mutex, portMAX_DELAY);
_controllers[ins->controller_idx].data = *ctl;
_controllers[ins->controller_idx].data_updated = true;
xSemaphoreGive(_controller_mutex);
}

Expand All @@ -252,29 +253,43 @@ static const uni_property_t* arduino_get_property(uni_property_idx_t idx) {
// CPU 1 - Application (Arduino) process
//
int arduino_get_gamepad_data(int idx, arduino_gamepad_data_t* out_data) {
int ret;

if (idx < 0 || idx >= CONFIG_BLUEPAD32_MAX_DEVICES)
return UNI_ARDUINO_ERROR;
return UNI_ARDUINO_ERROR_INVALID_DEVICE;
if (_controllers[idx].idx == UNI_ARDUINO_GAMEPAD_INVALID)
return UNI_ARDUINO_ERROR;
return UNI_ARDUINO_ERROR_INVALID_DEVICE;

ret = UNI_ARDUINO_ERROR_NO_DATA;
xSemaphoreTake(_controller_mutex, portMAX_DELAY);
*out_data = _controllers[idx].data.gamepad;
if (_controllers[idx].data_updated) {
*out_data = _controllers[idx].data.gamepad;
_controllers[idx].data_updated = false;
ret = UNI_ARDUINO_ERROR_SUCCESS;
}
xSemaphoreGive(_controller_mutex);

return UNI_ARDUINO_OK;
return ret;
}

int arduino_get_controller_data(int idx, arduino_controller_data_t* out_data) {
int ret;

if (idx < 0 || idx >= CONFIG_BLUEPAD32_MAX_DEVICES)
return UNI_ARDUINO_ERROR;
return UNI_ARDUINO_ERROR_INVALID_DEVICE;
if (_controllers[idx].idx == UNI_ARDUINO_GAMEPAD_INVALID)
return UNI_ARDUINO_ERROR;
return UNI_ARDUINO_ERROR_INVALID_DEVICE;

ret = UNI_ARDUINO_ERROR_NO_DATA;
xSemaphoreTake(_controller_mutex, portMAX_DELAY);
*out_data = _controllers[idx].data;
if (_controllers[idx].data_updated) {
*out_data = _controllers[idx].data;
_controllers[idx].data_updated = false;
ret = UNI_ARDUINO_ERROR_SUCCESS;
}
xSemaphoreGive(_controller_mutex);

return UNI_ARDUINO_OK;
return ret;
}

int arduino_get_gamepad_properties(int idx, arduino_gamepad_properties_t* out_properties) {
Expand All @@ -283,22 +298,22 @@ int arduino_get_gamepad_properties(int idx, arduino_gamepad_properties_t* out_pr

int arduino_get_controller_properties(int idx, arduino_controller_properties_t* out_properties) {
if (idx < 0 || idx >= CONFIG_BLUEPAD32_MAX_DEVICES)
return UNI_ARDUINO_ERROR;
return UNI_ARDUINO_ERROR_INVALID_DEVICE;
if (_controllers[idx].idx == UNI_ARDUINO_GAMEPAD_INVALID)
return UNI_ARDUINO_ERROR;
return UNI_ARDUINO_ERROR_INVALID_DEVICE;

xSemaphoreTake(_controller_mutex, portMAX_DELAY);
*out_properties = _controllers[idx].properties;
xSemaphoreGive(_controller_mutex);

return UNI_ARDUINO_OK;
return UNI_ARDUINO_ERROR_SUCCESS;
}

int arduino_set_player_leds(int idx, uint8_t leds) {
if (idx < 0 || idx >= CONFIG_BLUEPAD32_MAX_DEVICES)
return UNI_ARDUINO_ERROR;
return UNI_ARDUINO_ERROR_INVALID_DEVICE;
if (_controllers[idx].idx == UNI_ARDUINO_GAMEPAD_INVALID)
return UNI_ARDUINO_ERROR;
return UNI_ARDUINO_ERROR_INVALID_DEVICE;

pending_request_t request = (pending_request_t){
.controller_idx = idx,
Expand All @@ -307,14 +322,14 @@ int arduino_set_player_leds(int idx, uint8_t leds) {
};
xQueueSendToBack(_pending_queue, &request, (TickType_t)0);

return UNI_ARDUINO_OK;
return UNI_ARDUINO_ERROR_SUCCESS;
}

int arduino_set_lightbar_color(int idx, uint8_t r, uint8_t g, uint8_t b) {
if (idx < 0 || idx >= CONFIG_BLUEPAD32_MAX_DEVICES)
return UNI_ARDUINO_ERROR;
return UNI_ARDUINO_ERROR_INVALID_DEVICE;
if (_controllers[idx].idx == UNI_ARDUINO_GAMEPAD_INVALID)
return UNI_ARDUINO_ERROR;
return UNI_ARDUINO_ERROR_INVALID_DEVICE;

pending_request_t request = (pending_request_t){
.controller_idx = idx,
Expand All @@ -325,14 +340,14 @@ int arduino_set_lightbar_color(int idx, uint8_t r, uint8_t g, uint8_t b) {
};
xQueueSendToBack(_pending_queue, &request, (TickType_t)0);

return UNI_ARDUINO_OK;
return UNI_ARDUINO_ERROR_SUCCESS;
}

int arduino_set_rumble(int idx, uint8_t force, uint8_t duration) {
if (idx < 0 || idx >= CONFIG_BLUEPAD32_MAX_DEVICES)
return UNI_ARDUINO_ERROR;
return UNI_ARDUINO_ERROR_INVALID_DEVICE;
if (_controllers[idx].idx == UNI_ARDUINO_GAMEPAD_INVALID)
return UNI_ARDUINO_ERROR;
return UNI_ARDUINO_ERROR_INVALID_DEVICE;

pending_request_t request = (pending_request_t){
.controller_idx = idx,
Expand All @@ -342,27 +357,27 @@ int arduino_set_rumble(int idx, uint8_t force, uint8_t duration) {
};
xQueueSendToBack(_pending_queue, &request, (TickType_t)0);

return UNI_ARDUINO_OK;
return UNI_ARDUINO_ERROR_SUCCESS;
}

int arduino_disconnect_controller(int idx) {
if (idx < 0 || idx >= CONFIG_BLUEPAD32_MAX_DEVICES)
return UNI_ARDUINO_ERROR;
return UNI_ARDUINO_ERROR_INVALID_DEVICE;
if (_controllers[idx].idx == UNI_ARDUINO_GAMEPAD_INVALID)
return UNI_ARDUINO_ERROR;
return UNI_ARDUINO_ERROR_INVALID_DEVICE;

pending_request_t request = (pending_request_t){
.controller_idx = idx,
.cmd = PENDING_REQUEST_CMD_DISCONNECT,
};
xQueueSendToBack(_pending_queue, &request, (TickType_t)0);

return UNI_ARDUINO_OK;
return UNI_ARDUINO_ERROR_SUCCESS;
}

int arduino_forget_bluetooth_keys(void) {
uni_bt_del_keys_safe();
return UNI_ARDUINO_OK;
return UNI_ARDUINO_ERROR_SUCCESS;
}

static void version(void) {
Expand Down
6 changes: 4 additions & 2 deletions components/bluepad32_arduino/include/ArduinoBluepad32.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,10 @@ class Bluepad32 {
const char* firmwareVersion() const;
void setDebug(uint8_t on);

// Controller
void update();
// Request to update the controllers' data.
// Returns true if data was updated.
// False otherwise.
bool update();

// When a controller is paired to the ESP32, the ESP32 stores keys to enable reconnection.
// If you want to "forget" (delete) the keys from ESP32, you should call this
Expand Down
8 changes: 7 additions & 1 deletion components/bluepad32_arduino/include/ArduinoController.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ class Controller {
CONTROLLER_TYPE_GenericController = 53, // (Bluepad32)
CONTROLLER_TYPE_NimbusController = 54, // (Bluepad32)
CONTROLLER_TYPE_OUYAController = 55, // (Bluepad32)
CONTROLLER_TYPE_PSMoveController = 56, // (Bluepad32)
CONTROLLER_TYPE_AtariJoystick = 57, // (Bluepad32)

CONTROLLER_TYPE_LastController, // Don't add game controllers below this
// enumeration - this enumeration can
Expand Down Expand Up @@ -113,7 +115,7 @@ class Controller {
return 0;
}

// To test one button at the time.
// To test one button at a time.
bool a() const { return buttons() & BUTTON_A; }
bool b() const { return buttons() & BUTTON_B; }
bool x() const { return buttons() & BUTTON_X; }
Expand Down Expand Up @@ -165,6 +167,9 @@ class Controller {
// 255 = Battery full
uint8_t battery() const { return _data.battery; }

// Returns whether the controller has received data since the last time BP32.updated() was called.
bool hasData() const { return _hasData; }

bool isGamepad() const { return _data.klass == UNI_CONTROLLER_CLASS_GAMEPAD; }
bool isMouse() const { return _data.klass == UNI_CONTROLLER_CLASS_MOUSE; }
bool isBalanceBoard() const { return _data.klass == UNI_CONTROLLER_CLASS_BALANCE_BOARD; }
Expand Down Expand Up @@ -195,6 +200,7 @@ class Controller {
int8_t _idx;
ControllerData _data;
ControllerProperties _properties;
bool _hasData;

// Delete copy constructor to avoid copying the state by mistake. If so,
// chances are that the controller won't get updated automatically.
Expand Down
6 changes: 4 additions & 2 deletions components/bluepad32_arduino/include/arduino_platform.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,9 @@ extern "C" {
#include "uni_common.h"

enum {
UNI_ARDUINO_OK = 0,
UNI_ARDUINO_ERROR = -1,
UNI_ARDUINO_ERROR_SUCCESS = 0,
UNI_ARDUINO_ERROR_INVALID_DEVICE = -1,
UNI_ARDUINO_ERROR_NO_DATA = -2,
};

enum {
Expand Down Expand Up @@ -60,6 +61,7 @@ typedef arduino_controller_properties_t arduino_gamepad_properties_t;
typedef struct {
int8_t idx; // Gamepad index
arduino_controller_data_t data;
bool data_updated;

// TODO: To reduce RAM, the properties should be calculated at "request time", and
// not store them "forever".
Expand Down
Loading

0 comments on commit a8d661a

Please sign in to comment.