From 1e78ac7fdcea51a05aa0aee5dd42690d3659402d Mon Sep 17 00:00:00 2001 From: moparisthebest Date: Sat, 5 Dec 2020 02:06:48 -0500 Subject: [PATCH] Add UsbRadio output, which switches to radio if not connected to usb-hid capable host --- platformio.ini | 17 +++++- src/Debug.cpp | 5 +- src/RadioReceiver.cpp | 2 +- src/SnesNes.cpp | 2 +- src/gamepad/Debug-Gamepad/DebugGamepad.h | 14 +++-- src/gamepad/ESP32-BLE-Gamepad/BleGamepad.h | 16 ++--- src/gamepad/Gamepad.h | 4 ++ src/gamepad/Radio-Gamepad/RadioGamepad.h | 12 ++-- src/gamepad/USB-Gamepad/UsbGamepad.h | 18 ++++-- .../UsbRadio-Gamepad/UsbRadioGamepad.h | 58 +++++++++++++++++++ src/gamepad/common.h | 14 ++++- 11 files changed, 134 insertions(+), 28 deletions(-) create mode 100644 src/gamepad/UsbRadio-Gamepad/UsbRadioGamepad.h diff --git a/platformio.ini b/platformio.ini index af912ef..b65aefc 100644 --- a/platformio.ini +++ b/platformio.ini @@ -13,7 +13,7 @@ # supported values: # $board: micro, esp32 # $input: radio, snes, debug -# $output: radio, usb, bt, debug +# $output: radio, usb, usbradio, bt, debug # please note not all boards are compatible with all inputs/outputs # for example esp32 can only do bt # micro can only do radio or usb @@ -57,6 +57,11 @@ platform = atmelavr build_flags = -DGAMEPAD_OUTPUT=3 src_filter = + +[out-usbradio] +extends = out-usb, out-radio +build_flags = -DGAMEPAD_OUTPUT=4 +src_filter = ${out-usb.src_filter} ${out-radio.src_filter} + # radio input [in-radio] @@ -118,6 +123,11 @@ extends = micro, in-snes, out-radio src_filter = ${in-snes.src_filter} ${out-radio.src_filter} build_flags = ${in-snes.build_flags} ${out-radio.build_flags} +[env:micro-snes-usbradio] +extends = micro, in-snes, out-usbradio +src_filter = ${in-snes.src_filter} ${out-usbradio.src_filter} +build_flags = ${in-snes.build_flags} ${out-usbradio.build_flags} + # debug input [in-debug] @@ -149,3 +159,8 @@ extends = micro, in-debug, out-radio src_filter = ${in-debug.src_filter} ${out-radio.src_filter} build_flags = ${in-debug.build_flags} ${out-radio.build_flags} +[env:micro-debug-usbradio] +extends = micro, in-debug, out-usbradio +src_filter = ${in-debug.src_filter} ${out-usbradio.src_filter} +build_flags = ${in-debug.build_flags} ${out-usbradio.build_flags} + diff --git a/src/Debug.cpp b/src/Debug.cpp index 50c7c28..cf97d81 100644 --- a/src/Debug.cpp +++ b/src/Debug.cpp @@ -3,7 +3,9 @@ #include "gamepad/Gamepad.h" -Gamepad gamepad; +GAMEPAD_CLASS gamepad; + +uint8_t c = 0; void setup() { Serial.begin(115200); @@ -14,7 +16,6 @@ void setup() { void loop() { if (gamepad.isConnected()) { // test code to automate sending to test RadioReceiver (or any gamepad impl) - uint8_t c = 0; Serial.println("Press buttons 1 and 32. Move all axes to center. Set DPAD to down right."); gamepad.press(c, BUTTON_1); gamepad.press(c, BUTTON_32); diff --git a/src/RadioReceiver.cpp b/src/RadioReceiver.cpp index a112402..fb9cd64 100644 --- a/src/RadioReceiver.cpp +++ b/src/RadioReceiver.cpp @@ -13,7 +13,7 @@ RF24 radio(7, 8); // CE, CSN const byte address[13] = "OpenRetroPad"; -Gamepad gamepad; +GAMEPAD_CLASS gamepad; void setup() { gamepad.begin(); diff --git a/src/SnesNes.cpp b/src/SnesNes.cpp index 2e0e3cf..e27a702 100644 --- a/src/SnesNes.cpp +++ b/src/SnesNes.cpp @@ -146,7 +146,7 @@ static const int translateToHid[12] = { GameControllers controllers; -Gamepad gamepad; +GAMEPAD_CLASS gamepad; void setup() { gamepad.begin(); diff --git a/src/gamepad/Debug-Gamepad/DebugGamepad.h b/src/gamepad/Debug-Gamepad/DebugGamepad.h index 63921cb..83efd1c 100644 --- a/src/gamepad/Debug-Gamepad/DebugGamepad.h +++ b/src/gamepad/Debug-Gamepad/DebugGamepad.h @@ -3,6 +3,10 @@ #include +#ifndef GAMEPAD_CLASS +#define GAMEPAD_CLASS DebugGamepad +#endif + #include "../common.h" class DebugGamepad : public AbstractGamepad { @@ -10,22 +14,20 @@ class DebugGamepad : public AbstractGamepad { DebugGamepad() : AbstractGamepad() { } - void begin(void) { + virtual void begin(void) { Serial.begin(115200); Serial.println("DebugGamepad.begin"); } - void setAxes(const uint8_t cIdx, int16_t x, int16_t y, int16_t z, int16_t rZ, char rX, char rY, signed char hat) { + virtual void setAxes(const uint8_t cIdx, int16_t x, int16_t y, int16_t z, int16_t rZ, char rX, char rY, signed char hat) { Serial.println("DebugGamepad.setAxes"); AbstractGamepad::setAxes(cIdx, x, y, z, rZ, rX, rY, hat); } - void sync(const uint8_t cIdx) { - Serial.print("DebugGamepad.sync: "); + virtual void sendHidReport(const uint8_t cIdx, const void* d, int len) { + Serial.print("DebugGamepad.sendHidReport: "); Serial.println(cIdx); } }; -typedef DebugGamepad Gamepad; - #endif // USB_GAMEPAD_H diff --git a/src/gamepad/ESP32-BLE-Gamepad/BleGamepad.h b/src/gamepad/ESP32-BLE-Gamepad/BleGamepad.h index 480c4d5..43b2c0d 100644 --- a/src/gamepad/ESP32-BLE-Gamepad/BleGamepad.h +++ b/src/gamepad/ESP32-BLE-Gamepad/BleGamepad.h @@ -3,6 +3,10 @@ #include "sdkconfig.h" #if defined(CONFIG_BT_ENABLED) +#ifndef GAMEPAD_CLASS +#define GAMEPAD_CLASS BleGamepad +#endif + #include "../common.h" #include "BLE2902.h" @@ -84,22 +88,22 @@ class BleGamepad : public AbstractGamepad { this->connectionStatus = new BleConnectionStatus(); } - void begin(void) { + virtual void begin(void) { xTaskCreate(this->taskServer, "server", 20000, (void *)this, 5, NULL); } - void sync(const uint8_t cIdx) { + virtual void sendHidReport(const uint8_t cIdx, const void *d, int len) { if (this->isConnected()) { - this->inputGamepad[cIdx]->setValue(gamepadReport, GAMEPAD_REPORT_LEN); + this->inputGamepad[cIdx]->setValue((uint8_t *)d, len); this->inputGamepad[cIdx]->notify(); } } - bool isConnected(void) { + virtual bool isConnected(void) { return this->connectionStatus->connected; } - void setBatteryLevel(uint8_t level) { + virtual void setBatteryLevel(uint8_t level) { this->batteryLevel = level; if (hid != 0) this->hid->setBatteryLevel(this->batteryLevel); @@ -109,7 +113,5 @@ class BleGamepad : public AbstractGamepad { virtual void onStarted(BLEServer *pServer){}; }; -typedef BleGamepad Gamepad; - #endif // CONFIG_BT_ENABLED #endif // ESP32_BLE_GAMEPAD_H diff --git a/src/gamepad/Gamepad.h b/src/gamepad/Gamepad.h index 3719b38..88cbb44 100644 --- a/src/gamepad/Gamepad.h +++ b/src/gamepad/Gamepad.h @@ -19,6 +19,10 @@ #include "ESP32-BLE-Gamepad/BleGamepad.h" +#elif GAMEPAD_OUTPUT == 4 + +#include "UsbRadio-Gamepad/UsbRadioGamepad.h" + #else #error Unsupported value for GAMEPAD_OUTPUT, must be 0-3 diff --git a/src/gamepad/Radio-Gamepad/RadioGamepad.h b/src/gamepad/Radio-Gamepad/RadioGamepad.h index aa7cc11..8c525cc 100644 --- a/src/gamepad/Radio-Gamepad/RadioGamepad.h +++ b/src/gamepad/Radio-Gamepad/RadioGamepad.h @@ -7,6 +7,10 @@ #define GAMEPAD_REPORT_ARRAY_ADD 1 +#ifndef GAMEPAD_CLASS +#define GAMEPAD_CLASS RadioGamepad +#endif + #include "../common.h" RF24 radio(7, 8); // CE, CSN @@ -17,7 +21,7 @@ class RadioGamepad : public AbstractGamepad { RadioGamepad() : AbstractGamepad() { } - void begin(void) { + virtual void begin(void) { Serial.println("RadioGamepad.begin"); radio.begin(); radio.openWritingPipe(address); @@ -25,13 +29,11 @@ class RadioGamepad : public AbstractGamepad { radio.stopListening(); } - void sync(const uint8_t cIdx) { + virtual void sendHidReport(const uint8_t cIdx, const void* d, int len) { Serial.println("RadioGamepad.sync"); gamepadReport[15] = cIdx; - radio.write(&gamepadReport, 16); + radio.write(d, 16); } }; -typedef RadioGamepad Gamepad; - #endif // RADIO_GAMEPAD_H diff --git a/src/gamepad/USB-Gamepad/UsbGamepad.h b/src/gamepad/USB-Gamepad/UsbGamepad.h index ba2ef2a..d2f4e67 100644 --- a/src/gamepad/USB-Gamepad/UsbGamepad.h +++ b/src/gamepad/USB-Gamepad/UsbGamepad.h @@ -3,6 +3,10 @@ #include +#ifndef GAMEPAD_CLASS +#define GAMEPAD_CLASS UsbGamepad +#endif + #include "../common.h" #include "HID.h" @@ -53,6 +57,8 @@ class Gamepad_ : public PluggableUSBModule { // due to the USB specs, but Windows and Linux just assumes its in report mode. protocol = HID_REPORT_PROTOCOL; + this->sentHidReportDescriptor = true; + return USB_SendControl(TRANSFER_PGM, _hidReportDescriptor, sizeof(_hidReportDescriptor)); } @@ -100,6 +106,7 @@ class Gamepad_ : public PluggableUSBModule { } public: + boolean sentHidReportDescriptor = false; Gamepad_(void) : PluggableUSBModule(1, 1, epType), protocol(HID_REPORT_PROTOCOL), idle(1) { epType[0] = EP_TYPE_INTERRUPT_IN; PluggableUSB().plug(this); @@ -121,11 +128,14 @@ class UsbGamepad : public AbstractGamepad { this->deviceManufacturer = deviceManufacturer; } - void sync(const uint8_t cIdx) { - gamepad[cIdx].send(&gamepadReport, GAMEPAD_REPORT_LEN); + virtual bool isConnected(void) { + // if the first one is connected, we assume they all are + return gamepad[0].sentHidReportDescriptor; + } + + virtual void sendHidReport(const uint8_t cIdx, const void* d, int len) { + gamepad[cIdx].send(d, len); } }; -typedef UsbGamepad Gamepad; - #endif // USB_GAMEPAD_H diff --git a/src/gamepad/UsbRadio-Gamepad/UsbRadioGamepad.h b/src/gamepad/UsbRadio-Gamepad/UsbRadioGamepad.h new file mode 100644 index 0000000..f114f64 --- /dev/null +++ b/src/gamepad/UsbRadio-Gamepad/UsbRadioGamepad.h @@ -0,0 +1,58 @@ +#ifndef RADIO_USB_GAMEPAD_H +#define RADIO_USB_GAMEPAD_H + +// number of button presses we wait for USB-HID to connect +// works within 1 for me but who knows... +#ifndef GAMEPAD_USBRADIO_DETECT_BUTTONS +#define GAMEPAD_USBRADIO_DETECT_BUTTONS 1 +#endif + +#ifndef GAMEPAD_CLASS +#define GAMEPAD_CLASS UsbRadioGamepad +#endif + +#include "../Radio-Gamepad/RadioGamepad.h" +#include "../USB-Gamepad/UsbGamepad.h" + +class UsbRadioGamepad : public AbstractGamepad { + public: + uint8_t checkCount = 0; + + AbstractGamepad* gamepad = new UsbGamepad(); + + void (UsbRadioGamepad::*syncFunc)(const uint8_t cIdx, const void* d, int len) = &UsbRadioGamepad::syncDetectUsb; + + UsbRadioGamepad() : AbstractGamepad() { + } + + virtual void begin(void) { + gamepad->begin(); + } + + void syncDetectUsb(const uint8_t cIdx, const void* d, int len) { + if (++checkCount <= GAMEPAD_USBRADIO_DETECT_BUTTONS) { + if (gamepad->isConnected()) { + // we are connected to PC/phone/usb-hid-capable-device, stop checking + syncFunc = &UsbRadioGamepad::syncNoDetect; + } else if (checkCount == GAMEPAD_USBRADIO_DETECT_BUTTONS) { + // we assume we are connected to a power brick or power supply, and switch to radio + delete gamepad; + gamepad = new RadioGamepad(); + gamepad->begin(); + // stop trying to detect, it is what it is... + syncFunc = &UsbRadioGamepad::syncNoDetect; + } + } + gamepad->sendHidReport(cIdx, &gamepadReport, GAMEPAD_REPORT_LEN); + } + + void syncNoDetect(const uint8_t cIdx, const void* d, int len) { + gamepad->sendHidReport(cIdx, &gamepadReport, GAMEPAD_REPORT_LEN); + } + + virtual void sendHidReport(const uint8_t cIdx, const void* d, int len) { + (*this.*syncFunc)(cIdx, &gamepadReport, GAMEPAD_REPORT_LEN); + } +}; + +#endif // RADIO_GAMEPAD_H diff --git a/src/gamepad/common.h b/src/gamepad/common.h index 43eadf3..6ed18be 100644 --- a/src/gamepad/common.h +++ b/src/gamepad/common.h @@ -1,4 +1,7 @@ +#ifndef GAMEPAD_COMMON_H +#define GAMEPAD_COMMON_H + #include #define ARDUINO_ARCH_ESP32 1 @@ -241,5 +244,14 @@ class AbstractGamepad { return ((b & _buttons[cIdx]) > 0); } - virtual void sync(const uint8_t cIdx); // actually sends report + virtual void sync(const uint8_t cIdx) { + sendHidReport(cIdx, &gamepadReport, GAMEPAD_REPORT_LEN); + } + + virtual void sendHidReport(const uint8_t cIdx, const void* d, int len); // actually sends report + + virtual ~AbstractGamepad() { + } }; + +#endif // GAMEPAD_COMMON_H