diff --git a/Firmware/ESP32/main/Bluepad32/Bluepad32.cpp b/Firmware/ESP32/main/Bluepad32/Bluepad32.cpp index 96997cd..2704063 100644 --- a/Firmware/ESP32/main/Bluepad32/Bluepad32.cpp +++ b/Firmware/ESP32/main/Bluepad32/Bluepad32.cpp @@ -27,8 +27,8 @@ static constexpr uint32_t LED_TIME_MS = 500; struct Device { std::atomic connected{false}; - std::atomic report_in; - std::atomic report_out; + std::atomic report_in{ReportIn()}; + std::atomic report_out{ReportOut()}; }; std::array devices_; @@ -46,9 +46,13 @@ static inline void send_feedback_cb(btstack_timer_source *ts) } ReportOut report_out = devices_[i].report_out.load(); + if (!report_out.rumble_l && !report_out.rumble_r) + { + continue; + } bp_device->report_parser.play_dual_rumble(bp_device, 0, FEEDBACK_TIME_MS, report_out.rumble_l, report_out.rumble_r); } - // btstack_run_loop_remove_timer(ts); + btstack_run_loop_set_timer(ts, FEEDBACK_TIME_MS); btstack_run_loop_add_timer(ts); } @@ -69,7 +73,7 @@ static inline void check_led_cb(btstack_timer_source *ts) board_api::set_led(i, devices_[i].connected.load() ? 1 : (led_state ? 1 : 0)); } } - // btstack_run_loop_remove_timer(ts); + btstack_run_loop_set_timer(ts, LED_TIME_MS); btstack_run_loop_add_timer(ts); } @@ -85,15 +89,15 @@ static void init_complete_cb(void) { uni_bt_enable_new_connections_unsafe(true); - // Based on runtime condition, you can delete or list the stored BT keys. - if (1) - { - uni_bt_del_keys_unsafe(); - } - else - { - uni_bt_list_keys_unsafe(); - } + // // Based on runtime condition, you can delete or list the stored BT keys. + // if (1) + // { + // uni_bt_del_keys_unsafe(); + // } + // else + // { + // uni_bt_list_keys_unsafe(); + // } uni_property_dump_all(); } @@ -132,8 +136,8 @@ static void device_disconnected_cb(uni_hid_device_t* device) { return; } - - ReportIn report_in; + + ReportIn report_in = ReportIn(); report_in.index = static_cast(idx); devices_[idx].report_in.store(report_in); devices_[idx].connected.store(false); @@ -252,10 +256,6 @@ uni_platform* get_driver() ReportIn get_report_in(uint8_t index) { - // if (index >= CONFIG_BLUEPAD32_MAX_DEVICES) - // { - // return ReportIn(); - // } return devices_[index].report_in.load(); } @@ -270,6 +270,14 @@ void set_report_out(const ReportOut& report_out) void run_task() { + for (uint8_t i = 0; i < CONFIG_BLUEPAD32_MAX_DEVICES; ++i) + { + ReportIn report_in; + report_in.index = i; + devices_[i].report_in.store(report_in); + devices_[i].report_out.store(ReportOut()); + } + board_api::init_pins(); btstack_init(); @@ -312,333 +320,4 @@ bool connected(uint8_t index) return devices_[index].connected.load(); } -} // namespace bluepad32 - -// #include -// #include -// #include -// #include -// #include -// #include -// #include -// #include -// #include - -// #include "btstack_port_esp32.h" -// #include "btstack_run_loop.h" -// #include "btstack_stdio_esp32.h" -// #include "uni.h" - -// #include "sdkconfig.h" -// #include "Board/board_api.h" -// #include "I2CDriver/I2CDriver.h" -// #include "Bluepad32/Gamepad.h" -// #include "Bluepad32/Bluepad32.h" - -// namespace bluepad32 { - -// static constexpr uint32_t FEEDBACK_TIME_MS = 200; -// static constexpr uint32_t LED_TIME_MS = 500; - -// struct Device -// { -// std::atomic connected{false}; -// std::atomic report_in; -// std::atomic new_report_in{false}; -// std::atomic report_out; -// }; - -// std::array devices_; - -// static inline void send_feedback_cb(btstack_timer_source *ts) -// { -// uni_hid_device_t* bp_device; - -// for (uint8_t i = 0; i < CONFIG_BLUEPAD32_MAX_DEVICES; ++i) -// { -// bp_device = uni_hid_device_get_instance_for_idx(i); -// if (!bp_device || !bp_device->report_parser.play_dual_rumble) -// { -// continue; -// } - -// ReportOut report_out = devices_[i].report_out.load(); -// bp_device->report_parser.play_dual_rumble(bp_device, 0, FEEDBACK_TIME_MS, report_out.rumble_l, report_out.rumble_r); -// } - -// btstack_run_loop_remove_timer(ts); -// btstack_run_loop_set_timer(ts, FEEDBACK_TIME_MS); -// btstack_run_loop_add_timer(ts); -// } - -// static inline void check_led_cb(btstack_timer_source *ts) -// { -// static bool led_state = false; -// led_state = !led_state; - -// if constexpr (board_api::NUM_LEDS == 1) -// { -// board_api::set_led(any_connected() ? 1 : (led_state ? 1 : 0)); -// } -// else -// { -// for (uint8_t i = 0; i < board_api::NUM_LEDS; ++i) -// { -// board_api::set_led(i, devices_[i].connected.load() ? 1 : (led_state ? 1 : 0)); -// } -// } - -// btstack_run_loop_remove_timer(ts); -// btstack_run_loop_set_timer(ts, LED_TIME_MS); -// btstack_run_loop_add_timer(ts); -// } - -// //BT Driver - -// static void init(int argc, const char** arg_V) -// { - -// } - -// static void init_complete_cb(void) -// { -// uni_bt_enable_new_connections_unsafe(true); - -// // Based on runtime condition, you can delete or list the stored BT keys. -// if (1) -// { -// uni_bt_del_keys_unsafe(); -// } -// else -// { -// uni_bt_list_keys_unsafe(); -// } - -// uni_property_dump_all(); -// } - -// static uni_error_t device_discovered_cb(bd_addr_t addr, const char* name, uint16_t cod, uint8_t rssi) -// { -// if (!((cod & UNI_BT_COD_MINOR_MASK) & UNI_BT_COD_MINOR_GAMEPAD)) -// { -// return UNI_ERROR_IGNORE_DEVICE; -// } -// return UNI_ERROR_SUCCESS; -// } - -// static void device_connected_cb(uni_hid_device_t* device) -// { -// #ifdef CONFIG_BLUEPAD32_USB_CONSOLE_ENABLE -// logd("BP32", "Device connected, addr: %p, index: %i\n", device, uni_hid_device_get_idx_for_instance(device)); -// #endif - -// int idx = uni_hid_device_get_idx_for_instance(device); -// if (idx >= CONFIG_BLUEPAD32_MAX_DEVICES || idx < 0) -// { -// return; -// } -// devices_[idx].connected.store(true); -// } - -// static void device_disconnected_cb(uni_hid_device_t* device) -// { -// #ifdef CONFIG_BLUEPAD32_USB_CONSOLE_ENABLE -// logd("BP32", "Device disconnected, addr: %p, index: %i\n", device, uni_hid_device_get_idx_for_instance(device)); -// #endif - -// int idx = uni_hid_device_get_idx_for_instance(device); -// if (idx >= CONFIG_BLUEPAD32_MAX_DEVICES || idx < 0) -// { -// return; -// } - -// ReportIn report_in = ReportIn(); -// report_in.index = static_cast(idx); -// devices_[idx].report_in.store(report_in); -// devices_[idx].connected.store(false); -// } - -// static uni_error_t device_ready_cb(uni_hid_device_t* device) -// { -// return UNI_ERROR_SUCCESS; -// } - -// static void oob_event_cb(uni_platform_oob_event_t event, void* data) -// { -// return; -// } - -// static void controller_data_cb(uni_hid_device_t* device, uni_controller_t* controller) -// { -// static uni_gamepad_t prev_uni_gp[CONFIG_BLUEPAD32_MAX_DEVICES] = {}; - -// if (!device || !controller || controller->klass != UNI_CONTROLLER_CLASS_GAMEPAD) -// { -// return; -// } - -// uni_gamepad_t *uni_gp = &controller->gamepad; -// int idx = uni_hid_device_get_idx_for_instance(device); - -// if (idx >= CONFIG_BLUEPAD32_MAX_DEVICES || idx < 0 || std::memcmp(uni_gp, &prev_uni_gp[idx], sizeof(uni_gamepad_t)) == 0) -// { -// return; -// } - -// #ifdef CONFIG_BLUEPAD32_USB_CONSOLE_ENABLE -// logi("BP32 Stack", "Remaining stack: %d\n", uxTaskGetStackHighWaterMark(NULL)); -// #endif - -// ReportIn report_in; -// report_in.index = static_cast(idx); - -// switch (uni_gp->dpad) -// { -// case DPAD_UP: -// report_in.dpad = Gamepad::DPad::UP; -// break; -// case DPAD_DOWN: -// report_in.dpad = Gamepad::DPad::DOWN; -// break; -// case DPAD_LEFT: -// report_in.dpad = Gamepad::DPad::LEFT; -// break; -// case DPAD_RIGHT: -// report_in.dpad = Gamepad::DPad::RIGHT; -// break; -// case DPAD_UP | DPAD_RIGHT: -// report_in.dpad = Gamepad::DPad::UP_RIGHT; -// break; -// case DPAD_DOWN | DPAD_RIGHT: -// report_in.dpad = Gamepad::DPad::DOWN_RIGHT; -// break; -// case DPAD_DOWN | DPAD_LEFT: -// report_in.dpad = Gamepad::DPad::DOWN_LEFT; -// break; -// case DPAD_UP | DPAD_LEFT: -// report_in.dpad = Gamepad::DPad::UP_LEFT; -// break; -// default: -// break; -// } - -// if (uni_gp->buttons & BUTTON_A) report_in.buttons |= Gamepad::Button::A; -// if (uni_gp->buttons & BUTTON_B) report_in.buttons |= Gamepad::Button::B; -// if (uni_gp->buttons & BUTTON_X) report_in.buttons |= Gamepad::Button::X; -// if (uni_gp->buttons & BUTTON_Y) report_in.buttons |= Gamepad::Button::Y; -// if (uni_gp->buttons & BUTTON_SHOULDER_L) report_in.buttons |= Gamepad::Button::LB; -// if (uni_gp->buttons & BUTTON_SHOULDER_R) report_in.buttons |= Gamepad::Button::RB; -// if (uni_gp->buttons & BUTTON_THUMB_L) report_in.buttons |= Gamepad::Button::L3; -// if (uni_gp->buttons & BUTTON_THUMB_R) report_in.buttons |= Gamepad::Button::R3; -// if (uni_gp->misc_buttons & MISC_BUTTON_BACK) report_in.buttons |= Gamepad::Button::BACK; -// if (uni_gp->misc_buttons & MISC_BUTTON_START) report_in.buttons |= Gamepad::Button::START; -// if (uni_gp->misc_buttons & MISC_BUTTON_SYSTEM) report_in.buttons |= Gamepad::Button::SYS; -// if (uni_gp->misc_buttons & MISC_BUTTON_CAPTURE) report_in.buttons |= Gamepad::Button::MISC; - -// report_in.trigger_l = Gamepad::scale_trigger(uni_gp->brake); -// report_in.trigger_r = Gamepad::scale_trigger(uni_gp->throttle); - -// report_in.joystick_lx = static_cast(uni_gp->axis_x); -// report_in.joystick_ly = static_cast(uni_gp->axis_y); -// report_in.joystick_rx = static_cast(uni_gp->axis_rx); -// report_in.joystick_ry = static_cast(uni_gp->axis_ry); - -// devices_[idx].report_in.store(report_in); -// devices_[idx].new_report_in.store(true); - -// std::memcpy(uni_gp, &prev_uni_gp[idx], sizeof(uni_gamepad_t)); -// } - -// const uni_property_t* get_property_cb(uni_property_idx_t idx) -// { -// return nullptr; -// } - -// uni_platform* get_driver() -// { -// static uni_platform driver = -// { -// .name = "OGXMiniW", -// .init = init, -// .on_init_complete = init_complete_cb, -// .on_device_discovered = device_discovered_cb, -// .on_device_connected = device_connected_cb, -// .on_device_disconnected = device_disconnected_cb, -// .on_device_ready = device_ready_cb, -// .on_controller_data = controller_data_cb, -// .get_property = get_property_cb, -// .on_oob_event = oob_event_cb, -// }; -// return &driver; -// } - -// //Public - -// void run_task() -// { -// board_api::init_pins(); - -// btstack_init(); - -// uni_platform_set_custom(get_driver()); -// uni_init(0, nullptr); - -// btstack_timer_source_t feedback_timer; -// feedback_timer.process = send_feedback_cb; -// feedback_timer.context = nullptr; - -// btstack_run_loop_set_timer(&feedback_timer, FEEDBACK_TIME_MS); -// btstack_run_loop_add_timer(&feedback_timer); - -// btstack_timer_source_t led_timer; -// led_timer.process = check_led_cb; -// led_timer.context = nullptr; - -// btstack_run_loop_set_timer(&led_timer, LED_TIME_MS); -// btstack_run_loop_add_timer(&led_timer); - -// btstack_run_loop_execute(); -// } - -// //Thread safe -// bool any_connected() -// { -// for (auto& device : devices_) -// { -// if (device.connected.load()) -// { -// return true; -// } -// } -// return false; -// } - -// bool connected(uint8_t index) -// { -// return devices_[index].connected.load(); -// } - -// bool get_report_in(uint8_t index, ReportIn& report) -// { -// if (index >= CONFIG_BLUEPAD32_MAX_DEVICES) -// { -// return false; -// } -// if (devices_[index].new_report_in.exchange(false)) -// { -// report = devices_[index].report_in.load(); -// return true; -// } -// return false; -// } - -// void set_report_out(const ReportOut& report) -// { -// if (report.index >= CONFIG_BLUEPAD32_MAX_DEVICES) -// { -// return; -// } -// devices_[report.index].report_out.store(report); -// } - -// } // namespace bluepad32 \ No newline at end of file +} // namespace bluepad32 \ No newline at end of file diff --git a/Firmware/ESP32/main/I2CDriver/I2CDriver.cpp b/Firmware/ESP32/main/I2CDriver/I2CDriver.cpp index e0c52f6..af35fd9 100644 --- a/Firmware/ESP32/main/I2CDriver/I2CDriver.cpp +++ b/Firmware/ESP32/main/I2CDriver/I2CDriver.cpp @@ -1,149 +1,3 @@ -// #include -// #include -// #include -// #include -// #include -// #include -// #include -// #include -// #include - -// #include "sdkconfig.h" -// #include "Board/board_api.h" -// #include "I2CDriver/I2CDriver.h" -// #include "Bluepad32/Bluepad32.h" - -// namespace i2c_driver { - -// static constexpr bool MULTI_SLAVE = -// #if CONFIG_MULTI_SLAVE_MODE == 0 -// false; -// #else -// true; -// #endif - -// std::atomic poll_rumble_{false}; - -// void i2c_initialize() -// { -// i2c_config_t conf; -// std::memset(&conf, 0, sizeof(i2c_config_t)); -// conf.mode = I2C_MODE_MASTER; -// conf.sda_io_num = GPIO_NUM_21; -// conf.scl_io_num = GPIO_NUM_22; -// conf.sda_pullup_en = GPIO_PULLUP_ENABLE; -// conf.scl_pullup_en = GPIO_PULLUP_ENABLE; -// conf.master.clk_speed = 400 * 1000; - -// i2c_param_config(I2C_NUM_0, &conf); -// i2c_driver_install(I2C_NUM_0, conf.mode, 0, 0, 0); - -// ESP_LOGD("I2C", "I2C initialized at pins {SDA: %d, SCL: %d}", conf.sda_io_num, conf.scl_io_num); -// } - -// static inline esp_err_t i2c_write_blocking(uint8_t slave_address, uint8_t* data, size_t data_len) -// { -// i2c_cmd_handle_t cmd = i2c_cmd_link_create(); -// i2c_master_start(cmd); -// i2c_master_write_byte(cmd, (slave_address << 1) | I2C_MASTER_WRITE, true); -// i2c_master_write(cmd, data, data_len, true); -// i2c_master_stop(cmd); - -// esp_err_t ret = i2c_master_cmd_begin(I2C_NUM_0, cmd, pdMS_TO_TICKS(2)); -// i2c_cmd_link_delete(cmd); -// return ret; -// } - -// static inline esp_err_t i2c_read_blocking(uint8_t slave_address, uint8_t* data, size_t data_len) -// { -// i2c_cmd_handle_t cmd = i2c_cmd_link_create(); -// i2c_master_start(cmd); -// i2c_master_write_byte(cmd, (slave_address << 1) | I2C_MASTER_READ, true); - -// if (data_len > 1) -// { -// i2c_master_read(cmd, data, data_len - 1, I2C_MASTER_ACK); -// } - -// i2c_master_read_byte(cmd, data + data_len - 1, I2C_MASTER_NACK); -// i2c_master_stop(cmd); - -// esp_err_t ret = i2c_master_cmd_begin(I2C_NUM_0, cmd, pdMS_TO_TICKS(2)); -// i2c_cmd_link_delete(cmd); -// return ret; -// } - -// void run_loop(void* queue) -// { -// ReportIn report_in = ReportIn(); -// ReportOut report_out = ReportOut(); - -// i2c_initialize(); - -// ESP_LOGD("I2C Stack", "Remaining stack: %d", uxTaskGetStackHighWaterMark(NULL)); - -// while (true) -// { -// for (uint8_t i = 0; i < CONFIG_BLUEPAD32_MAX_DEVICES; ++i) -// { -// vTaskDelay(1); - -// if (!bluepad32::connected(i)) -// { -// continue; -// } - -// report_in = bluepad32::get_report_in(i); -// if (i2c_write_blocking(MULTI_SLAVE ? (report_in.index + 1) : 0x01, reinterpret_cast(&report_in), sizeof(ReportIn)) != ESP_OK) -// { -// ESP_LOGD("I2C", "Failed to write report_in"); -// continue; -// } - -// vTaskDelay(1); - -// if (i2c_read_blocking(MULTI_SLAVE ? (report_in.index + 1) : 0x01, reinterpret_cast(&report_out), sizeof(ReportOut)) != ESP_OK) -// { -// ESP_LOGD("I2C", "Failed to read report_out"); -// continue; -// } - -// bluepad32::set_report_out(report_out); -// } -// } -// } - -// // void run_loop() -// // { -// // ESP_LOGD("Stack", "Remaining stack: %d", uxTaskGetStackHighWaterMark(NULL)); - -// // ReportIn report_in = ReportIn(); - -// // ESP_LOGD("I2C", "ReportIn 1 created: size: %d", sizeof(report_in)); -// // ESP_LOGD("Stack", "Remaining stack: %d", uxTaskGetStackHighWaterMark(NULL)); - -// // ReportIn different_rep = ReportIn(); - -// // ESP_LOGD("I2C", "ReportIn 2 created: size: %d", sizeof(different_rep)); -// // ESP_LOGD("Stack", "Remaining stack: %d", uxTaskGetStackHighWaterMark(NULL)); - -// // ReportOut report_out = ReportOut(); - -// // ESP_LOGD("I2C", "ReportOut created: size: %d", sizeof(report_out)); -// // ESP_LOGD("Stack", "Remaining stack: %d", uxTaskGetStackHighWaterMark(NULL)); - -// // i2c_initialize(); -// // ESP_LOGD("Stack", "Remaining stack: %d", uxTaskGetStackHighWaterMark(NULL)); - -// // while (true) -// // { -// // i2c_write_blocking(MULTI_SLAVE ? (report_in.index + 1) : 0x01, reinterpret_cast(&report_in), sizeof(ReportIn)); -// // vTaskDelay(1); -// // } -// // } - -// } // namespace I2CDriver - #include #include #include @@ -186,14 +40,12 @@ void I2CDriver::run_task() vTaskDelay(1); continue; } - // if (bluepad32::get_report_in(i, report_in)) - // { + report_in = bluepad32::get_report_in(i); if (i2c_write_blocking(MULTI_SLAVE ? (i + 1) : 0x01, reinterpret_cast(&report_in), sizeof(ReportIn)) != ESP_OK) { continue; } - // } vTaskDelay(1); diff --git a/Firmware/ESP32/main/I2CDriver/I2CDriver.h b/Firmware/ESP32/main/I2CDriver/I2CDriver.h index dca7f55..109e5bd 100644 --- a/Firmware/ESP32/main/I2CDriver/I2CDriver.h +++ b/Firmware/ESP32/main/I2CDriver/I2CDriver.h @@ -8,6 +8,7 @@ #include "Reports.h" +//Will probably refactor this to be event driven class I2CDriver { public: @@ -23,6 +24,9 @@ private: #else true; #endif + // std::array report_in_buffer_{}; + // std::array, CONFIG_BLUEPAD32_MAX_DEVICES> new_report_in_{false}; + // std::array report_out_buffer_{}; void initialize_i2c(); diff --git a/Firmware/ESP32/sdkconfig b/Firmware/ESP32/sdkconfig index 32133fe..0651f2d 100644 --- a/Firmware/ESP32/sdkconfig +++ b/Firmware/ESP32/sdkconfig @@ -1383,19 +1383,20 @@ CONFIG_HEAP_TRACING_OFF=y # # Log output # -# CONFIG_LOG_DEFAULT_LEVEL_NONE is not set -CONFIG_LOG_DEFAULT_LEVEL_ERROR=y +CONFIG_LOG_DEFAULT_LEVEL_NONE=y +# CONFIG_LOG_DEFAULT_LEVEL_ERROR is not set # CONFIG_LOG_DEFAULT_LEVEL_WARN is not set # CONFIG_LOG_DEFAULT_LEVEL_INFO is not set # CONFIG_LOG_DEFAULT_LEVEL_DEBUG is not set # CONFIG_LOG_DEFAULT_LEVEL_VERBOSE is not set -CONFIG_LOG_DEFAULT_LEVEL=1 +CONFIG_LOG_DEFAULT_LEVEL=0 CONFIG_LOG_MAXIMUM_EQUALS_DEFAULT=y +# CONFIG_LOG_MAXIMUM_LEVEL_ERROR is not set # CONFIG_LOG_MAXIMUM_LEVEL_WARN is not set # CONFIG_LOG_MAXIMUM_LEVEL_INFO is not set # CONFIG_LOG_MAXIMUM_LEVEL_DEBUG is not set # CONFIG_LOG_MAXIMUM_LEVEL_VERBOSE is not set -CONFIG_LOG_MAXIMUM_LEVEL=1 +CONFIG_LOG_MAXIMUM_LEVEL=0 CONFIG_LOG_COLORS=y CONFIG_LOG_TIMESTAMP_SOURCE_RTOS=y # CONFIG_LOG_TIMESTAMP_SOURCE_SYSTEM is not set diff --git a/Firmware/RP2040/src/Bluepad32/Bluepad32.cpp b/Firmware/RP2040/src/Bluepad32/Bluepad32.cpp index 28ba0ab..080d7a0 100644 --- a/Firmware/RP2040/src/Bluepad32/Bluepad32.cpp +++ b/Firmware/RP2040/src/Bluepad32/Bluepad32.cpp @@ -272,16 +272,13 @@ uni_platform* get_driver() //Public API -void initialize(std::array& gamepads) +void run_task(std::array& gamepads) { for (uint8_t i = 0; i < MAX_GAMEPADS; ++i) { devices_[i].gamepad = &gamepads[i]; } -} -void run_loop() -{ uni_platform_set_custom(get_driver()); uni_init(0, nullptr); diff --git a/Firmware/RP2040/src/Bluepad32/Bluepad32.h b/Firmware/RP2040/src/Bluepad32/Bluepad32.h index 7d02bbd..d70daf3 100644 --- a/Firmware/RP2040/src/Bluepad32/Bluepad32.h +++ b/Firmware/RP2040/src/Bluepad32/Bluepad32.h @@ -11,10 +11,9 @@ /* NOTE: Everything bluepad32/uni needs to be wrapped and kept away from tinyusb due to naming conflicts */ -namespace bluepad32 { - - void initialize(std::array& gamepads); - void run_loop(); +namespace bluepad32 +{ + void run_task(std::array& gamepads); std::array get_connected_map(); bool any_connected(); diff --git a/Firmware/RP2040/src/I2CDriver/i2c_driver_esp32.cpp b/Firmware/RP2040/src/I2CDriver/i2c_driver_esp32.cpp index 04576a1..4add287 100644 --- a/Firmware/RP2040/src/I2CDriver/i2c_driver_esp32.cpp +++ b/Firmware/RP2040/src/I2CDriver/i2c_driver_esp32.cpp @@ -60,7 +60,7 @@ static constexpr size_t MAX_BUFFER_SIZE = std::max(sizeof(ReportOut), sizeof(Rep static constexpr uint8_t I2C_ADDR = 0x01; std::array gamepads_{nullptr}; -std::atomic pad_connected_ = false; +// std::atomic pad_connected_ = false; inline void process_in_report(ReportIn* report_in) { @@ -174,108 +174,9 @@ void initialize(std::array& gamepad) i2c_slave_init(I2C_PORT, I2C_ADDR, &slave_handler); } -bool pad_connected() -{ - return pad_connected_.load(); -} - -} // namespace i2c_driver_esp - -// #include -// #include -// #include -// #include -// #include - -// #include "Gamepad.h" -// #include "board_config.h" -// #include "Board/board_api.h" -// #include "I2CDriver/I2CDriver_ESP.h" - -// namespace i2c_driver_esp { - -// #pragma pack(push, 1) -// struct ReportIn +// bool pad_connected() // { -// uint8_t dpad; -// uint16_t buttons; -// uint8_t trigger_l; -// uint8_t trigger_r; -// int16_t joystick_lx; -// int16_t joystick_ly; -// int16_t joystick_rx; -// int16_t joystick_ry; - -// ReportIn() -// { -// std::memset(this, 0, sizeof(ReportIn)); -// } -// }; -// static_assert(sizeof(ReportIn) == 13, "i2c_driver_esp::ReportIn size mismatch"); - -// struct ReportOut -// { -// uint8_t rumble_l; -// uint8_t rumble_r; - -// ReportOut() -// { -// std::memset(this, 0, sizeof(ReportOut)); -// } -// }; -// static_assert(sizeof(ReportOut) == 2, "i2c_driver_esp::ReportOut size mismatch"); -// #pragma pack(pop) - -// static constexpr uint8_t I2C_ADDR = 0x50; - -// Gamepad* gamepad_ = nullptr; - -// inline void get_in_report() -// { -// static ReportIn report_in = ReportIn(); -// int count = i2c_read_timeout_us(I2C_PORT, I2C_ADDR, reinterpret_cast(&report_in), sizeof(ReportIn), false, 1000); -// if (count > 0) -// { -// gamepad_->set_buttons(report_in.buttons); -// gamepad_->set_dpad(report_in.dpad); -// gamepad_->set_trigger_l(report_in.trigger_l); -// gamepad_->set_trigger_r(report_in.trigger_r); -// gamepad_->set_joystick_lx_int10(report_in.joystick_lx); -// gamepad_->set_joystick_ly_int10(report_in.joystick_ly); -// gamepad_->set_joystick_rx_int10(report_in.joystick_rx); -// gamepad_->set_joystick_ry_int10(report_in.joystick_ry); -// } +// return pad_connected_.load(); // } -// inline void set_out_report() -// { -// static ReportOut report_out = ReportOut(); -// report_out.rumble_l = gamepad_->get_rumble_l().uint8(); -// report_out.rumble_r = gamepad_->get_rumble_r().uint8(); -// i2c_write_timeout_us(I2C_PORT, I2C_ADDR, reinterpret_cast(&report_out), sizeof(ReportOut), false, 1000); -// } - -// void initialize(std::array& gamepad) -// { -// gamepad_ = &gamepad[0]; - -// i2c_init(I2C_PORT, I2C_BAUDRATE); - -// gpio_init(I2C_SDA_PIN); -// gpio_set_function(I2C_SDA_PIN, GPIO_FUNC_I2C); -// gpio_pull_up(I2C_SDA_PIN); - -// gpio_init(I2C_SCL_PIN); -// gpio_set_function(I2C_SCL_PIN, GPIO_FUNC_I2C); -// gpio_pull_up(I2C_SCL_PIN); -// } - -// void task() -// { -// get_in_report(); -// sleep_us(100); -// // set_out_report(); -// // sleep_us(100); -// } - -// } // namespace i2c_driver_esp \ No newline at end of file +} // namespace i2c_driver_esp32 \ No newline at end of file diff --git a/Firmware/RP2040/src/I2CDriver/i2c_driver_esp32.h b/Firmware/RP2040/src/I2CDriver/i2c_driver_esp32.h index 719b976..ea9e3b7 100644 --- a/Firmware/RP2040/src/I2CDriver/i2c_driver_esp32.h +++ b/Firmware/RP2040/src/I2CDriver/i2c_driver_esp32.h @@ -8,7 +8,7 @@ namespace i2c_driver_esp32 { void initialize(std::array& gamepad); - bool pad_connected(); + // bool pad_connected(); } #endif // _I2CDRIVER_ESP_H_ \ No newline at end of file diff --git a/Firmware/RP2040/src/OGXMini/OGXMini_ESP32.cpp b/Firmware/RP2040/src/OGXMini/OGXMini_ESP32.cpp index ad59da3..6c67881 100644 --- a/Firmware/RP2040/src/OGXMini/OGXMini_ESP32.cpp +++ b/Firmware/RP2040/src/OGXMini/OGXMini_ESP32.cpp @@ -13,79 +13,24 @@ #include "USBDevice/DeviceManager.h" #include "Board/board_api.h" #include "OGXMini/OGXMini.h" -#include "I2CDriver/I2CDriver_ESP.h" +#include "I2CDriver/i2c_driver_esp32.h" #include "Gamepad.h" namespace OGXMini { std::array gamepads_; -// void init_esp32_gpio() -// { -// gpio_init(ESP_PROG_PIN); -// gpio_set_dir(ESP_PROG_PIN, GPIO_IN); -// gpio_pull_up(ESP_PROG_PIN); - -// gpio_init(ESP_RST_PIN); -// gpio_set_dir(ESP_RST_PIN, GPIO_IN); -// gpio_pull_up(ESP_RST_PIN); -// } - -// bool uart_passthrough_mode() -// { -// gpio_init(MODE_SEL_PIN); -// gpio_set_dir(MODE_SEL_PIN, GPIO_IN); -// gpio_pull_up(MODE_SEL_PIN); - -// if (gpio_get(MODE_SEL_PIN) == 0) -// { -// return true; -// } -// return false; -// } - -// void reset_esp32() -// { -// gpio_init(ESP_RST_PIN); -// gpio_set_dir(ESP_RST_PIN, GPIO_OUT); -// gpio_put(ESP_RST_PIN, 0); -// sleep_ms(500); -// gpio_put(ESP_RST_PIN, 1); -// sleep_ms(250); -// } - -// void reset_esp32() -// { -// gpio_init(ESP_PROG_PIN); -// gpio_set_dir(ESP_PROG_PIN, GPIO_OUT); -// gpio_put(ESP_PROG_PIN, 1); - -// gpio_init(ESP_RST_PIN); -// gpio_set_dir(ESP_RST_PIN, GPIO_OUT); -// gpio_put(ESP_PROG_PIN, 1); - -// gpio_put(ESP_PROG_PIN, 0); -// sleep_ms(250); - -// gpio_put(ESP_RST_PIN, 0); -// sleep_ms(500); -// gpio_put(ESP_RST_PIN, 1); -// sleep_ms(250); -// gpio_put(ESP_PROG_PIN, 1); -// } - void run_esp32_uart_bridge() { DeviceManager& device_manager = DeviceManager::get_instance(); device_manager.initialize_driver(DeviceDriver::Type::UART_BRIDGE); board_api::enter_esp32_prog_mode(); - // reset_esp32(); device_manager.get_driver()->process(0, gamepads_.front()); //Runs UART Bridge task } void core1_task() { - i2c_driver_esp::initialize(gamepads_); + i2c_driver_esp32::initialize(gamepads_); while (true) { @@ -107,10 +52,7 @@ void run_program() board_api::init_gpio(); - // init_esp32_gpio(); - if (board_api::uart_bridge_mode()) - // if (uart_passthrough_mode()) { run_esp32_uart_bridge(); return; @@ -119,8 +61,6 @@ void run_program() // { // user_settings.write_firmware_version(); // } - - // user_settings.initialize_flash(); board_init(); @@ -130,18 +70,16 @@ void run_program() } DeviceManager& device_manager = DeviceManager::get_instance(); - // device_manager.initialize_driver(user_settings.get_current_driver()); - device_manager.initialize_driver(DeviceDriver::Type::XINPUT); + device_manager.initialize_driver(user_settings.get_current_driver()); multicore_reset_core1(); multicore_launch_core1(core1_task); board_api::reset_esp32(); - // reset_esp32(); - // GPCheckContext gp_check_ctx = { false, user_settings }; - // repeating_timer_t gp_check_timer; - // add_repeating_timer_ms(UserSettings::GP_CHECK_DELAY_MS, gp_check_cb, &gp_check_ctx, &gp_check_timer); + GPCheckContext gp_check_ctx = { false, user_settings }; + repeating_timer_t gp_check_timer; + add_repeating_timer_ms(UserSettings::GP_CHECK_DELAY_MS, gp_check_cb, &gp_check_ctx, &gp_check_timer); DeviceDriver* device_driver = device_manager.get_driver(); @@ -154,16 +92,14 @@ void run_program() device_driver->process(i, gamepads_[i]); tud_task(); } - // device_driver->process(0, gamepads_.front()); - // tud_task(); sleep_us(100); - // if (gp_check_ctx.driver_changed) - // { - // cancel_repeating_timer(&gp_check_timer); - // user_settings.store_driver_type_safe(user_settings.get_current_driver()); - // } + if (gp_check_ctx.driver_changed) + { + cancel_repeating_timer(&gp_check_timer); + user_settings.store_driver_type_safe(user_settings.get_current_driver()); + } } } diff --git a/Firmware/RP2040/src/OGXMini/OGXMini_PicoW.cpp b/Firmware/RP2040/src/OGXMini/OGXMini_PicoW.cpp index 5f53b3d..7420eda 100644 --- a/Firmware/RP2040/src/OGXMini/OGXMini_PicoW.cpp +++ b/Firmware/RP2040/src/OGXMini/OGXMini_PicoW.cpp @@ -2,8 +2,6 @@ #if (OGXM_BOARD == PI_PICOW) #include -#include - #include #include #include @@ -28,8 +26,7 @@ void core1_task() return; } - bluepad32::initialize(gamepads_); - bluepad32::run_loop(); + bluepad32::run_task(gamepads_); } bool gp_check_cb(repeating_timer_t* rt) diff --git a/Firmware/RP2040/src/USBHost/HostDriver/tuh_uni/tuh_uni.cpp b/Firmware/RP2040/src/USBHost/HostDriver/tuh_uni/tuh_uni.cpp index 28e0497..b39d48d 100644 --- a/Firmware/RP2040/src/USBHost/HostDriver/tuh_uni/tuh_uni.cpp +++ b/Firmware/RP2040/src/USBHost/HostDriver/tuh_uni/tuh_uni.cpp @@ -1,6 +1,9 @@ #include #include "tusb_option.h" +#include "tusb.h" +#include "host/usbh.h" +#include "host/usbh_pvt.h" #include "class/hid/hid_host.h" #include "USBHost/HostDriver/tuh_uni/tuh_uni.h" @@ -11,44 +14,63 @@ namespace tuh_uni { enum class HostType { UNKNOWN = 0, HID, XINPUT }; static std::array host_type_; -// const usbh_class_driver_t* xinput_class_driver_ = nullptr; +const usbh_class_driver_t* xinput_class_driver_ = nullptr; + +static inline HostType& get_host_type(uint8_t dev_addr) +{ + return host_type_[dev_addr - 1]; +} bool init() { host_type_.fill(HostType::UNKNOWN); hidh_init(); - tuh_xinput::class_driver()->init(); + xinput_class_driver_ = tuh_xinput::class_driver(); + xinput_class_driver_->init(); return true; } bool deinit() { hidh_deinit(); - tuh_xinput::class_driver()->deinit(); + xinput_class_driver_->deinit(); host_type_.fill(HostType::UNKNOWN); return true; } bool open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const* desc_itf, uint16_t max_len) { - if (tuh_xinput::class_driver()->open(rhport, dev_addr, desc_itf, max_len)) + HostType& host_type = get_host_type(dev_addr); + switch (host_type) { - host_type_[dev_addr - 1] = HostType::XINPUT; - return true; + case HostType::HID: + return hidh_open(rhport, dev_addr, desc_itf, max_len); + case HostType::XINPUT: + return xinput_class_driver_->open(rhport, dev_addr, desc_itf, max_len); + default: + if (xinput_class_driver_->open(rhport, dev_addr, desc_itf, max_len)) + { + host_type = HostType::XINPUT; + return true; + } + else if (hidh_open(rhport, dev_addr, desc_itf, max_len)) + { + host_type = HostType::HID; + return true; + } + break; } - - host_type_[dev_addr - 1] = HostType::HID; - return hidh_open(rhport, dev_addr, desc_itf, max_len);; + return false; } bool set_config(uint8_t dev_addr, uint8_t itf_num) { - switch (host_type_[dev_addr - 1]) + switch (get_host_type(dev_addr)) { case HostType::HID: return hidh_set_config(dev_addr, itf_num); case HostType::XINPUT: - return tuh_xinput::class_driver()->set_config(dev_addr, itf_num); + return xinput_class_driver_->set_config(dev_addr, itf_num); default: return false; } @@ -56,12 +78,12 @@ bool set_config(uint8_t dev_addr, uint8_t itf_num) bool xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) { - switch (host_type_[dev_addr - 1]) + switch (get_host_type(dev_addr)) { case HostType::HID: return hidh_xfer_cb(dev_addr, ep_addr, result, xferred_bytes); case HostType::XINPUT: - return tuh_xinput::class_driver()->xfer_cb(dev_addr, ep_addr, result, xferred_bytes); + return xinput_class_driver_->xfer_cb(dev_addr, ep_addr, result, xferred_bytes); default: return false; } @@ -69,17 +91,19 @@ bool xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t x void close(uint8_t dev_addr) { - switch (host_type_[dev_addr - 1]) + HostType& host_type = get_host_type(dev_addr); + switch (host_type) { case HostType::HID: hidh_close(dev_addr); break; case HostType::XINPUT: - tuh_xinput::class_driver()->close(dev_addr); + xinput_class_driver_->close(dev_addr); break; default: break; } + host_type = HostType::UNKNOWN; } const usbh_class_driver_t* class_driver() diff --git a/Firmware/RP2040/src/USBHost/HostDriver/tuh_uni/tuh_uni.h b/Firmware/RP2040/src/USBHost/HostDriver/tuh_uni/tuh_uni.h index 7f0ad5d..d654263 100644 --- a/Firmware/RP2040/src/USBHost/HostDriver/tuh_uni/tuh_uni.h +++ b/Firmware/RP2040/src/USBHost/HostDriver/tuh_uni/tuh_uni.h @@ -4,9 +4,10 @@ #include #include "tusb.h" +#include "host/usbh.h" #include "host/usbh_pvt.h" -/* This solves a conflict with tinyusb using multiple host class drivers */ +/* There seems to be a conflict with tinyusb using multiple host class drivers */ namespace tuh_uni { const usbh_class_driver_t* class_driver(); diff --git a/Firmware/RP2040/src/USBHost/HostManager.h b/Firmware/RP2040/src/USBHost/HostManager.h index bc34040..a3ff154 100644 --- a/Firmware/RP2040/src/USBHost/HostManager.h +++ b/Firmware/RP2040/src/USBHost/HostManager.h @@ -175,14 +175,14 @@ public: { for (auto& host_slot : host_slots_) { - // if (host_slot.driver_class == driver_class && host_slot.address == address) - // { - // host_slot.reset(); - // } - if (host_slot.address == address) + if (host_slot.driver_class == driver_class && host_slot.address == address) { host_slot.reset(); } + // if (host_slot.address == address) + // { + // host_slot.reset(); + // } } if (OGXMini::update_tuh_status) diff --git a/Firmware/RP2040/src/USBHost/tuh_callbacks.cpp b/Firmware/RP2040/src/USBHost/tuh_callbacks.cpp index d831a03..128e8ce 100644 --- a/Firmware/RP2040/src/USBHost/tuh_callbacks.cpp +++ b/Firmware/RP2040/src/USBHost/tuh_callbacks.cpp @@ -31,10 +31,6 @@ void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_re tuh_vid_pid_get(dev_addr, &vid, &pid); HostManager& host_manager = HostManager::get_instance(); - // if (host_manager.setup_driver(HostDriver::Type::HID_GENERIC, dev_addr, instance, desc_report, desc_len)) - // { - // } - if (host_manager.setup_driver(HostManager::get_type({ vid, pid }), dev_addr, instance, desc_report, desc_len)) { #if defined(CONFIG_EN_4CH) diff --git a/LICENSE b/LICENSE index 7dfda8a..9ee7aa2 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,11 @@ MIT License +Note: Wired configurations of this project exclusively contain MIT licensed +code, configurations using Bluetooth libraries/functionality are licensed +under Apache 2.0 (Bluepad32) and BTStack's proprietary (non-commercial) +license. For full details, please refer to the repositories contained in the +Firmware/external directory of this project. + Copyright (c) 2024 wiredOpposite (wiredopposite.com) Copyright (c) 2024 OpenStickCommunity (gp2040-ce.info) Copyright (c) 2021 Jason Skuby (mytechtoybox.com) @@ -21,4 +27,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md index 96e6b8b..0781237 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,13 @@ # OGX-Mini ![OGX-Mini Boards](images/OGX-Mini-github.jpg "OGX-Mini Boards") -Firmware for the RP2040, capable of emulating gamepads for several consoles. The firmware now comes in 3 flavors, for the [Adafruit Feather USB Host board](https://www.adafruit.com/product/5723), the Pi Pico, and the Waveshare RP2040-Zero. +Firmware for the RP2040, capable of emulating gamepads for several game consoles. The firmware comes in 6 flavors, the [Adafruit Feather USB Host board](https://www.adafruit.com/product/5723), Pi Pico, Waveshare RP2040-Zero, Pi Pico W, RP2040/ESP32 hybrid, and 4-Channel RP2040-Zero. ## Supported platforms - Original Xbox - Playstation 3 - Nintendo Switch (docked) -- XInput (not Xbox 360) +- XInput (use [UsbdSecPatch](https://github.com/InvoxiPlayGames/UsbdSecPatch) for Xbox 360) - Playstation Classic ## Supported devices @@ -16,19 +16,31 @@ Firmware for the RP2040, capable of emulating gamepads for several consoles. The - Xbox 360, One, Series, and Elite - Dualshock 3 (PS3) - Dualshock 4 (PS4) -- Dualsense (PS5, Dualsense Edge should work but it's untested) +- Dualsense (PS5) - Nintendo Switch Pro -- Nintendo Switch wired (tested with PowerA brand) -- Nintendo 64 USB (experimental, tested with RetroLink brand) +- Nintendo Switch wired +- Nintendo 64 Generic USB - Playstation Classic - Generic DInput +- Generic HID (Limited) + +Note: There are some third party controllers that can change their VID/PID, these might not work correctly. ### Wireless adapters - Xbox 360 PC adapter (Microsoft or clones, syncs 1 controller) - 8Bitdo v1 and v2 Bluetooth adapters (set to XInput mode) - Most wireless adapters that present themselves as Switch/XInput/PlayStation controllers should work -Note: There are some third party controllers that can change their VID/PID, these might not work correctly. +### Wireless Bluetooth controllers (Pico W & ESP32) +Note: Bluetooth functionality is in early testing, some may have quirks. +- Xbox Series, One, and Elite 2 +- Dualshock 3 +- Dualshock 4 +- Dualsense +- Switch Pro +- Steam +- Stadia +Please visit [this page](https://bluepad32.readthedocs.io/en/latest/supported_gamepads/) for a more comprehensive list of supported controllers. ## Changing input mode By default the input mode is set to OG Xbox, you must hold a button combo for 3 seconds to change which platform you want to play on. Your chosen input mode will persist after powering off the device. @@ -48,28 +60,30 @@ Start + A (Cross for PlayStation and B for Switch gamepads) After a new mode is stored, the RP2040 will reset itself so you don't need to unplug it. +## Features new to v1.0.0 +- Web application for configuring deadzones and buttons mappings, supports up to 8 saved configurations. +- Bluetooth functionality for the Pico W and ESP32 (in combination with an RP2040). +- 4 channel functionality, connect 4 Picos via I2C and use your Xbox 360 wireless adapter. +- Delayed USB mount until a controller is plugged in, useful for internal installation. +- Generic HID controller support. +- Steel Battalion controller emulation with a wireless Xbox 360 chatpad. +- Xbox DVD dongle emulation. + ## Hardware +For the Pi Pico, RP2040-Zero, 4 channel, and ESP32 configurations, please see the hardware folder for diagrams. + I've designed a PCB for the RP2040-Zero so you can make a small form-factor adapter yourself. The gerber files, schematic, and BOM are in Hardware folder. ![OGX-Mini Boards](images/OGX-Mini-rpzero-int.jpg "OGX-Mini Boards") -If you would like a prebuilt unit, you can purchase one, with cable and Xbox adapter included, from my website [wiredopposite.com](https://wiredopposite.com/product/ogx-mini-controller-adapter-for-original-xbox-playstation-3-and-switch-ogx360/) or my [Etsy store](https://www.etsy.com/listing/1426992904/ogx-mini-controller-adapter-for-original). - -For the Pi Pico, this is a diagram of how you'd connect the extra USB port: - -![Pi Pico Wiring Diagram](images/pi_pico_diagram.png "Pi Pico Wiring Diagram]") - -For the [Adafruit Feather USB Host board](https://www.adafruit.com/product/5723), no extra work is needed. +If you would like a prebuilt unit, you can purchase one, with cable and Xbox adapter included, from my [Etsy store](https://www.etsy.com/listing/1426992904/ogx-mini-controller-adapter-for-original). ## Adding supported controllers If your third party controller isn't working, but the original version is listed above, send me the device's VID and PID and I'll add it so it's recognized properly. ## Compiling -You can compile this for different boards by changing USBD_BOARD in the usbd_config.h file, you can also adjust USBD_MAX_GAMEPADS to enable more controllers on PlayStation 3 (this is experimental). +You can compile this for different boards with CMake arguments while configuring the project. Choosing OGXM_PI_PICO will set the D+ and D- host pins to GPIO 0 and 1. -You can also choose OGXM_RPZERO_INTERPOSER for the RP2040-Zero and that will set D+ and D- to GPIO 10 and 11, so connecting a USB port is easier. You can still use the Pi Pico firmware on the RP2040-Zero (the other way around has not been tested though). - -## Special thanks -Thank you to Ryzee119 and the OpenStickCommunity, without their work this project would not exist. \ No newline at end of file +You can also choose OGXM_RPZERO_INTERPOSER for the RP2040-Zero and that will set D+ and D- to GPIO 10 and 11, so connecting a USB port is easier. You can still use the Pi Pico firmware on the RP2040-Zero (the other way around has not been tested though). \ No newline at end of file