From 1ca4bda4412f57ff68ff6f1a69247817145bfb78 Mon Sep 17 00:00:00 2001 From: jessikitty Date: Tue, 1 Sep 2026 23:29:24 +1000 Subject: [PATCH] Fix build: hoist all type declarations above the first function The Arduino IDE injects generated prototypes immediately before the first function definition in the sketch. clearDesired() sat above struct Hub and struct PortThrottle, so prototypes referencing those types were emitted before the types existed. Moved all structs and enums into one block at the top, and documented the constraint in the header so it does not recur. --- ps4_lego_onebrain/ps4_lego_onebrain.ino | 64 +++++++++++++++---------- 1 file changed, 39 insertions(+), 25 deletions(-) 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) {