· 5 months ago · Apr 21, 2025, 11:25 PM
1#include <WiFi.h>
2#include <WiFiClient.h>
3#include <BlynkSimpleEsp32_SSL.h>
4
5// Your WiFi credentials.
6// Set password to "" for open networks.
7char ssid[] = "YourNetworkName";
8char pass[] = "YourPassword";
9
10
11const bool GAMMA_CORRECT = true; // set to false to disable gamma correction
12byte rgb_light_control_mode = 0;
13byte animation_fade_strobe = 0;
14
15// This table remaps linear input RGB values to
16// nonlinear gamma-corrected output values for human eyes.
17// To use/read it: result = pgm_read_byte(&gamma8[color]);
18// Source: https://learn.adafruit.com/led-tricks-gamma-correction/the-quick-fix
19const uint8_t PROGMEM gamma8[] = {
20 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
21 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,
22 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2,
23 2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5,
24 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10,
25 10, 10, 11, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16,
26 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 24, 24, 25,
27 25, 26, 27, 27, 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 35, 36,
28 37, 38, 39, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 50,
29 51, 52, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 66, 67, 68,
30 69, 70, 72, 73, 74, 75, 77, 78, 79, 81, 82, 83, 85, 86, 87, 89,
31 90, 92, 93, 95, 96, 98, 99,101,102,104,105,107,109,110,112,114,
32 115,117,119,120,122,124,126,127,129,131,133,135,137,138,140,142,
33 144,146,148,150,152,154,156,158,160,162,164,167,169,171,173,175,
34 177,180,182,184,186,189,191,193,196,198,200,203,205,208,210,213,
35 215,218,220,223,225,228,231,233,236,239,241,244,247,249,252,255
36};
37
38
39// Makes sure you assign PWM capable pins
40const byte PIN_RED = 14;
41const byte PIN_GREEN = 12;
42const byte PIN_BLUE = 13;
43
44int rgb_red = 0;
45int rgb_green = 0;
46int rgb_blue = 0;
47int rgb_brightness = 0;
48
49//uncomment next line if using a Common Anode LED
50//#define COMMON_ANODE
51const uint8_t MAX_COLOR_VALUE = 255;
52
53struct HSL_t {
54 double h; // angle in degrees
55 double s; // saturation % between 0 and 1
56 double l; // luminance % between 0 and 1
57};
58
59struct RGB_t {
60 int r; // red, 0 to 255
61 int g; // green, 0 to 255
62 int b; // blue, 0 to 255
63};
64
65
66HSL_t rgb2hsl(uint8_t red, uint8_t green, uint8_t blue) {
67 // Returns HSL value calculated from red, green, blue values where:
68 // h = hue in degrees, 0 to 360
69 // s = saturation, 0.0 to 1.0
70 // l = luminance, 0.0 to 1.0
71 HSL_t result;
72
73 // Scale red, blue, green from integer range 0-255 to float 0-1
74 double r = static_cast<double>(red / 255.0);
75 double g = static_cast<double>(green / 255.0);
76 double b = static_cast<double>(blue / 255.0);
77
78 float c_max = max(max(r,g),b);
79 float c_min = min(min(r,g),b);
80
81 // Luminance
82 result.l = (c_max + c_min) / 2.0;
83
84 // Saturation
85 if (c_max == c_min) {
86 // No saturation, achromatic
87 result.h = 0.0;
88 result.s = 0.0;
89 return result;
90 } else {
91 if (result.l > 0.5) {
92 // Saturation = ( max-min)/(2.0-max-min)
93 result.s = (c_max - c_min) / (2.0 - c_max - c_min);
94 } else {
95 // Saturation = (max-min)/(max+min)
96 result.s = (c_max - c_min) / (c_max + c_min);
97 }
98 }
99
100 // Hue
101 if (r == max(max(r,g),b)) {
102 // red is the maximum value
103 result.h = (g - b) / (c_max - c_min);
104 } else if (g == max(max(r,g),b)) {
105 // green is the maximum value
106 result.h = 2.0 + (b - r) / (c_max - c_min);
107 } else if (b == max(max(r,g),b)) {
108 // blue is the maximum value
109 result.h = 4.0 + (r - g) / (c_max - c_min);
110 } else {
111 // ERROR
112 result.h = 0.0;
113 }
114
115 result.h *= 60.0;
116 if (result.h < 0) {
117 result.h += 360.0;
118 }
119
120 return result;
121
122} //rgb2hsl()
123
124
125RGB_t hsl2rgb(double h, double s, double l) {
126 RGB_t result;
127 float t_1 = 0.0;
128 float t_2 = 0.0;
129 float t_r = 0.0;
130 float t_g = 0.0;
131 float t_b = 0.0;
132
133 if (h == 0.0 && s == 0.0) {
134 // No saturation, so it is a shade of gray.
135 // Covert the luminance and set r, g, b to that level.
136 result.r = static_cast<int>(l * 255.0);
137 result.g = static_cast<int>(l * 255.0);
138 result.b = static_cast<int>(l * 255.0);
139 } else {
140 if (l < 0.5) {
141 t_1 = l * (1.0 + s);
142 } else {
143 t_1 = l + s - l * s;
144 }
145
146 t_2 = 2.0 * l - t_1;
147
148 // convert hue in degress to 1 by diving by 360 degrees
149 h /= 360.0;
150
151 t_r = h + (1.0/3.0);
152 if (t_r < 0.0) {
153 t_r += 1.0;
154 }
155 if (t_r > 1.0) {
156 t_r -= 1.0;
157 }
158
159 t_g = h;
160 if (t_g < 0.0) {
161 t_g += 1.0;
162 }
163 if (t_g > 1.0) {
164 t_g -= 1.0;
165 }
166
167 t_b = h - (1.0/3.0);
168 if (t_b < 0.0) {
169 t_b += 1.0;
170 }
171 if (t_b > 1.0) {
172 t_b -= 1.0;
173 }
174
175 // red
176 if (6.0*t_r < 1.0) {
177 result.r = static_cast<int>(round((t_2 + (t_1 - t_2) * 6.0 * t_r) * 255.0));
178 } else if (2.0*t_r < 1.0) {
179 result.r = static_cast<int>(round(t_1 * 255.0));
180 } else if (3.0*t_r < 2.0) {
181 result.r = static_cast<int>(round((t_2 + (t_1 - t_2) * (2.0/3.0 - t_r) * 6.0) * 255.0));
182 } else if (3.0*t_r > 2.0) {
183 result.r = static_cast<int>(round(t_2 * 255.0));
184 }
185
186 // green
187 if (6.0*t_g < 1.0) {
188 result.g = static_cast<int>(round((t_2 + (t_1 - t_2) * 6.0 * t_g) * 255.0));
189 } else if (2.0*t_g < 1.0) {
190 result.g = static_cast<int>(round(t_1 * 255.0));
191 } else if (3.0*t_g < 2.0) {
192 result.g = static_cast<int>(round((t_2 + (t_1 - t_2) * (2.0/3.0 - t_g) * 6.0) * 255.0));
193 } else if (3.0*t_g > 2.0) {
194 result.g = static_cast<int>(round(t_2 * 255.0));
195 }
196
197 // blue
198 if (6.0*t_b < 1.0) {
199 result.b = static_cast<int>(round((t_2 + (t_1 - t_2) * 6.0 * t_b) * 255.0));
200 } else if (2.0*t_b < 1.0) {
201 result.b = static_cast<int>(round(t_1 * 255.0));
202 } else if (3.0*t_b < 2.0) {
203 result.b = static_cast<int>(round((t_2 + (t_1 - t_2) * (2.0/3.0 - t_b) * 6.0) * 255.0));
204 } else if (3.0*t_b > 2.0) {
205 result.b = static_cast<int>(round(t_2 * 255.0));
206 }
207
208 }
209
210 return result;
211
212} // hsl2rgb()
213
214
215void setRGB(uint8_t red, uint8_t green, uint8_t blue, uint8_t luminance, bool gamma_correct) {
216
217 // Note: the Blynk RGB Light Control widget color values
218 // assume the use of a common cathode RGB.
219
220 bool white_mode = false;
221
222 if (red > 218 && green > 218 && blue > 168) {
223 // Possibly in White Mode
224 white_mode = true;
225 }
226
227 if (MAX_COLOR_VALUE == 255 && gamma_correct == true && white_mode == false) {
228 // Gamma correction is applied to compensate for the non-linearity of
229 // the human eye perception of color using pgm_read_byte(&gamma8[color]);
230 red = pgm_read_byte(&gamma8[red]);
231 green = pgm_read_byte(&gamma8[green]);
232 blue = pgm_read_byte(&gamma8[blue]);
233 }
234
235 // Scale RGB color values based on the luminance setting.
236 // Note that above 50% luminance, the color changes a lot.
237 // Therefore, scale everything down by 1/2, so 100% = 50%.
238 HSL_t hsl = rgb2hsl(red, green, blue);
239 // hsl.l range is 0.0 to 1.0
240 // luminance range is 0 to 100
241 // Scale luminance from 0 to 100 to 0.0 to 1.0
242 hsl.l = static_cast<double>(luminance)/100.0;
243 // Scale luminance further by 1/2 to keep color true
244 hsl.l /= 2.0;
245 RGB_t rgb = hsl2rgb(hsl.h, hsl.s, hsl.l);
246 red = rgb.r; green = rgb.g; blue=rgb.b;
247
248 #ifdef COMMON_ANODE
249 red = MAX_COLOR_VALUE - red;
250 green = MAX_COLOR_VALUE - green;
251 blue = MAX_COLOR_VALUE - blue;
252 #else
253 // common cathode RGB
254 #endif
255
256 // Write the RGB values to the RGB attached to the hardware
257 analogWrite(PIN_RED, red);
258 analogWrite(PIN_GREEN, green);
259 analogWrite(PIN_BLUE, blue);
260} // setRGB()
261
262RGB_t rgb_color_white_mode;
263uint8_t animation_pattern_count = 0; // either 3 or 2 when known
264RGB_t rgb_animation_left;
265RGB_t rgb_animation_right;
266RGB_t rgb_animation_top;
267
268/////////////////////////////////////////////////////////////////////////
269
270
271BLYNK_WRITE(V10) {
272 // V10 integer, BUTTON, 0 or 1
273 // Turn RGB on/off based on the BUTTON value
274 int btn_state = param.asInt();
275 if (btn_state == 1) {
276 // do nothing with rgb_brightness
277 } else {
278 rgb_brightness = 0;
279 }
280 Serial.print("V10 = '");
281 Serial.print(btn_state);
282 Serial.println("' BUTTON..");
283 setRGB(rgb_red, rgb_green, rgb_blue, rgb_brightness, GAMMA_CORRECT);
284} // BLYNK_WRITE(V10)
285
286
287BLYNK_WRITE(V2) {
288 // Called when datastream for virtual pin V2 is updated
289 // V2 string, COLOR
290
291 Serial.println("");
292 //Serial.println("All V2 array items:");
293 byte param_items_count = 0;
294 for (auto i = param.begin(); i < param.end(); ++i) {
295 //Serial.print("\t");
296 //Serial.print(param_items_count);
297 //Serial.print(" = '");
298 //Serial.print(i.asString());
299 //Serial.println("'");
300 if (param_items_count == 3) {
301 String button_mode = i.asString();
302 if (button_mode == "true") {
303 rgb_light_control_mode = 1; // Color Mode
304 } else if (button_mode == "false") {
305 rgb_light_control_mode = 2; // White Mode
306 }
307 }
308 if (param_items_count == 4) {
309 //Serial.println("\tparam_items_count == 4");
310 rgb_light_control_mode = 3; // Animation mode
311 int fade_strobe = param[4].asInt();
312 if (fade_strobe == 0) {
313 animation_fade_strobe = 2; // fade
314 } else if (fade_strobe == 1) {
315 animation_fade_strobe = 1; // strobe
316 } else {
317 animation_fade_strobe = 0;
318 }
319 }
320 param_items_count++;
321 }
322 //Serial.print("param_items_count = "); Serial.println(param_items_count);
323
324 rgb_color_white_mode.r = param[0].asInt();
325 rgb_color_white_mode.g = param[1].asInt();
326 rgb_color_white_mode.b = param[2].asInt();
327
328 if (param_items_count > 4) {
329 rgb_animation_left.r = param[5].asInt();;
330 rgb_animation_left.g = param[6].asInt();;
331 rgb_animation_left.b = param[7].asInt();;
332 }
333 if (param_items_count == 11) {
334 animation_pattern_count = 2;
335 rgb_animation_top.r = 0;
336 rgb_animation_top.g = 0;
337 rgb_animation_top.b = 0;
338 rgb_animation_right.r = param[8].asInt();;
339 rgb_animation_right.g = param[9].asInt();;
340 rgb_animation_right.b = param[10].asInt();;
341 } else if (param_items_count == 14) {
342 animation_pattern_count = 3;
343 rgb_animation_top.r = param[8].asInt();;
344 rgb_animation_top.g = param[9].asInt();;
345 rgb_animation_top.b = param[10].asInt();;
346 rgb_animation_right.r = param[11].asInt();;
347 rgb_animation_right.g = param[12].asInt();;
348 rgb_animation_right.b = param[13].asInt();;
349 } else {
350 animation_pattern_count = 0;
351 }
352
353 // Using the populated global values for:
354 // rgb_light_control_mode, animation_fade_strobe, rgb_light_control_mode
355 // animation_fade_strobe, rgb_color_white_mode, rgb_animation_left
356 // rgb_animation_right, rgb_animation_top, animation_pattern_count
357 switch (rgb_light_control_mode) {
358 case 0:
359 // button mode unknown
360 Serial.println("RGB Light Control button mode UNKNOWN");
361 break;
362 case 1:
363 // Color Mode
364 Serial.println("Color Mode");
365 Serial.print("R: "); Serial.print(rgb_color_white_mode.r); Serial.print("\tG:"); Serial.print(rgb_color_white_mode.g); Serial.print("\tB:"); Serial.println(rgb_color_white_mode.b);
366 break;
367 case 2:
368 // White Mode
369 Serial.println("White Mode");
370 Serial.print("R: "); Serial.print(rgb_color_white_mode.r); Serial.print("\tG:"); Serial.print(rgb_color_white_mode.g); Serial.print("\tB:"); Serial.println(rgb_color_white_mode.b);
371 break;
372 case 3:
373 // Animation Mode
374 if (animation_fade_strobe == 1) {
375 Serial.println("Animation Mode - strobe");
376 } else if (animation_fade_strobe == 2) {
377 Serial.println("Animation Mode - fade");
378 }
379 if (animation_fade_strobe > 0) {
380 Serial.print("\t"); Serial.print(animation_pattern_count); Serial.println("x Pattern: ");
381 Serial.print("\tLeft\tR: "); Serial.print(rgb_animation_left.r); Serial.print("\tG:"); Serial.print(rgb_animation_left.g); Serial.print("\tB:"); Serial.println(rgb_animation_left.b);
382 if (animation_pattern_count > 2) {
383 Serial.print("\tTop\tR: "); Serial.print(rgb_animation_top.r); Serial.print("\tG:"); Serial.print(rgb_animation_top.g); Serial.print("\tB:"); Serial.println(rgb_animation_top.b);
384 }
385 Serial.print("\tRight\tR: "); Serial.print(rgb_animation_right.r); Serial.print("\tG:"); Serial.print(rgb_animation_right.g); Serial.print("\tB:"); Serial.println(rgb_animation_right.b);
386 }
387 break;
388 } // switch
389 Serial.println("");
390
391 setRGB(rgb_color_white_mode.r, rgb_color_white_mode.g, rgb_color_white_mode.b, rgb_brightness, GAMMA_CORRECT);
392
393} // BLYNK_WRITE(V2)
394
395
396BLYNK_WRITE(V6) {
397 // V6 integer, BRIGHTNESS. 0 to 100
398 rgb_brightness = param.asInt();
399 Serial.print("V6 = '");
400 Serial.print(rgb_brightness);
401 Serial.println("' BRIGHTNESS..");
402 setRGB(rgb_red, rgb_green, rgb_blue, rgb_brightness, GAMMA_CORRECT);
403} // BLYNK_WRITE(V6)
404
405
406BLYNK_WRITE(V5) {
407 // V5 integer, ANIMATION SPEED, 0 to 10000
408 int animation_speed = param.asInt();
409 Serial.print("V5 = '");
410 Serial.print(animation_speed);
411 Serial.println("' ANIMATION SPEED");
412} // BLYNK_WRITE(V5)
413
414BLYNK_CONNECTED() {
415 // Executes every time Blynk is connected to the cloud
416 // (typically only if WiFi connection is lost, otherwise Blynk stays connected)
417 Serial.println("BLYNK_CONNECTED() .. Blynk connected to the cloud.");
418
419 // Request Blynk server to re-send latest values for all virtual pins
420 Blynk.syncAll();
421} // BLYNK_CONNECTED
422
423/////////////////////////////////////////////////////////////////////////
424
425
426void setup() {
427 Serial.begin(115200);
428 while (!Serial) {
429 delay(1);
430 }
431 Serial.println("\nSerial ready");
432
433 // RGB
434 pinMode(PIN_RED, OUTPUT);
435 pinMode(PIN_GREEN, OUTPUT);
436 pinMode(PIN_BLUE, OUTPUT);
437 setRGB(0, 0, 0, 0, GAMMA_CORRECT);
438
439 rgb_color_white_mode.r = 0;
440 rgb_color_white_mode.g = 0;
441 rgb_color_white_mode.b = 0;
442
443 rgb_animation_left.r = 0;
444 rgb_animation_left.g = 0;
445 rgb_animation_left.b = 0;
446
447 rgb_animation_right.r = 0;
448 rgb_animation_right.g = 0;
449 rgb_animation_right.b = 0;
450
451 rgb_animation_top.r = 0;
452 rgb_animation_top.g = 0;
453 rgb_animation_top.b = 0;
454
455 // Try to connect to Blynk Cloud.
456 Serial.println("Connecting to Blynk..");
457 Blynk.begin(BLYNK_AUTH_TOKEN, ssid, pass);
458
459 Serial.println("\nSetup complete\n");
460} // setup()
461
462
463void loop() {
464
465 Blynk.run();
466
467} // loop()