33#include " touch_input.h"
44#include " power_latch.h"
55#include " buzzer_control.h"
6+ #include " display_service.h"
7+ #include " ble_transport.h"
68#include " od_log.h"
79#include < string.h>
810
911#ifdef TARGET_ESP32
12+ #include " wake_button.h"
1013void enterDeepSleep (bool force = false , uint16_t overrideSleepSeconds = 0 );
1114#endif
1215
@@ -235,6 +238,10 @@ static void pollAdcButtons() {
235238 ((l->press_count & 0x0F ) << 3 ) |
236239 ((state & 0x01 ) << 7 ));
237240 if (l->byte_index < 11 ) dynamicreturndata[l->byte_index ] = data;
241+ if (state != 0u ) {
242+ ble.boostAdvertising ();
243+ buttonPressFeedback ();
244+ }
238245 updatemsdata ();
239246 od_log_debug (" ADC btn pin %u adc=%d idx=%d id=%u cnt=%u state=%u" ,
240247 l->pin , adc, btn, l->last_button_id , l->press_count , state);
@@ -330,9 +337,10 @@ static void led_all_off(struct LedConfig* led) {
330337 if (led == NULL ) {
331338 return ;
332339 }
333- bool invertRed = (led->led_flags & 0x01 ) != 0 ;
334- bool invertGreen = (led->led_flags & 0x02 ) != 0 ;
335- bool invertBlue = (led->led_flags & 0x04 ) != 0 ;
340+ bool invertRed = (led->led_flags & OD_LED_FLAG_LED1_INVERT ) != 0 ;
341+ bool invertGreen = (led->led_flags & OD_LED_FLAG_LED2_INVERT ) != 0 ;
342+ bool invertBlue = (led->led_flags & OD_LED_FLAG_LED3_INVERT ) != 0 ;
343+ bool invert4 = (led->led_flags & OD_LED_FLAG_LED4_INVERT ) != 0 ;
336344 if (led->led_1_r != 0xFF ) {
337345 digitalWrite (led->led_1_r , invertRed ? HIGH : LOW );
338346 }
@@ -342,6 +350,9 @@ static void led_all_off(struct LedConfig* led) {
342350 if (led->led_3_b != 0xFF ) {
343351 digitalWrite (led->led_3_b , invertBlue ? HIGH : LOW );
344352 }
353+ if (led->led_4 != 0xFF ) {
354+ digitalWrite (led->led_4 , invert4 ? HIGH : LOW );
355+ }
345356}
346357
347358static void led_stop_internal (bool clear_mode) {
@@ -596,6 +607,88 @@ void handleLedStop(uint8_t* data, uint16_t len) {
596607 sendResponse (successResponse, sizeof (successResponse));
597608}
598609
610+ static void led_channels_on (struct LedConfig * led) {
611+ if (led == NULL ) {
612+ return ;
613+ }
614+ const bool invert1 = (led->led_flags & OD_LED_FLAG_LED1_INVERT ) != 0 ;
615+ const bool invert2 = (led->led_flags & OD_LED_FLAG_LED2_INVERT ) != 0 ;
616+ const bool invert3 = (led->led_flags & OD_LED_FLAG_LED3_INVERT ) != 0 ;
617+ const bool invert4 = (led->led_flags & OD_LED_FLAG_LED4_INVERT ) != 0 ;
618+ if (led->led_1_r != 0xFF ) {
619+ digitalWrite (led->led_1_r , invert1 ? LOW : HIGH );
620+ }
621+ if (led->led_2_g != 0xFF ) {
622+ digitalWrite (led->led_2_g , invert2 ? LOW : HIGH );
623+ }
624+ if (led->led_3_b != 0xFF ) {
625+ digitalWrite (led->led_3_b , invert3 ? LOW : HIGH );
626+ }
627+ if (led->led_4 != 0xFF ) {
628+ digitalWrite (led->led_4 , invert4 ? LOW : HIGH );
629+ }
630+ }
631+
632+ static void ledButtonPressAlert (void ) {
633+ if (s_led.active ) {
634+ return ;
635+ }
636+ struct LedConfig * led = nullptr ;
637+ for (uint8_t i = 0 ; i < globalConfig.led_count ; i++) {
638+ struct LedConfig * cand = &globalConfig.leds [i];
639+ if ((cand->led_flags & OD_LED_FLAG_BUTTON_PRESS ) == 0u ) {
640+ continue ;
641+ }
642+ if (cand->led_1_r == 0xFF && cand->led_2_g == 0xFF &&
643+ cand->led_3_b == 0xFF && cand->led_4 == 0xFF ) {
644+ continue ;
645+ }
646+ led = cand;
647+ break ;
648+ }
649+ if (!led) {
650+ return ;
651+ }
652+ led_channels_on (led);
653+ delay (60 );
654+ led_all_off (led);
655+ }
656+
657+ void buttonPressFeedback (void ) {
658+ ledButtonPressAlert ();
659+ passiveBuzzerButtonPressAlert ();
660+ }
661+
662+ static void publishButtonMsd (ButtonState* btn, uint8_t pressed) {
663+ if (btn == nullptr || btn->byte_index >= 11 ) {
664+ return ;
665+ }
666+ btn->current_state = pressed ? 1u : 0u ;
667+ const uint8_t buttonData = (uint8_t )((btn->button_id & 0x07u ) |
668+ ((btn->press_count & 0x0Fu ) << 3 ) |
669+ ((btn->current_state & 0x01u ) << 7 ));
670+ dynamicreturndata[btn->byte_index ] = buttonData;
671+ ble.boostAdvertising ();
672+ updatemsdata ();
673+ }
674+
675+ #if defined(TARGET_ESP32)
676+ void buttonWakeDeliverSyntheticClick (void ) {
677+ const int idx = wakeButtonFindIndex ();
678+ if (idx < 0 ) {
679+ return ;
680+ }
681+ ButtonState* btn = &buttonStates[idx];
682+ btn->press_count = (uint8_t )((btn->press_count + 1u ) & 0x0Fu );
683+ od_log_info (" Button wake: synthetic click id=%u pin=%u" , btn->button_id , btn->pin );
684+ publishButtonMsd (btn, 1u );
685+ buttonPressFeedback ();
686+ delay (80 );
687+ publishButtonMsd (btn, 0u );
688+ ble.tick ();
689+ }
690+ #endif
691+
599692void processButtonEvents () {
600693 powerButtonPoll ();
601694 pollConfiguredPowerOffButtons (); // no-op unless the board declares a latch
@@ -615,24 +708,13 @@ void processButtonEvents() {
615708 uint8_t logicalState = logicalPressed ? 1 : 0 ;
616709 btn->current_state = logicalState;
617710 od_log_debug (" Button: %u, Press count: %u, Current state: %u" , btn->button_id , btn->press_count , btn->current_state );
618- uint8_t buttonData = (btn->button_id & 0x07 ) |
619- ((btn->press_count & 0x0F ) << 3 ) |
620- ((btn->current_state & 0x01 ) << 7 );
621- if (btn->byte_index < 11 ) {
622- dynamicreturndata[btn->byte_index ] = buttonData;
711+ if (logicalState != 0u ) {
712+ buttonPressFeedback ();
623713 }
714+ publishButtonMsd (btn, logicalState);
624715 }
625- // ORDER IS LOAD-BEARING: boost first, publish second. updatemsdata() ends in
626- // setManufacturerData(), which calls applyAdvInterval() and then restarts
627- // advertising -- so the interval is chosen DURING the publish. Boosting
628- // afterwards set the deadline too late to affect the packet it exists for:
629- // the press went out at the 160 ms slow interval (~1 advertisement in a
630- // typical 230 ms press window, which a passive scanner routinely misses)
631- // while the release 230 ms later got the 20 ms boosted interval, because by
632- // then s_advBoostUntil was set. Net effect: a host saw "not pressed"
633- // reliably and "pressed" almost never.
634- ble.boostAdvertising (); // no-op where the stack has no fast-adv window
635- updatemsdata ();
716+ // boostAdvertising() runs inside publishButtonMsd(); order is load-bearing
717+ // for nRF (interval chosen during setManufacturerData restart).
636718 }
637719}
638720
0 commit comments