diff --git a/ps4_lego_onebrain/ps4_lego_onebrain.ino b/ps4_lego_onebrain/ps4_lego_onebrain.ino index 2502073..fe78fff 100644 --- a/ps4_lego_onebrain/ps4_lego_onebrain.ino +++ b/ps4_lego_onebrain/ps4_lego_onebrain.ino @@ -17,6 +17,13 @@ * first - see docs/BUILD.md. Without it you get: * fatal error: btstack.h: No such file or directory * + * FILE ORDER MATTERS: + * The Arduino IDE generates function prototypes and injects them immediately + * before the FIRST function definition in the file. Any type used in a + * function signature must therefore be declared above that point. All the + * structs and enums live at the top for this reason. Move a function above + * them and you get a wall of "'Hub' was not declared in this scope". + * * THREADING - read this before changing anything: * BTstack is not thread-safe. loop() runs in the Arduino task, BTstack runs * in its own task. The only BTstack call allowed from loop() is @@ -93,12 +100,11 @@ static const uint8_t LWP3_CHAR_UUID[16] = { 0x00, 0x00, 0x16, 0x24, 0x12, 0x12, 0xef, 0xde, 0x16, 0x23, 0x78, 0x5f, 0xea, 0xbc, 0xd1, 0x23}; -// ============================================== shared between the 2 tasks +// ===================================================================== types +// +// Everything the Arduino prototype generator might need. Keep this block above +// the first function definition in the file - see the note in the header. -// Written by loop() (Arduino task), read by the BTstack timer. 32-bit aligned -// int stores on the ESP32 are single instructions, so a torn read is not -// possible; the worst case is one tick of stale data, which we do not care -// about at 40 Hz. struct DesiredState { volatile int leftTrack; // hub 0 port B volatile int rightTrack; // hub 0 port A @@ -108,21 +114,6 @@ struct DesiredState { volatile int leftArm; // hub 1 port C volatile int rightArm; // hub 1 port D }; -static DesiredState gDesired = {0, 0, 0, 0, 0, 0, 0}; - -static volatile bool gHubsReady = false; - -static void clearDesired() { - gDesired.leftTrack = 0; - gDesired.rightTrack = 0; - gDesired.bodyLift = 0; - gDesired.headTilt = 0; - gDesired.headTurn = 0; - gDesired.leftArm = 0; - gDesired.rightArm = 0; -} - -// ============================================================ hub plumbing enum HubState { HUB_IDLE, @@ -145,13 +136,23 @@ struct Hub { uint32_t lastCmdAt; // per-hub rate limit, shared across its ports }; -static Hub gHubs[2]; -static int gActive = -1; // hub currently mid-discovery, -1 if none - struct PortThrottle { int lastPower; uint32_t lastSentAt; }; + +// ================================================================== globals + +// gDesired is written by loop() (Arduino task) and read by the BTstack timer. +// 32-bit aligned int stores on the ESP32 are single instructions, so a torn +// read is not possible; the worst case is one tick of stale data, which we do +// not care about at 40 Hz. +static DesiredState gDesired = {0, 0, 0, 0, 0, 0, 0}; +static volatile bool gHubsReady = false; + +static Hub gHubs[2]; +static int gActive = -1; // hub currently mid-discovery, -1 if none + static PortThrottle gThrottle[7] = {{999, 0}, {999, 0}, {999, 0}, {999, 0}, {999, 0}, {999, 0}, {999, 0}}; @@ -159,11 +160,24 @@ static btstack_packet_callback_registration_t gHciRegistration; static btstack_context_callback_registration_t gBootstrapRegistration; static btstack_timer_source_t gLegoTimer; -// ============================================================== gamepad io - ControllerPtr myControllers[BP32_MAX_CONTROLLERS]; static bool gDiscoveryEnabled = false; +// ============================================================== gamepad io +// +// First function definition in the file. Everything above this line is types +// and data, which is what makes the generated prototypes compile. + +static void clearDesired() { + gDesired.leftTrack = 0; + gDesired.rightTrack = 0; + gDesired.bodyLift = 0; + gDesired.headTilt = 0; + gDesired.headTurn = 0; + gDesired.leftArm = 0; + gDesired.rightArm = 0; +} + void onConnectedController(ControllerPtr ctl) { for (int i = 0; i < BP32_MAX_CONTROLLERS; i++) { if (myControllers[i] == nullptr) {