Bandeau de LEDs contrôlable (puissance, couleur, effet) au travers de Home Assistant
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

599 lines
16 KiB

3 years ago
3 years ago
1 year ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
  1. #include <Arduino.h>
  2. // LED
  3. // TODO : essayer, devrait limiter le flikering
  4. //#define FASTLED_ALLOW_INTERRUPTS 0
  5. #define FASTLED_ESP8266_NODEMCU_PIN_ORDER
  6. #include <FastLED.h>
  7. // WIFI
  8. #include <ESP8266WiFi.h>
  9. // MQTT
  10. #include <PubSubClient.h>
  11. // OTA
  12. #include <ESP8266mDNS.h>
  13. #include <WiFiUdp.h>
  14. #include <ArduinoOTA.h>
  15. // RemoteDebug
  16. #include <RemoteDebug.h>
  17. #include "mqttfastledmenu.h"
  18. // LED
  19. // En déplaçant ces vars dans le .h + init dans le setup, cylon crash au moment du premier retour ?!
  20. float brightness = LED_BRIGHTNESS_DEFAULT;
  21. int color = LED_COLOR_DEFAULT;
  22. int speed = LED_SPEED_DEFAULT;
  23. CRGB leds[LED_NUM];
  24. String ledEffect = LED_EFFECT_ERROR;
  25. boolean ledState = false;
  26. // WIFI
  27. WiFiClient espClient;
  28. // MQTT
  29. char message_buff[100];
  30. PubSubClient client(espClient);
  31. void setup()
  32. {
  33. Serial.begin(SERIAL_SPEED);
  34. Serial.println("\nresetting");
  35. // WIFI
  36. setupWifi();
  37. // OTA
  38. setupOTA();
  39. // RemoteDebug
  40. Debug.begin("chamber");
  41. // LED
  42. /*
  43. brightness = LED_BRIGHTNESS_DEFAULT;
  44. color = LED_COLOR_DEFAULT;
  45. speed = LED_SPEED_DEFAULT;
  46. ledEffect = LED_EFFECT_ERROR;
  47. ledState = false;
  48. */
  49. LEDS.addLeds<LED_CHIPSET,LED_PIN, LED_COLOR_ORDER>(leds, LED_NUM).setCorrection(TypicalSMD5050);
  50. ledBlackAll();
  51. FastLED.setBrightness(brightness);
  52. //////////////////////////////// ColorPalette ///////////////////////////////
  53. currentPalette = RainbowColors_p;
  54. currentBlending = LINEARBLEND;
  55. //////////////////////////////// ColorPalette ///////////////////////////////
  56. // MQTT
  57. client.setServer(MQTT_SERVER, MQTT_PORT);
  58. client.setCallback(callbackMQTT);
  59. testConnectMQTT();
  60. Debug.println("Ready");
  61. /* MQTT
  62. * Il est important de faire un loop avant toute chose,
  63. * afin de récupérer les valeurs provenant du broker mqtt
  64. * et pas démarrer avec de vieilles infos.
  65. * Il faut un certains nombres de tentative pour tout récuperer.
  66. */
  67. for (short int i = 0; i < 10; i++) {
  68. delay(200);
  69. client.loop();
  70. }
  71. Debug.println("End of setup");
  72. }
  73. // OTA
  74. void setupOTA()
  75. {
  76. ArduinoOTA.setHostname("mqttfastledmenu"); // on donne une petit nom a notre module
  77. ArduinoOTA.setPassword(OTA_PASSWORD);
  78. ArduinoOTA.onStart([]() {
  79. Debug.println("OTA Starting");
  80. Serial.println("OTA Starting");
  81. });
  82. ArduinoOTA.onEnd([]() {
  83. Debug.println("\nOTA End");
  84. Serial.println("\nOTA End");
  85. });
  86. ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
  87. Debug.printf("OTA Progress: %u%%\r", (progress / (total / 100)));
  88. Serial.printf("OTA Progress: %u%%\r", (progress / (total / 100)));
  89. });
  90. ArduinoOTA.onError([](ota_error_t error) {
  91. Serial.printf("OTA Error[%u]: ", error);
  92. Debug.printf("OTA Error[%u]: ", error);
  93. if (error == OTA_AUTH_ERROR) {
  94. Serial.println("Auth Failed");
  95. Debug.println("Auth Failed");
  96. } else if (error == OTA_BEGIN_ERROR) {
  97. Serial.println("Begin Failed");
  98. Debug.println("Begin Failed");
  99. } else if (error == OTA_CONNECT_ERROR) {
  100. Serial.println("Connect Failed");
  101. Debug.println("Connect Failed");
  102. } else if (error == OTA_RECEIVE_ERROR) {
  103. Serial.println("Receive Failed");
  104. Debug.println("Receive Failed");
  105. } else if (error == OTA_END_ERROR) {
  106. Serial.println("End Failed");
  107. Debug.println("End Failed");
  108. }
  109. });
  110. ArduinoOTA.begin();
  111. }
  112. // WIFI
  113. void setupWifi()
  114. {
  115. Serial.print("Connexion a ");
  116. Serial.print(WIFI_SSID);
  117. WiFi.mode(WIFI_STA);
  118. WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  119. while (WiFi.status() != WL_CONNECTED) {
  120. delay(500);
  121. Serial.print(".");
  122. }
  123. Serial.println(" OK");
  124. Serial.print("IP : ");
  125. Serial.println(WiFi.localIP());
  126. }
  127. // MQTT
  128. void testConnectMQTT()
  129. {
  130. while (!client.connected()) {
  131. Debug.print("Connexion au serveur MQTT... ");
  132. // TODO : changer ça pour un truc specifique sinon provoque des erreurs mqtt
  133. if (client.connect(MQTT_CLIENT, MQTT_USER, MQTT_PASS)) {
  134. Debug.print("OK\nSend Current State");
  135. mqttSendState();
  136. mqttSendSpeedState();
  137. mqttSendBrightnessState();
  138. mqttSendEffectState();
  139. mqttSendColorState();
  140. Debug.print("OK\nSubscribe");
  141. client.subscribe(MQTT_LED_COMMAND);
  142. client.subscribe(MQTT_LED_EFFECT_COMMAND);
  143. client.subscribe(MQTT_LED_BRIGHTNESS_COMMAND);
  144. client.subscribe(MQTT_LED_SPEED_COMMAND);
  145. client.subscribe(MQTT_LED_COLOR_COMMAND);
  146. Debug.println(" OK");
  147. } else {
  148. Debug.print("KO, erreur : ");
  149. Debug.print(client.state());
  150. Debug.println(", on attend 5 secondes avant de recommencer");
  151. delay(5000);
  152. }
  153. }
  154. }
  155. // Déclenche les actions à la réception d'un message
  156. void callbackMQTT(char* topic, byte* payload, unsigned int length)
  157. {
  158. String stopic = String(topic);
  159. unsigned int i = 0;
  160. for(i = 0; i < length; i++) {
  161. message_buff[i] = payload[i];
  162. }
  163. message_buff[i] = '\0';
  164. String msgString = String(message_buff);
  165. Debug.print("Received [" + stopic + "] : ");
  166. Debug.println(msgString);
  167. if (stopic == MQTT_LED_COMMAND) {
  168. if (msgString == "ON") {
  169. ledState = true;
  170. } else {
  171. ledState = false;
  172. ledBlackAll();
  173. }
  174. mqttSendState();
  175. } else if (stopic == MQTT_LED_EFFECT_COMMAND) {
  176. // Si on ne repasse pas tout à noir, cela peut faire des effets surprenants
  177. ledBlackAll();
  178. ledEffect = msgString;
  179. mqttSendEffectState();
  180. } else if (stopic == MQTT_LED_BRIGHTNESS_COMMAND) {
  181. brightness = msgString.toInt();
  182. FastLED.setBrightness(brightness);
  183. mqttSendBrightnessState();
  184. } else if (stopic == MQTT_LED_COLOR_COMMAND) {
  185. // Sample : 134,168,255
  186. int red = msgString.substring(0, msgString.indexOf(',')).toInt();
  187. int green = msgString.substring(msgString.indexOf(',') + 1, msgString.lastIndexOf(',')).toInt();
  188. int blue = msgString.substring(msgString.lastIndexOf(',') + 1).toInt();
  189. color=((red <<16)|(green <<8)|blue);
  190. mqttSendColorState();
  191. } else if (stopic == MQTT_LED_SPEED_COMMAND) {
  192. speed = msgString.toInt();
  193. mqttSendSpeedState();
  194. }
  195. }
  196. void mqttSendState()
  197. {
  198. client.publish(MQTT_LED_STATE, (ledState) ? "ON": "OFF", true);
  199. }
  200. void mqttSendEffectState()
  201. {
  202. char buff[ledEffect.length() + 1];
  203. ledEffect.toCharArray(buff, ledEffect.length() + 1);
  204. client.publish(MQTT_LED_EFFECT_STATE, buff, true);
  205. }
  206. void mqttSendBrightnessState()
  207. {
  208. char buff[4];
  209. itoa(brightness, buff, 10);
  210. client.publish(MQTT_LED_BRIGHTNESS_STATE, buff, true);
  211. }
  212. void mqttSendSpeedState()
  213. {
  214. char buff[4];
  215. itoa(speed, buff, 10);
  216. client.publish(MQTT_LED_SPEED_STATE, buff, true);
  217. }
  218. void mqttSendColorState()
  219. {
  220. int red = color>>16 & 0xFF;
  221. int green = color>>8 & 0xFF;
  222. int blue = color & 0xFF;
  223. char buff[12];
  224. sprintf(buff, "%i,%i,%i", red, green, blue);
  225. client.publish(MQTT_LED_COLOR_STATE, buff, true);
  226. }
  227. // LED
  228. /**
  229. * Coupe tout le strip de led.
  230. */
  231. void ledBlackAll()
  232. {
  233. FastLED.clear();
  234. FastLED.show();
  235. }
  236. /**
  237. * Effet Cylon : défilement d'une simple led sur le strip aller/retour.
  238. * Pour faire plus sympas on ajoute une lueur autour, avec une lumière atténué.
  239. */
  240. void ledCylon()
  241. {
  242. for (int i = 0; i < LED_NUM; i++) {
  243. client.loop();
  244. if (ledEffect != LED_EFFECT_CYLON) {
  245. return;
  246. }
  247. if ((i - 3) >= 0) {
  248. leds[i - 3] = CRGB::Black;
  249. }
  250. if ((i - 2) >= 0) {
  251. /*
  252. * Se lit 128/256 d'intensité lumineuse actuelle
  253. * https://github.com/FastLED/FastLED/wiki/Pixel-reference#dimming-and-brightening-colors
  254. */
  255. leds[i - 2] = color;
  256. leds[i - 2].fadeLightBy(220);
  257. }
  258. if ((i - 1) >= 0) {
  259. leds[i - 1] = color;
  260. leds[i - 1].fadeLightBy(200);
  261. }
  262. leds[i] = color;
  263. if ((i + 1) <= LED_NUM) {
  264. leds[i + 1] = color;
  265. // Je suis volontairement un peu moins puissant sur l'avant
  266. // pour donner un effet de trainée sur l'arrière
  267. leds[i + 1].fadeLightBy(249);
  268. }
  269. FastLED.delay(1000 / speed);
  270. }
  271. // Il faut nettoyer certaines cases avant la prochaine loop
  272. if ((LED_NUM - 2) >= 0) {
  273. leds[LED_NUM - 2] = color;
  274. leds[LED_NUM - 2].fadeLightBy(220);
  275. }
  276. if ((LED_NUM - 1) >= 0 ) {
  277. leds[LED_NUM - 1] = CRGB::Black;
  278. }
  279. FastLED.show();
  280. // led[0] et led[255] sont gérées par la loop précédante
  281. for (int i = LED_NUM - 1; i >= 0; i--) {
  282. client.loop();
  283. if (ledEffect != LED_EFFECT_CYLON) {
  284. return;
  285. }
  286. if ((i - 1) >= 0) {
  287. leds[i - 1] = color;
  288. leds[i - 1].fadeLightBy(249);
  289. }
  290. leds[i] = color;
  291. if ((i + 1) <= LED_NUM) {
  292. leds[i + 1] = color;
  293. leds[i + 1].fadeLightBy(200);
  294. }
  295. if ((i + 2) <= LED_NUM) {
  296. leds[i + 2] = color;
  297. leds[i + 2].fadeLightBy(220);
  298. }
  299. if ((i + 3) <= LED_NUM) {
  300. leds[i + 3] = CRGB::Black;
  301. }
  302. FastLED.delay(1000 / speed);
  303. }
  304. // Il faut nettoyer certaines cases avant la prochaine loop
  305. if (1 <= LED_NUM) {
  306. leds[1] = color;
  307. leds[1].fadeLightBy(220);
  308. }
  309. if (2 <= LED_NUM) {
  310. leds[2] = CRGB::Black;
  311. }
  312. FastLED.show();
  313. }
  314. /**
  315. * Utilise pour indiquer une erreur sur la reception de l'effet.
  316. */
  317. void ledError()
  318. {
  319. for (int i = 0; i < LED_NUM; i++) {
  320. if ((i % 2) == 0) {
  321. leds[i] = CRGB::Black;
  322. } else {
  323. leds[i] = CRGB::Red;
  324. }
  325. }
  326. FastLED.delay(1000 / speed);
  327. }
  328. /**
  329. * Affiche une couleur de manière uniforme sur le strip.
  330. * Pour éviter un éclairage basique, on applique un breath qui permet
  331. * de faire respirer la couleur (brightness).
  332. */
  333. void ledBreathing()
  334. {
  335. // Source : http://sean.voisen.org/blog/2011/10/breathing-led-with-arduino/
  336. // Voic la version avec la gestion du speed, mais je ne suis pas convaincu
  337. //float breath = (exp(sin(millis() / 2000.0 * map(speed, 0, 255, 50, 300)/100 * PI)) - 0.3678794) * 108.4;
  338. float breath = (exp(sin(millis() / 4000.0 * PI)) - 0.3678794) * 108.4;
  339. // J'ai essayé de mapper breath sur 3;brightness pour ne pas eteindre les leds,
  340. // mais l'effet est plus saccadé
  341. fill_solid(leds, LED_NUM, color);
  342. FastLED.setBrightness(breath);
  343. FastLED.show();
  344. }
  345. ///////////////////// FastLED-3.1.5/examples/ColorPalette /////////////////////
  346. void ledColorPattern()
  347. {
  348. ChangePalettePeriodically();
  349. static uint8_t startIndex = 0;
  350. startIndex = startIndex + 1; /* motion speed */
  351. FillLEDsFromPaletteColors(startIndex);
  352. FastLED.delay(1000 / speed);
  353. }
  354. void FillLEDsFromPaletteColors(uint8_t colorIndex)
  355. {
  356. uint8_t brightness = 255;
  357. for( int i = 0; i < LED_NUM; i++) {
  358. leds[i] = ColorFromPalette(
  359. currentPalette,
  360. colorIndex,
  361. brightness,
  362. currentBlending
  363. );
  364. colorIndex += 3;
  365. }
  366. }
  367. // There are several different palettes of colors demonstrated here.
  368. //
  369. // FastLED provides several 'preset' palettes: RainbowColors_p, RainbowStripeColors_p,
  370. // OceanColors_p, CloudColors_p, LavaColors_p, ForestColors_p, and PartyColors_p.
  371. //
  372. // Additionally, you can manually define your own color palettes, or you can write
  373. // code that creates color palettes on the fly. All are shown here.
  374. void ChangePalettePeriodically()
  375. {
  376. uint8_t secondHand = (millis() / 1000) % 60;
  377. static uint8_t lastSecond = 99;
  378. if( lastSecond != secondHand) {
  379. lastSecond = secondHand;
  380. /*
  381. if (secondHand == 0) { currentPalette = RainbowColors_p; currentBlending = LINEARBLEND; }
  382. if (secondHand == 10) { currentPalette = RainbowStripeColors_p; currentBlending = NOBLEND; }
  383. if (secondHand == 15) { currentPalette = RainbowStripeColors_p; currentBlending = LINEARBLEND; }
  384. if (secondHand == 20) { SetupPurpleAndGreenPalette(); currentBlending = LINEARBLEND; }
  385. if (secondHand == 25) { SetupTotallyRandomPalette(); currentBlending = LINEARBLEND; }
  386. if (secondHand == 30) { SetupBlackAndWhiteStripedPalette(); currentBlending = NOBLEND; }
  387. if (secondHand == 35) { SetupBlackAndWhiteStripedPalette(); currentBlending = LINEARBLEND; }
  388. if (secondHand == 40) { currentPalette = CloudColors_p; currentBlending = LINEARBLEND; }
  389. if (secondHand == 45) { currentPalette = PartyColors_p; currentBlending = LINEARBLEND; }
  390. if (secondHand == 50) { currentPalette = myRedWhiteBluePalette_p; currentBlending = NOBLEND; }
  391. if (secondHand == 55) { currentPalette = myRedWhiteBluePalette_p; currentBlending = LINEARBLEND; }
  392. */
  393. if (secondHand == 0) { SetupPurpleAndGreenPalette(); currentBlending = LINEARBLEND; }
  394. if (secondHand == 10) { SetupBlackAndWhiteStripedPalette(); currentBlending = LINEARBLEND; }
  395. if (secondHand == 30) { currentPalette = CloudColors_p; currentBlending = LINEARBLEND; }
  396. if (secondHand == 40) { currentPalette = myRedWhiteBluePalette_p; currentBlending = LINEARBLEND; }
  397. }
  398. }
  399. /*
  400. // This function fills the palette with totally random colors.
  401. void SetupTotallyRandomPalette()
  402. {
  403. for (int i = 0; i < 16; i++) {
  404. currentPalette[i] = CHSV(random8(), 255, random8());
  405. }
  406. }
  407. */
  408. // This function sets up a palette of black and white stripes,
  409. // using code. Since the palette is effectively an array of
  410. // sixteen CRGB colors, the various fill_* functions can be used
  411. // to set them up.
  412. void SetupBlackAndWhiteStripedPalette()
  413. {
  414. // 'black out' all 16 palette entries...
  415. fill_solid(currentPalette, 16, CRGB::Black);
  416. // and set every fourth one to white.
  417. currentPalette[0] = CRGB::White;
  418. currentPalette[4] = CRGB::White;
  419. currentPalette[8] = CRGB::White;
  420. currentPalette[12] = CRGB::White;
  421. }
  422. // This function sets up a palette of purple and green stripes.
  423. void SetupPurpleAndGreenPalette()
  424. {
  425. CRGB purple = CHSV(HUE_PURPLE, 255, 255);
  426. CRGB green = CHSV(HUE_GREEN, 255, 255);
  427. CRGB black = CRGB::Black;
  428. currentPalette = CRGBPalette16(
  429. green, green, black, black,
  430. purple, purple, black, black,
  431. green, green, black, black,
  432. purple, purple, black, black
  433. );
  434. }
  435. ///////////////////// FastLED-3.1.5/examples/ColorPalette /////////////////////
  436. /////////////////// FastLED-3.1.5/examples/ColorTemperature ///////////////////
  437. void colorTemp()
  438. {
  439. // draw a generic, no-name rainbow
  440. static uint8_t starthue = 0;
  441. fill_rainbow(leds + 5, LED_NUM - 5, --starthue, 20);
  442. // Choose which 'color temperature' profile to enable.
  443. uint8_t secs = (millis() / 1000) % (DISPLAYTIME * 2);
  444. if (secs < DISPLAYTIME) {
  445. FastLED.setTemperature(TEMPERATURE_1 ); // first temperature
  446. leds[0] = TEMPERATURE_1; // show indicator pixel
  447. } else {
  448. FastLED.setTemperature(TEMPERATURE_2 ); // second temperature
  449. leds[0] = TEMPERATURE_2; // show indicator pixel
  450. }
  451. // Black out the LEDs for a few secnds between color changes
  452. // to let the eyes and brains adjust
  453. if((secs % DISPLAYTIME) < BLACKTIME) {
  454. memset8(leds, 0, LED_NUM * sizeof(CRGB));
  455. }
  456. FastLED.show();
  457. FastLED.delay(8);
  458. }
  459. /////////////////// FastLED-3.1.5/examples/ColorTemperature ///////////////////
  460. //////////////////////// FastLED-3.1.5/examples/Fire202 ///////////////////////
  461. void fire()
  462. {
  463. // Array of temperature readings at each simulation cell
  464. static byte heat[LED_NUM];
  465. // Step 1. Cool down every cell a little
  466. for (int i = 0; i < LED_NUM; i++) {
  467. heat[i] = qsub8(heat[i], random8(0, ((COOLING * 10) / LED_NUM) + 2));
  468. }
  469. // Step 2. Heat from each cell drifts 'up' and diffuses a little
  470. for (int k= LED_NUM - 1; k >= 2; k--) {
  471. heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2] ) / 3;
  472. }
  473. // Step 3. Randomly ignite new 'sparks' of heat near the bottom
  474. if (random8() < SPARKING ) {
  475. int y = random8(7);
  476. heat[y] = qadd8(heat[y], random8(160,255));
  477. }
  478. // Step 4. Map from heat cells to LED colors
  479. for (int j = 0; j < LED_NUM; j++) {
  480. CRGB color = HeatColor( heat[j]);
  481. int pixelnumber;
  482. if (gReverseDirection) {
  483. pixelnumber = (LED_NUM - 1) - j;
  484. } else {
  485. pixelnumber = j;
  486. }
  487. leds[pixelnumber] = color;
  488. }
  489. FastLED.delay(1000 / speed);
  490. }
  491. //////////////////////// FastLED-3.1.5/examples/Fire202 ///////////////////////
  492. void loop() {
  493. // OTA
  494. ArduinoOTA.handle();
  495. // RemoteDebug
  496. Debug.handle();
  497. // MQTT
  498. testConnectMQTT();
  499. client.loop();
  500. // LED
  501. if (!ledState) {
  502. FastLED.delay(1000);
  503. } else {
  504. if (ledEffect == LED_EFFECT_CYLON) {
  505. ledCylon();
  506. } else if (ledEffect == LED_EFFECT_BREATHING) {
  507. ledBreathing();
  508. } else if (ledEffect == LED_EFFECT_COLORPATTERN) {
  509. ledColorPattern();
  510. } else if (ledEffect == LED_EFFECT_COLORTEMP) {
  511. colorTemp();
  512. } else if (ledEffect == LED_EFFECT_FIRE) {
  513. fire();
  514. } else {
  515. ledError();
  516. }
  517. }
  518. }