Add UsbRadio output, which switches to radio if not connected to usb-hid capable host

This commit is contained in:
Travis Burtrum 2020-12-05 02:06:48 -05:00
parent 7cf833e59f
commit 1e78ac7fdc
11 changed files with 134 additions and 28 deletions

View File

@ -13,7 +13,7 @@
# supported values: # supported values:
# $board: micro, esp32 # $board: micro, esp32
# $input: radio, snes, debug # $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 # please note not all boards are compatible with all inputs/outputs
# for example esp32 can only do bt # for example esp32 can only do bt
# micro can only do radio or usb # micro can only do radio or usb
@ -57,6 +57,11 @@ platform = atmelavr
build_flags = -DGAMEPAD_OUTPUT=3 build_flags = -DGAMEPAD_OUTPUT=3
src_filter = +<gamepad/ESP32-BLE-Gamepad> src_filter = +<gamepad/ESP32-BLE-Gamepad>
[out-usbradio]
extends = out-usb, out-radio
build_flags = -DGAMEPAD_OUTPUT=4
src_filter = ${out-usb.src_filter} ${out-radio.src_filter}
# radio input # radio input
[in-radio] [in-radio]
@ -118,6 +123,11 @@ extends = micro, in-snes, out-radio
src_filter = ${in-snes.src_filter} ${out-radio.src_filter} src_filter = ${in-snes.src_filter} ${out-radio.src_filter}
build_flags = ${in-snes.build_flags} ${out-radio.build_flags} 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 # debug input
[in-debug] [in-debug]
@ -149,3 +159,8 @@ extends = micro, in-debug, out-radio
src_filter = ${in-debug.src_filter} ${out-radio.src_filter} src_filter = ${in-debug.src_filter} ${out-radio.src_filter}
build_flags = ${in-debug.build_flags} ${out-radio.build_flags} 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}

View File

@ -3,7 +3,9 @@
#include "gamepad/Gamepad.h" #include "gamepad/Gamepad.h"
Gamepad gamepad; GAMEPAD_CLASS gamepad;
uint8_t c = 0;
void setup() { void setup() {
Serial.begin(115200); Serial.begin(115200);
@ -14,7 +16,6 @@ void setup() {
void loop() { void loop() {
if (gamepad.isConnected()) { if (gamepad.isConnected()) {
// test code to automate sending to test RadioReceiver (or any gamepad impl) // 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."); 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_1);
gamepad.press(c, BUTTON_32); gamepad.press(c, BUTTON_32);

View File

@ -13,7 +13,7 @@ RF24 radio(7, 8); // CE, CSN
const byte address[13] = "OpenRetroPad"; const byte address[13] = "OpenRetroPad";
Gamepad gamepad; GAMEPAD_CLASS gamepad;
void setup() { void setup() {
gamepad.begin(); gamepad.begin();

View File

@ -146,7 +146,7 @@ static const int translateToHid[12] = {
GameControllers controllers; GameControllers controllers;
Gamepad gamepad; GAMEPAD_CLASS gamepad;
void setup() { void setup() {
gamepad.begin(); gamepad.begin();

View File

@ -3,6 +3,10 @@
#include <WString.h> #include <WString.h>
#ifndef GAMEPAD_CLASS
#define GAMEPAD_CLASS DebugGamepad
#endif
#include "../common.h" #include "../common.h"
class DebugGamepad : public AbstractGamepad { class DebugGamepad : public AbstractGamepad {
@ -10,22 +14,20 @@ class DebugGamepad : public AbstractGamepad {
DebugGamepad() : AbstractGamepad() { DebugGamepad() : AbstractGamepad() {
} }
void begin(void) { virtual void begin(void) {
Serial.begin(115200); Serial.begin(115200);
Serial.println("DebugGamepad.begin"); 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"); Serial.println("DebugGamepad.setAxes");
AbstractGamepad::setAxes(cIdx, x, y, z, rZ, rX, rY, hat); AbstractGamepad::setAxes(cIdx, x, y, z, rZ, rX, rY, hat);
} }
void sync(const uint8_t cIdx) { virtual void sendHidReport(const uint8_t cIdx, const void* d, int len) {
Serial.print("DebugGamepad.sync: "); Serial.print("DebugGamepad.sendHidReport: ");
Serial.println(cIdx); Serial.println(cIdx);
} }
}; };
typedef DebugGamepad Gamepad;
#endif // USB_GAMEPAD_H #endif // USB_GAMEPAD_H

View File

@ -3,6 +3,10 @@
#include "sdkconfig.h" #include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED) #if defined(CONFIG_BT_ENABLED)
#ifndef GAMEPAD_CLASS
#define GAMEPAD_CLASS BleGamepad
#endif
#include "../common.h" #include "../common.h"
#include "BLE2902.h" #include "BLE2902.h"
@ -84,22 +88,22 @@ class BleGamepad : public AbstractGamepad {
this->connectionStatus = new BleConnectionStatus(); this->connectionStatus = new BleConnectionStatus();
} }
void begin(void) { virtual void begin(void) {
xTaskCreate(this->taskServer, "server", 20000, (void *)this, 5, NULL); 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()) { if (this->isConnected()) {
this->inputGamepad[cIdx]->setValue(gamepadReport, GAMEPAD_REPORT_LEN); this->inputGamepad[cIdx]->setValue((uint8_t *)d, len);
this->inputGamepad[cIdx]->notify(); this->inputGamepad[cIdx]->notify();
} }
} }
bool isConnected(void) { virtual bool isConnected(void) {
return this->connectionStatus->connected; return this->connectionStatus->connected;
} }
void setBatteryLevel(uint8_t level) { virtual void setBatteryLevel(uint8_t level) {
this->batteryLevel = level; this->batteryLevel = level;
if (hid != 0) if (hid != 0)
this->hid->setBatteryLevel(this->batteryLevel); this->hid->setBatteryLevel(this->batteryLevel);
@ -109,7 +113,5 @@ class BleGamepad : public AbstractGamepad {
virtual void onStarted(BLEServer *pServer){}; virtual void onStarted(BLEServer *pServer){};
}; };
typedef BleGamepad Gamepad;
#endif // CONFIG_BT_ENABLED #endif // CONFIG_BT_ENABLED
#endif // ESP32_BLE_GAMEPAD_H #endif // ESP32_BLE_GAMEPAD_H

View File

@ -19,6 +19,10 @@
#include "ESP32-BLE-Gamepad/BleGamepad.h" #include "ESP32-BLE-Gamepad/BleGamepad.h"
#elif GAMEPAD_OUTPUT == 4
#include "UsbRadio-Gamepad/UsbRadioGamepad.h"
#else #else
#error Unsupported value for GAMEPAD_OUTPUT, must be 0-3 #error Unsupported value for GAMEPAD_OUTPUT, must be 0-3

View File

@ -7,6 +7,10 @@
#define GAMEPAD_REPORT_ARRAY_ADD 1 #define GAMEPAD_REPORT_ARRAY_ADD 1
#ifndef GAMEPAD_CLASS
#define GAMEPAD_CLASS RadioGamepad
#endif
#include "../common.h" #include "../common.h"
RF24 radio(7, 8); // CE, CSN RF24 radio(7, 8); // CE, CSN
@ -17,7 +21,7 @@ class RadioGamepad : public AbstractGamepad {
RadioGamepad() : AbstractGamepad() { RadioGamepad() : AbstractGamepad() {
} }
void begin(void) { virtual void begin(void) {
Serial.println("RadioGamepad.begin"); Serial.println("RadioGamepad.begin");
radio.begin(); radio.begin();
radio.openWritingPipe(address); radio.openWritingPipe(address);
@ -25,13 +29,11 @@ class RadioGamepad : public AbstractGamepad {
radio.stopListening(); radio.stopListening();
} }
void sync(const uint8_t cIdx) { virtual void sendHidReport(const uint8_t cIdx, const void* d, int len) {
Serial.println("RadioGamepad.sync"); Serial.println("RadioGamepad.sync");
gamepadReport[15] = cIdx; gamepadReport[15] = cIdx;
radio.write(&gamepadReport, 16); radio.write(d, 16);
} }
}; };
typedef RadioGamepad Gamepad;
#endif // RADIO_GAMEPAD_H #endif // RADIO_GAMEPAD_H

View File

@ -3,6 +3,10 @@
#include <WString.h> #include <WString.h>
#ifndef GAMEPAD_CLASS
#define GAMEPAD_CLASS UsbGamepad
#endif
#include "../common.h" #include "../common.h"
#include "HID.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. // due to the USB specs, but Windows and Linux just assumes its in report mode.
protocol = HID_REPORT_PROTOCOL; protocol = HID_REPORT_PROTOCOL;
this->sentHidReportDescriptor = true;
return USB_SendControl(TRANSFER_PGM, _hidReportDescriptor, sizeof(_hidReportDescriptor)); return USB_SendControl(TRANSFER_PGM, _hidReportDescriptor, sizeof(_hidReportDescriptor));
} }
@ -100,6 +106,7 @@ class Gamepad_ : public PluggableUSBModule {
} }
public: public:
boolean sentHidReportDescriptor = false;
Gamepad_(void) : PluggableUSBModule(1, 1, epType), protocol(HID_REPORT_PROTOCOL), idle(1) { Gamepad_(void) : PluggableUSBModule(1, 1, epType), protocol(HID_REPORT_PROTOCOL), idle(1) {
epType[0] = EP_TYPE_INTERRUPT_IN; epType[0] = EP_TYPE_INTERRUPT_IN;
PluggableUSB().plug(this); PluggableUSB().plug(this);
@ -121,11 +128,14 @@ class UsbGamepad : public AbstractGamepad {
this->deviceManufacturer = deviceManufacturer; this->deviceManufacturer = deviceManufacturer;
} }
void sync(const uint8_t cIdx) { virtual bool isConnected(void) {
gamepad[cIdx].send(&gamepadReport, GAMEPAD_REPORT_LEN); // 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 #endif // USB_GAMEPAD_H

View File

@ -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

View File

@ -1,4 +1,7 @@
#ifndef GAMEPAD_COMMON_H
#define GAMEPAD_COMMON_H
#include <Arduino.h> #include <Arduino.h>
#define ARDUINO_ARCH_ESP32 1 #define ARDUINO_ARCH_ESP32 1
@ -241,5 +244,14 @@ class AbstractGamepad {
return ((b & _buttons[cIdx]) > 0); 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