Send d-pad and analog triggers in the link frame
Johnny 5 needs seven axes of control, so the frame now carries dpad and both analog triggers alongside the sticks and button mask.
This commit is contained in:
+45
-18
@@ -5,18 +5,23 @@
|
||||
* Board: "ESP32 Dev Module" listed under esp32_bluepad32
|
||||
* Libraries: none (Bluepad32 ships inside the board package)
|
||||
*
|
||||
* Reads a Bluetooth Classic gamepad via Bluepad32 and streams the stick /
|
||||
* button state out of Serial2 as plain ASCII lines. The second ESP32 (see
|
||||
* ../receiver) picks those up and drives the LEGO hubs over BLE.
|
||||
* Reads a Bluetooth Classic gamepad via Bluepad32 and streams the full stick,
|
||||
* trigger, d-pad and button state out of Serial2 as plain ASCII lines. The
|
||||
* second ESP32 (see ../receiver) picks those up and drives the LEGO hubs.
|
||||
*
|
||||
* This board does NOT talk to the hubs and must not have Legoino or NimBLE
|
||||
* installed. Keeping BTstack and NimBLE on separate chips is the entire point.
|
||||
*
|
||||
* Wiring to the receiver board:
|
||||
* TX GPIO17 -> RX GPIO16 on receiver
|
||||
* RX GPIO16 <- TX GPIO17 on receiver (unused today, reserved for acks)
|
||||
* GND -> GND (mandatory - common ground)
|
||||
*
|
||||
* Line format: G,<lx>,<ly>,<rx>,<ry>,<buttons>*<XX>\n
|
||||
* Line format: G,<lx>,<ly>,<rx>,<ry>,<buttons>,<dpad>,<l2>,<r2>*<XX>\n
|
||||
* lx/ly/rx/ry : -512..511 (Bluepad32 raw axis range)
|
||||
* buttons : 16-bit mask, decimal
|
||||
* dpad : 8-bit mask, decimal
|
||||
* l2/r2 : analog triggers, 0..1023
|
||||
* XX : XOR checksum of everything before the '*', two hex digits
|
||||
*
|
||||
* Created by: Jess Rogerson (yelling commands at Claude.AI)
|
||||
@@ -29,16 +34,21 @@ static const int LINK_TX_PIN = 17;
|
||||
static const int LINK_RX_PIN = 16;
|
||||
static const long LINK_BAUD = 115200;
|
||||
static const int STATUS_LED_PIN = 2;
|
||||
static const unsigned long SEND_INTERVAL_MS = 20; // 50 Hz
|
||||
static const unsigned long SEND_INTERVAL_MS = 20; // 50 Hz ceiling
|
||||
static const unsigned long HEARTBEAT_MS = 200; // resend even if idle
|
||||
|
||||
// Set to 1 to echo every outgoing frame to the USB serial monitor.
|
||||
#define DEBUG_FRAMES 0
|
||||
|
||||
// ------------------------------------------------------------------ state
|
||||
ControllerPtr myControllers[BP32_MAX_CONTROLLERS];
|
||||
|
||||
static unsigned long lastSend = 0;
|
||||
static unsigned long lastHeartbeat = 0;
|
||||
static int lastLx = 0, lastLy = 0, lastRx = 0, lastRy = 0;
|
||||
static int lastL2 = 0, lastR2 = 0;
|
||||
static uint16_t lastButtons = 0;
|
||||
static uint8_t lastDpad = 0;
|
||||
|
||||
// -------------------------------------------------------------- callbacks
|
||||
void onConnectedController(ControllerPtr ctl) {
|
||||
@@ -71,20 +81,30 @@ static uint8_t xorChecksum(const char *s) {
|
||||
return c;
|
||||
}
|
||||
|
||||
static void sendFrame(int lx, int ly, int rx, int ry, uint16_t buttons) {
|
||||
char payload[64];
|
||||
snprintf(payload, sizeof(payload), "G,%d,%d,%d,%d,%u", lx, ly, rx, ry, buttons);
|
||||
static void sendFrame(int lx, int ly, int rx, int ry,
|
||||
uint16_t buttons, uint8_t dpad, int l2, int r2) {
|
||||
char payload[80];
|
||||
snprintf(payload, sizeof(payload), "G,%d,%d,%d,%d,%u,%u,%d,%d",
|
||||
lx, ly, rx, ry, buttons, dpad, l2, r2);
|
||||
Serial2.printf("%s*%02X\n", payload, xorChecksum(payload));
|
||||
#if DEBUG_FRAMES
|
||||
Serial.printf("%s*%02X\n", payload, xorChecksum(payload));
|
||||
#endif
|
||||
}
|
||||
|
||||
// Only bother transmitting when something actually moved, or on heartbeat.
|
||||
static bool worthSending(int lx, int ly, int rx, int ry, uint16_t buttons) {
|
||||
const int DELTA = 4; // ignore stick jitter of a few counts
|
||||
// Only transmit when something actually moved, or on heartbeat.
|
||||
static bool worthSending(int lx, int ly, int rx, int ry,
|
||||
uint16_t buttons, uint8_t dpad, int l2, int r2) {
|
||||
const int DELTA = 4; // ignore stick jitter of a few counts
|
||||
const int TRIG = 8; // triggers are noisier at rest
|
||||
if (buttons != lastButtons) return true;
|
||||
if (dpad != lastDpad) return true;
|
||||
if (abs(lx - lastLx) >= DELTA) return true;
|
||||
if (abs(ly - lastLy) >= DELTA) return true;
|
||||
if (abs(rx - lastRx) >= DELTA) return true;
|
||||
if (abs(ry - lastRy) >= DELTA) return true;
|
||||
if (abs(l2 - lastL2) >= TRIG) return true;
|
||||
if (abs(r2 - lastR2) >= TRIG) return true;
|
||||
return (millis() - lastHeartbeat) >= HEARTBEAT_MS;
|
||||
}
|
||||
|
||||
@@ -100,12 +120,12 @@ void setup() {
|
||||
|
||||
BP32.setup(&onConnectedController, &onDisconnectedController);
|
||||
|
||||
// Clear stored pairings so a controller that was last paired to a phone
|
||||
// will bind here. Comment this out once you are happy with the pairing,
|
||||
// otherwise you re-pair on every boot.
|
||||
// Bluepad32 keeps pairing keys in NVS and reflashing does NOT clear them.
|
||||
// Leave this in for the first pairing, then comment it out - otherwise you
|
||||
// re-pair the pad on every boot.
|
||||
BP32.forgetBluetoothKeys();
|
||||
|
||||
// We only care about real gamepads, not the mouse/keyboard virtual device.
|
||||
// We only want real gamepads, not the mouse/keyboard virtual device.
|
||||
BP32.enableVirtualDevice(false);
|
||||
|
||||
Serial.println("Ready - put the PS4 pad in pairing mode (SHARE + PS)");
|
||||
@@ -130,10 +150,12 @@ void loop() {
|
||||
if (gp == nullptr) {
|
||||
// Nothing connected: keep sending neutral so the receiver's failsafe
|
||||
// never has to guess.
|
||||
sendFrame(0, 0, 0, 0, 0);
|
||||
sendFrame(0, 0, 0, 0, 0, 0, 0, 0);
|
||||
lastHeartbeat = now;
|
||||
lastLx = lastLy = lastRx = lastRy = 0;
|
||||
lastL2 = lastR2 = 0;
|
||||
lastButtons = 0;
|
||||
lastDpad = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -141,12 +163,17 @@ void loop() {
|
||||
int ly = gp->axisY();
|
||||
int rx = gp->axisRX();
|
||||
int ry = gp->axisRY();
|
||||
int l2 = gp->brake();
|
||||
int r2 = gp->throttle();
|
||||
uint16_t buttons = gp->buttons();
|
||||
uint8_t dpad = gp->dpad();
|
||||
|
||||
if (dataUpdated || worthSending(lx, ly, rx, ry, buttons)) {
|
||||
sendFrame(lx, ly, rx, ry, buttons);
|
||||
if (dataUpdated || worthSending(lx, ly, rx, ry, buttons, dpad, l2, r2)) {
|
||||
sendFrame(lx, ly, rx, ry, buttons, dpad, l2, r2);
|
||||
lastLx = lx; lastLy = ly; lastRx = rx; lastRy = ry;
|
||||
lastL2 = l2; lastR2 = r2;
|
||||
lastButtons = buttons;
|
||||
lastDpad = dpad;
|
||||
lastHeartbeat = now;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user