1+ #include " wled.h"
2+
3+ #define ESP_NOW_STATE_UNINIT 0
4+ #define ESP_NOW_STATE_ON 1
5+ #define ESP_NOW_STATE_ERROR 2
6+
7+ #define NIGHT_MODE_DEACTIVATED -1
8+ #define NIGHT_MODE_BRIGHTNESS 5
9+
10+ #define WIZMOTE_BUTTON_ON 1
11+ #define WIZMOTE_BUTTON_OFF 2
12+ #define WIZMOTE_BUTTON_NIGHT 3
13+ #define WIZMOTE_BUTTON_ONE 16
14+ #define WIZMOTE_BUTTON_TWO 17
15+ #define WIZMOTE_BUTTON_THREE 18
16+ #define WIZMOTE_BUTTON_FOUR 19
17+ #define WIZMOTE_BUTTON_BRIGHT_UP 9
18+ #define WIZMOTE_BUTTON_BRIGHT_DOWN 8
19+
20+ #ifdef WLED_DISABLE_ESPNOW
21+ void handleRemote (){}
22+ #else
23+
24+ #if !defined(ARDUINO_ARCH_ESP32) && !defined(ESP_OK)
25+ #define ESP_OK 0 // add missing constant for stupid esp8266
26+ #endif
27+
28+ // This is kind of an esoteric strucure because it's pulled from the "Wizmote"
29+ // product spec. That remote is used as the baseline for behavior and availability
30+ // since it's broadly commercially available and works out of the box as a drop-in
31+ typedef struct message_structure {
32+ uint8_t program; // 0x91 for ON button, 0x81 for all others
33+ uint8_t seq[4 ]; // Incremetal sequence number 32 bit unsigned integer LSB first
34+ uint8_t byte5 = 32 ; // Unknown
35+ uint8_t button; // Identifies which button is being pressed
36+ uint8_t byte8 = 1 ; // Unknown, but always 0x01
37+ uint8_t byte9 = 100 ; // Unnkown, but always 0x64
38+
39+ uint8_t byte10; // Unknown, maybe checksum
40+ uint8_t byte11; // Unknown, maybe checksum
41+ uint8_t byte12; // Unknown, maybe checksum
42+ uint8_t byte13; // Unknown, maybe checksum
43+ } message_structure;
44+
45+ static int esp_now_state = ESP_NOW_STATE_UNINIT;
46+ static uint32_t last_seq = -1 ;
47+ static int brightnessBeforeNightMode = NIGHT_MODE_DEACTIVATED;
48+ static message_structure incoming;
49+
50+ // Pulled from the IR Remote logic but reduced to 10 steps with a constant of 3
51+ static const byte brightnessSteps[] = {
52+ 6 , 9 , 14 , 22 , 33 , 50 , 75 , 113 , 170 , 255
53+ };
54+ static const size_t numBrightnessSteps = sizeof (brightnessSteps) / sizeof (uint8_t );
55+
56+ static bool nightModeActive () {
57+ return brightnessBeforeNightMode != NIGHT_MODE_DEACTIVATED;
58+ }
59+
60+ static void activateNightMode () {
61+ brightnessBeforeNightMode = bri;
62+ bri = NIGHT_MODE_BRIGHTNESS;
63+ }
64+
65+ static bool resetNightMode () {
66+ if (!nightModeActive ()) {
67+ return false ;
68+ }
69+ bri = brightnessBeforeNightMode;
70+ brightnessBeforeNightMode = NIGHT_MODE_DEACTIVATED;
71+ return true ;
72+ }
73+
74+ // increment `bri` to the next `brightnessSteps` value
75+ static void brightnessUp () {
76+ if (nightModeActive ()) { return ; }
77+ // dumb incremental search is efficient enough for so few items
78+ for (uint8_t index = 0 ; index < numBrightnessSteps; ++index) {
79+ if (brightnessSteps[index] > bri) {
80+ bri = brightnessSteps[index];
81+ break ;
82+ }
83+ }
84+ }
85+
86+ // decrement `bri` to the next `brightnessSteps` value
87+ static void brightnessDown () {
88+ if (nightModeActive ()) { return ; }
89+ // dumb incremental search is efficient enough for so few items
90+ for (int index = numBrightnessSteps - 1 ; index >= 0 ; --index) {
91+ if (brightnessSteps[index] < bri) {
92+ bri = brightnessSteps[index];
93+ break ;
94+ }
95+ }
96+ }
97+
98+ static void setOn () {
99+ if (resetNightMode ()) {
100+ stateUpdated (CALL_MODE_BUTTON);
101+ }
102+ if (!bri) {
103+ toggleOnOff ();
104+ }
105+ }
106+
107+ static void setOff () {
108+ if (resetNightMode ()) {
109+ stateUpdated (CALL_MODE_BUTTON);
110+ }
111+ if (bri) {
112+ toggleOnOff ();
113+ }
114+ }
115+
116+ static void presetWithFallback (uint8_t presetID, uint8_t effectID, uint8_t paletteID) {
117+ applyPresetWithFallback (presetID, CALL_MODE_BUTTON_PRESET, effectID, paletteID);
118+ }
119+
120+ // Callback function that will be executed when data is received
121+ #ifdef ESP8266
122+ void OnDataRecv (uint8_t * mac, uint8_t *incomingData, uint8_t len) {
123+ #else
124+ void OnDataRecv (const uint8_t * mac, const uint8_t *incomingData, int len) {
125+ #endif
126+
127+ sprintf (last_signal_src, " %02x%02x%02x%02x%02x%02x" ,
128+ mac [0 ], mac [1 ], mac [2 ], mac [3 ], mac [4 ], mac [5 ]);
129+
130+ if (strcmp (last_signal_src, linked_remote) != 0 ) {
131+ USER_PRINT (F (" ESP Now Message Received from Unlinked Sender: " ));
132+ USER_PRINTLN (last_signal_src);
133+ return ;
134+ }
135+
136+ if (len != sizeof (incoming)) {
137+ USER_PRINT (F (" Unknown incoming ESP Now message received of length " ));
138+ USER_PRINTLN (len);
139+ return ;
140+ }
141+
142+ memcpy (&(incoming.program ), incomingData, sizeof (incoming));
143+ uint32_t cur_seq = incoming.seq [0 ] | (incoming.seq [1 ] << 8 ) | (incoming.seq [2 ] << 16 ) | (incoming.seq [3 ] << 24 );
144+
145+ if (cur_seq == last_seq) {
146+ return ;
147+ }
148+
149+
150+ USER_PRINT (F (" \n Incoming ESP Now Packet[" ));
151+ USER_PRINT (cur_seq);
152+ USER_PRINT (F (" ] from sender[" ));
153+ USER_PRINT (last_signal_src);
154+ USER_PRINT (F (" ] button: " ));
155+ USER_PRINTLN (incoming.button );
156+ switch (incoming.button ) {
157+ case WIZMOTE_BUTTON_ON : setOn (); stateUpdated (CALL_MODE_BUTTON); break ;
158+ case WIZMOTE_BUTTON_OFF : setOff (); stateUpdated (CALL_MODE_BUTTON); break ;
159+ case WIZMOTE_BUTTON_ONE : presetWithFallback (1 , FX_MODE_STATIC, 0 ); resetNightMode (); break ;
160+ case WIZMOTE_BUTTON_TWO : presetWithFallback (2 , FX_MODE_BREATH, 0 ); resetNightMode (); break ;
161+ case WIZMOTE_BUTTON_THREE : presetWithFallback (3 , FX_MODE_FIRE_FLICKER, 0 ); resetNightMode (); break ;
162+ case WIZMOTE_BUTTON_FOUR : presetWithFallback (4 , FX_MODE_RAINBOW, 0 ); resetNightMode (); break ;
163+ case WIZMOTE_BUTTON_NIGHT : activateNightMode (); stateUpdated (CALL_MODE_BUTTON); break ;
164+ case WIZMOTE_BUTTON_BRIGHT_UP : brightnessUp (); stateUpdated (CALL_MODE_BUTTON); break ;
165+ case WIZMOTE_BUTTON_BRIGHT_DOWN : brightnessDown (); stateUpdated (CALL_MODE_BUTTON); break ;
166+ default : break ;
167+
168+ }
169+
170+ last_seq = cur_seq;
171+ }
172+
173+ void handleRemote () {
174+ if (enable_espnow_remote) {
175+ if (esp_now_state == ESP_NOW_STATE_UNINIT) {
176+ USER_PRINTLN (F (" \n Initializing ESP_NOW listener!\n " ));
177+ // Init ESP-NOW
178+ if (esp_now_init () != ESP_OK) {
179+ USER_PRINTLN (F (" Error initializing ESP-NOW" ));
180+ esp_now_state = ESP_NOW_STATE_ERROR;
181+ }
182+
183+ #ifdef ESP8266
184+ esp_now_set_self_role (ESP_NOW_ROLE_SLAVE);
185+ #endif
186+
187+ esp_now_register_recv_cb (OnDataRecv);
188+ esp_now_state = ESP_NOW_STATE_ON;
189+ }
190+ } else {
191+ if (esp_now_state == ESP_NOW_STATE_ON) {
192+ USER_PRINTLN (F (" Disabling ESP-NOW Remote Listener" ));
193+ if (esp_now_deinit () != ESP_OK) {
194+ USER_PRINTLN (F (" Error de-initializing ESP-NOW" ));
195+ }
196+ esp_now_state = ESP_NOW_STATE_UNINIT;
197+ } else if (esp_now_state == ESP_NOW_STATE_ERROR) {
198+ // Clear any error states (allows retrying by cycling)
199+ esp_now_state = ESP_NOW_STATE_UNINIT;
200+ }
201+ }
202+ }
203+
204+ #endif
0 commit comments