diff --git a/.gitignore b/.gitignore index f099378..b678c9b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ .vscode/* arduino/mqttfastledmenu/mqttfastledmenu.h -arduino/.pioenvs -arduino/.piolibdeps -arduino/.clang_complete -arduino/.gcc-flags.json +.travis.yml +lib/* +.pioenvs +.piolibdeps +.clang_complete +.gcc-flags.json diff --git a/arduino/mqttfastledmenu/mqttfastledmenu.cpp b/arduino/mqttfastledmenu/mqttfastledmenu.cpp index a8d2fdb..452d08f 100644 --- a/arduino/mqttfastledmenu/mqttfastledmenu.cpp +++ b/arduino/mqttfastledmenu/mqttfastledmenu.cpp @@ -1,17 +1,20 @@ #include -#include "mqttfastledmenu.h" - +// TODO : essayer, devrait limiter le flikering +//#define FASTLED_ALLOW_INTERRUPTS 0 +#define FASTLED_ESP8266_NODEMCU_PIN_ORDER #include #include #include +#include "mqttfastledmenu.h" + // LED int brightness = LED_BRIGHTNESS_DEFAULT; int color = LED_COLOR_DEFAULT; int speed = LED_SPEED_DEFAULT; CRGB leds[LED_NUM]; -String ledEffect = LED_EFFECT_CYLON; +String ledEffect = LED_EFFECT_ERROR; boolean ledState = false; // WIFI @@ -21,6 +24,7 @@ WiFiClient espClient; char message_buff[100]; PubSubClient client(espClient); + void setup() { Serial.begin(SERIAL_SPEED); @@ -33,8 +37,6 @@ void setup() client.setServer(MQTT_SERVER, MQTT_PORT); client.setCallback(callbackMQTT); testConnectMQTT(); - // TODO : ne marche pas comme je le désire : - // au boot il prends les params par défaut, j'aimerais ceux de home assistant // LED LEDS.addLeds(leds, LED_NUM).setCorrection(TypicalSMD5050); @@ -43,16 +45,21 @@ void setup() Serial.println("Ready"); /* MQTT - * Il est important de faire un loop avant toute chose, - * afin de récupérer les valeurs provenant du broker mqtt - * et pas démarrer avec de vieilles infos. - */ - for (short int i = 0; i < 10; i++) { - delay(200); - client.loop(); - } + * Il est important de faire un loop avant toute chose, + * afin de récupérer les valeurs provenant du broker mqtt + * et pas démarrer avec de vieilles infos. + */ + for (short int i = 0; i < 10; i++) { + delay(200); + client.loop(); + } - Serial.println("End of setup"); + //////////////////////////////// ColorPalette /////////////////////////////// + currentPalette = RainbowColors_p; + currentBlending = LINEARBLEND; + //////////////////////////////// ColorPalette /////////////////////////////// + + Serial.println("End of setup"); } // WIFI @@ -122,7 +129,6 @@ void callbackMQTT(char* topic, byte* payload, unsigned int length) // Si on ne repasse pas tout à noir, cela peut faire des effets surprenants ledBlackAll(); ledEffect = msgString; - // TODO : a vraiment tester client.publish(MQTT_LED_EFFECT_STATE, message_buff, true); } else if (stopic == MQTT_LED_BRIGHTNESS_COMMAND) { brightness = msgString.toInt(); @@ -143,62 +149,288 @@ void callbackMQTT(char* topic, byte* payload, unsigned int length) } // LED +/** +* Coupe tout le strip de led. +*/ void ledBlackAll() { - FastLED.clear(); - FastLED.show(); + FastLED.clear(); + FastLED.show(); } +/** +* Effet Cylon : défilement d'une simple led sur le strip aller/retour. +* Pour faire plus sympas on ajoute une lueur autour, avec une lumière atténué. +*/ void ledCylon() { - // Effet cylon : on allume une led, on attends, on eteinds, on passe à la suivante - for(int i = 0; i < LED_NUM; i++) { - client.loop(); - if (ledEffect != LED_EFFECT_CYLON) { - return; - } + for(int i = 0; i < LED_NUM; i++) { + client.loop(); - leds[i] = color; - FastLED.delay(1000 / speed); - leds[i] = CRGB::Black; - FastLED.delay(1000 / speed); + if (ledEffect != LED_EFFECT_CYLON) { + return; } - for(int i = LED_NUM - 1; i > 0; i--) { - client.loop(); - if (ledEffect != LED_EFFECT_CYLON) { - return; - } - - leds[i] = color; - FastLED.delay(1000 / speed); - leds[i] = CRGB::Black; - FastLED.show(); + if ((i - 3) >= 0) { + leds[i - 3] = CRGB::Black; } + if ((i - 2) >= 0) { + /* + * Se lit 128/256 d'intensité lumineuse actuelle + * https://github.com/FastLED/FastLED/wiki/Pixel-reference#dimming-and-brightening-colors + */ + leds[i - 2].fadeLightBy(240); + } + if ((i - 1) >= 0) { + leds[i - 1].fadeLightBy(200); + } + + leds[i] = color; + + if ((i + 1) <= LED_NUM) { + leds[i + 1] = color; + // Je suis volontairement un peu moins puissant sur l'avant + // pour donner un effet de trainée sur l'arrière + leds[i + 1].fadeLightBy(220); + } + if ((i + 2) <= LED_NUM) { + leds[i + 2] = color; + leds[i + 2].fadeLightBy(240); + } + FastLED.delay(1000 / speed); + } + + // led[0] et led[255] sont gérées par la loop précédante + for(int i = LED_NUM - 1; i > 0; i--) { + client.loop(); + + if (ledEffect != LED_EFFECT_CYLON) { + return; + } + + if ((i - 2) >= 0) { + leds[i - 2] = color; + leds[i - 2].fadeLightBy(240); + } + if ((i - 1) >= 0) { + leds[i - 1] = color; + leds[i - 1].fadeLightBy(220); + } + + leds[i] = color; + + if ((i + 1) <= LED_NUM) { + leds[i + 1].fadeLightBy(200); + } + if ((i + 2) <= LED_NUM) { + leds[i + 2].fadeLightBy(240); + } + if ((i + 3) <= LED_NUM) { + leds[i + 3] = CRGB::Black; + } + + FastLED.delay(1000 / speed); + } } +/** + * Utilise pour indiquer une erreur sur la reception de l'effet. + */ void ledError() { - for(int i = 0; i < LED_NUM; i++) { - if ((i % 2) == 0) { - leds[i] = CRGB::Black; - } else { - leds[i] = color; - } + for (int i = 0; i < LED_NUM; i++) { + if ((i % 2) == 0) { + leds[i] = CRGB::Black; + } else { + leds[i] = CRGB::Red; } + } - FastLED.delay(1000 / speed); + FastLED.delay(1000 / speed); } +/** + * Affiche une couleur de manière uniforme sur le strip. + * Pour éviter un éclairage basique, on applique un breath qui permet + * de faire respirer la couleur (brightness). + */ void ledFullColor() { - fill_solid(leds, LED_NUM, color); - int breath = (exp(sin(millis() / 2000.0 * PI)) - 0.36787944) * 108.4; - FastLED.setBrightness(breath); - FastLED.delay(100 / speed); + fill_solid(leds, LED_NUM, color); + // TODO : il fadrait pas faire 0 -> 255 mais plutot 20 (ou plus) -> brightness + // Source : http://sean.voisen.org/blog/2011/10/breathing-led-with-arduino/ + // Augmenter 2000 augmente la fréquence (c'est en fait sin((temps / 1000) * Pi/2) + // 0.36787944 ?? censé correspondre au minimum + // 108.4 ?? censé correspondre au maximum + int breath = (exp(sin(millis() / 2000.0 * PI)) - 0.3678794) * 108.4; + Serial.print(breath); + Serial.println(" / 255"); + FastLED.setBrightness(breath); + FastLED.delay(100 / speed); } +///////////////////// FastLED-3.1.5/examples/ColorPalette ///////////////////// +void ledColorPattern() +{ + ChangePalettePeriodically(); + + static uint8_t startIndex = 0; + startIndex = startIndex + 1; /* motion speed */ + + FillLEDsFromPaletteColors(startIndex); + + FastLED.delay(1000 / speed); +} + +void FillLEDsFromPaletteColors(uint8_t colorIndex) +{ + uint8_t brightness = 255; + + for( int i = 0; i < LED_NUM; i++) { + leds[i] = ColorFromPalette( + currentPalette, + colorIndex, + brightness, + currentBlending + ); + colorIndex += 3; + } +} + +// There are several different palettes of colors demonstrated here. +// +// FastLED provides several 'preset' palettes: RainbowColors_p, RainbowStripeColors_p, +// OceanColors_p, CloudColors_p, LavaColors_p, ForestColors_p, and PartyColors_p. +// +// Additionally, you can manually define your own color palettes, or you can write +// code that creates color palettes on the fly. All are shown here. +void ChangePalettePeriodically() +{ + uint8_t secondHand = (millis() / 1000) % 60; + static uint8_t lastSecond = 99; + + if( lastSecond != secondHand) { + lastSecond = secondHand; + if (secondHand == 0) { currentPalette = RainbowColors_p; currentBlending = LINEARBLEND; } + if (secondHand == 10) { currentPalette = RainbowStripeColors_p; currentBlending = NOBLEND; } + if (secondHand == 15) { currentPalette = RainbowStripeColors_p; currentBlending = LINEARBLEND; } + if (secondHand == 20) { SetupPurpleAndGreenPalette(); currentBlending = LINEARBLEND; } + if (secondHand == 25) { SetupTotallyRandomPalette(); currentBlending = LINEARBLEND; } + if (secondHand == 30) { SetupBlackAndWhiteStripedPalette(); currentBlending = NOBLEND; } + if (secondHand == 35) { SetupBlackAndWhiteStripedPalette(); currentBlending = LINEARBLEND; } + if (secondHand == 40) { currentPalette = CloudColors_p; currentBlending = LINEARBLEND; } + if (secondHand == 45) { currentPalette = PartyColors_p; currentBlending = LINEARBLEND; } + if (secondHand == 50) { currentPalette = myRedWhiteBluePalette_p; currentBlending = NOBLEND; } + if (secondHand == 55) { currentPalette = myRedWhiteBluePalette_p; currentBlending = LINEARBLEND; } + } +} + +// This function fills the palette with totally random colors. +void SetupTotallyRandomPalette() +{ + for (int i = 0; i < 16; i++) { + currentPalette[i] = CHSV(random8(), 255, random8()); + } +} + +// This function sets up a palette of black and white stripes, +// using code. Since the palette is effectively an array of +// sixteen CRGB colors, the various fill_* functions can be used +// to set them up. +void SetupBlackAndWhiteStripedPalette() +{ + // 'black out' all 16 palette entries... + fill_solid(currentPalette, 16, CRGB::Black); + // and set every fourth one to white. + currentPalette[0] = CRGB::White; + currentPalette[4] = CRGB::White; + currentPalette[8] = CRGB::White; + currentPalette[12] = CRGB::White; +} + +// This function sets up a palette of purple and green stripes. +void SetupPurpleAndGreenPalette() +{ + CRGB purple = CHSV(HUE_PURPLE, 255, 255); + CRGB green = CHSV(HUE_GREEN, 255, 255); + CRGB black = CRGB::Black; + + currentPalette = CRGBPalette16( + green, green, black, black, + purple, purple, black, black, + green, green, black, black, + purple, purple, black, black + ); +} +///////////////////// FastLED-3.1.5/examples/ColorPalette ///////////////////// + +/////////////////// FastLED-3.1.5/examples/ColorTemperature /////////////////// +void colorTemp() +{ + // draw a generic, no-name rainbow + static uint8_t starthue = 0; + fill_rainbow(leds + 5, LED_NUM - 5, --starthue, 20); + + // Choose which 'color temperature' profile to enable. + uint8_t secs = (millis() / 1000) % (DISPLAYTIME * 2); + if (secs < DISPLAYTIME) { + FastLED.setTemperature(TEMPERATURE_1 ); // first temperature + leds[0] = TEMPERATURE_1; // show indicator pixel + } else { + FastLED.setTemperature(TEMPERATURE_2 ); // second temperature + leds[0] = TEMPERATURE_2; // show indicator pixel + } + + // Black out the LEDs for a few secnds between color changes + // to let the eyes and brains adjust + if((secs % DISPLAYTIME) < BLACKTIME) { + memset8(leds, 0, LED_NUM * sizeof(CRGB)); + } + + FastLED.show(); + FastLED.delay(8); +} +/////////////////// FastLED-3.1.5/examples/ColorTemperature /////////////////// + +//////////////////////// FastLED-3.1.5/examples/Fire202 /////////////////////// +void fire() +{ + // Array of temperature readings at each simulation cell + static byte heat[LED_NUM]; + + // Step 1. Cool down every cell a little + for (int i = 0; i < LED_NUM; i++) { + heat[i] = qsub8(heat[i], random8(0, ((COOLING * 10) / LED_NUM) + 2)); + } + + // Step 2. Heat from each cell drifts 'up' and diffuses a little + for (int k= LED_NUM - 1; k >= 2; k--) { + heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2] ) / 3; + } + + // Step 3. Randomly ignite new 'sparks' of heat near the bottom + if (random8() < SPARKING ) { + int y = random8(7); + heat[y] = qadd8(heat[y], random8(160,255)); + } + + // Step 4. Map from heat cells to LED colors + for (int j = 0; j < LED_NUM; j++) { + CRGB color = HeatColor( heat[j]); + int pixelnumber; + if (gReverseDirection) { + pixelnumber = (LED_NUM - 1) - j; + } else { + pixelnumber = j; + } + leds[pixelnumber] = color; + } + + FastLED.delay(1000 / speed); +} +//////////////////////// FastLED-3.1.5/examples/Fire202 /////////////////////// + void loop() { // MQTT testConnectMQTT(); @@ -212,6 +444,12 @@ void loop() { ledCylon(); } else if (ledEffect == LED_EFFECT_FULLRED) { ledFullColor(); + } else if (ledEffect == LED_EFFECT_COLORPATTERN) { + ledColorPattern(); + } else if (ledEffect == LED_EFFECT_COLORTEMP) { + colorTemp(); + } else if (ledEffect == LED_EFFECT_FIRE) { + fire(); } else { ledError(); } diff --git a/arduino/mqttfastledmenu/mqttfastledmenu.example.h b/arduino/mqttfastledmenu/mqttfastledmenu.example.h index 3653ce5..2ee2ea4 100644 --- a/arduino/mqttfastledmenu/mqttfastledmenu.example.h +++ b/arduino/mqttfastledmenu/mqttfastledmenu.example.h @@ -10,6 +10,9 @@ #define LED_COLOR_DEFAULT CRGB::Red #define LED_EFFECT_CYLON "cylon" +#define LED_EFFECT_COLORPATTERN "colorp" +#define LED_EFFECT_COLORTEMP "colort" +#define LED_EFFECT_FIRE "fire" #define LED_EFFECT_FULLRED "full" #define LED_EFFECT_ERROR "error" @@ -34,11 +37,6 @@ #define MQTT_LED_COLOR_COMMAND "strip1/color/switch" #define MQTT_LED_COLOR_STATE "strip1/color/status" -// FastLED -// TODO : essayer, devrait limiter le flikering -//#define FASTLED_ALLOW_INTERRUPTS 0 -#define FASTLED_ESP8266_NODEMCU_PIN_ORDER - void setupWifi(); void testConnectMQTT(); void callbackMQTT(char* topic, byte* payload, unsigned int length); @@ -46,3 +44,129 @@ void ledBlackAll(); void ledCylon(); void ledError(); void ledFullColor(); +///////////////////////////////// ColorPalette +// This example shows several ways to set up and use 'palettes' of colors +// with FastLED. +// +// These compact palettes provide an easy way to re-colorize your +// animation on the fly, quickly, easily, and with low overhead. +// +// USING palettes is MUCH simpler in practice than in theory, so first just +// run this sketch, and watch the pretty lights as you then read through +// the code. Although this sketch has eight (or more) different color schemes, +// the entire sketch compiles down to about 6.5K on AVR. +// +// FastLED provides a few pre-configured color palettes, and makes it +// extremely easy to make up your own color schemes with palettes. +// +// Some notes on the more abstract 'theory and practice' of +// FastLED compact palettes are at the bottom of this file. +CRGBPalette16 currentPalette; +TBlendType currentBlending; + +extern CRGBPalette16 myRedWhiteBluePalette; +extern const TProgmemPalette16 myRedWhiteBluePalette_p PROGMEM; + +// This example shows how to set up a static color palette +// which is stored in PROGMEM (flash), which is almost always more +// plentiful than RAM. A static PROGMEM palette like this +// takes up 64 bytes of flash. +const TProgmemPalette16 myRedWhiteBluePalette_p PROGMEM = +{ + CRGB::Red, + CRGB::Gray, // 'white' is too bright compared to red and blue + CRGB::Blue, + CRGB::Black, + + CRGB::Red, + CRGB::Gray, + CRGB::Blue, + CRGB::Black, + + CRGB::Red, + CRGB::Red, + CRGB::Gray, + CRGB::Gray, + CRGB::Blue, + CRGB::Blue, + CRGB::Black, + CRGB::Black +}; + +void ledColorPattern(); +void FillLEDsFromPaletteColors(uint8_t colorIndex); +void ChangePalettePeriodically(); +void SetupTotallyRandomPalette(); +void SetupBlackAndWhiteStripedPalette(); +void SetupPurpleAndGreenPalette(); +//////////////////////////////////////////////// ColorTemperature +// THIS EXAMPLE demonstrates the second, "color temperature" control. +// It shows a simple rainbow animation first with one temperature profile, +// and a few seconds later, with a different temperature profile. +// +// The first pixel of the strip will show the color temperature. +// +// HELPFUL HINTS for "seeing" the effect in this demo: +// * Don't look directly at the LED pixels. Shine the LEDs aganst +// a white wall, table, or piece of paper, and look at the reflected light. +// +// * If you watch it for a bit, and then walk away, and then come back +// to it, you'll probably be able to "see" whether it's currently using +// the 'redder' or the 'bluer' temperature profile, even not counting +// the lowest 'indicator' pixel. +// +// +// FastLED provides these pre-conigured incandescent color profiles: +// Candle, Tungsten40W, Tungsten100W, Halogen, CarbonArc, +// HighNoonSun, DirectSunlight, OvercastSky, ClearBlueSky, +// FastLED provides these pre-configured gaseous-light color profiles: +// WarmFluorescent, StandardFluorescent, CoolWhiteFluorescent, +// FullSpectrumFluorescent, GrowLightFluorescent, BlackLightFluorescent, +// MercuryVapor, SodiumVapor, MetalHalide, HighPressureSodium, +// FastLED also provides an "Uncorrected temperature" profile +// UncorrectedTemperature; + +#define TEMPERATURE_1 Tungsten100W +#define TEMPERATURE_2 OvercastSky +// How many seconds to show each temperature before switching +#define DISPLAYTIME 20 +// How many seconds to show black between switches +#define BLACKTIME 3 +void colorTemp(); +///////////////////////////////////////////////Fire202 +bool gReverseDirection = false; +// This basic one-dimensional 'fire' simulation works roughly as follows: +// There's a underlying array of 'heat' cells, that model the temperature +// at each point along the line. Every cycle through the simulation, +// four steps are performed: +// 1) All cells cool down a little bit, losing heat to the air +// 2) The heat from each cell drifts 'up' and diffuses a little +// 3) Sometimes randomly new 'sparks' of heat are added at the bottom +// 4) The heat from each cell is rendered as a color into the leds array +// The heat-to-color mapping uses a black-body radiation approximation. +// +// Temperature is in arbitrary units from 0 (cold black) to 255 (white hot). +// +// This simulation scales it self a bit depending on NUM_LEDS; it should look +// "OK" on anywhere from 20 to 100 LEDs without too much tweaking. +// +// I recommend running this simulation at anywhere from 30-100 frames per second, +// meaning an interframe delay of about 10-35 milliseconds. +// +// Looks best on a high-density LED setup (60+ pixels/meter). +// +// +// There are two main parameters you can play with to control the look and +// feel of your fire: COOLING (used in step 1 above), and SPARKING (used +// in step 3 above). +// +// COOLING: How much does the air cool as it rises? +// Less cooling = taller flames. More cooling = shorter flames. +// Default 50, suggested range 20-100 +#define COOLING 55 + +// SPARKING: What chance (out of 255) is there that a new spark will be lit? +// Higher chance = more roaring fire. Lower chance = more flickery fire. +// Default 120, suggested range 50-200. +#define SPARKING 120 +void fire(); diff --git a/home-assistant/ha_configuration.yml b/home-assistant/ha_configuration.yml index 2481434..445e3bc 100644 --- a/home-assistant/ha_configuration.yml +++ b/home-assistant/ha_configuration.yml @@ -5,12 +5,16 @@ mqtt: username: "XXX" password: "XXX" +# TODO : idéee ! préfixé les functions d'un nombre qu'on sort lors du publish, cela permet de n'avoir que des id input_select: strip1_effect: name: "Choix de l'effet" options: - "cylon" - "full" + - "colorp" + - "colort" + - "fire" - "error" input_slider: diff --git a/arduino/platformio.ini b/platformio.ini similarity index 93% rename from arduino/platformio.ini rename to platformio.ini index deddd6f..427912b 100644 --- a/arduino/platformio.ini +++ b/platformio.ini @@ -14,5 +14,5 @@ board=nodemcuv2 framework=arduino [platformio] -src_dir=mqttfastledmenu +src_dir=arduino/mqttfastledmenu lib_dir=/home/jcabillot/Arduino/libraries