#include // homie lib from: https://github.com/marvinroger/homie-esp8266/ #include #include #include "NeoPatterns.h" #include "font.h" #ifdef __AVR__ #include #endif #define PIN D1 //data pin for ws2812 (pixelprojektor @ ctdo: PIN 2) #define NUMPIXELS 64 #define FPS 15 uint8_t effect = 0; #define EFFECT_NONE 0 #define EFFECT_SMOOTH 1 uint8_t movingPoint_x = 3; uint8_t movingPoint_y = 3; uint8_t wheelPos = 0; uint8_t wheelPosSlow = 0; //for slower wheelPos increment than 1 int wheelSpeed = 16; //16=+1/frame int smoothing = 80; //0 to 100. 100=no change (ultrasmooth), 0=no smoothing. int strength = 50; //how much pixels to apply color to #define EFFECT_SPIRAL 2 #define EFFECT_RANDOMFADE 3 #define EFFECT_CHASE 4 #define EFFECT_RADAR 5 #define EFFECT_LARSON 6 int fadespeedmax = 5; //1 to 255 int iconCountStart = 0; //for percentage calculation int iconCountdown = 0; //0=off uint8_t iconchar = 0; //last displayed char int32_t iconcolor = 0; //last icon color uint16_t Index; // current step within the pattern // int Index = 0; // Step for Effect (e.g. chase) // int state = 0; // Direction for Larson Scanner (spiral) direction Direction; // direction to run the pattern Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800); HomieNode homieNode("pixel", "commands"); uint8_t pixelR[NUMPIXELS]; uint8_t pixelG[NUMPIXELS]; uint8_t pixelB[NUMPIXELS]; //write to buffer, flip with showBuffer() uint8_t pixelR_buffer[NUMPIXELS]; uint8_t pixelG_buffer[NUMPIXELS]; uint8_t pixelB_buffer[NUMPIXELS]; long lastMillis = 0; long fpsdelay = 1000 / FPS; int xyToPos(int x, int y) { //convert x y pixel position to matrix position if (y % 2 == 0) { return (y * 8 + x); } else { return (y * 8 + (7 - x)); } } int numToPos(int num) { //convert pixel number to actual 8x8 matrix position int x = num % 8; int y = num / 8; return xyToPos(x, y); } int numToSpiralPos(int num) { // convert pixel number to actual 8x8 matrix position in a spiral int findx = 7; int findy = 0; int stepsize = 7; // initial value (0..7) int stepnumber = 0; // each "step" should be used twice int count = -1; int dir = 1; // direction: 0 = incX, 1=incY, 2=decX, 3=decY if (num < 8) { return num; // trivial } for (int i = 8; i <= num; i++) { count++; if (count == stepsize) { count = 0; // Change direction dir++; stepnumber++; if (stepnumber == 2) { stepsize -= 1; stepnumber = 0; } if (dir == 4) { dir = 0; } } switch (dir) { case 0: findx++; break; case 1: findy++; break; case 2: findx--; break; case 3: findy--; break; } } return xyToPos(findx, findy); } uint32_t wheel(byte WheelPos) { WheelPos = 255 - WheelPos; if (WheelPos < 85) { return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3); } if (WheelPos < 170) { WheelPos -= 85; return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3); } WheelPos -= 170; return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0); } void led_fill(uint32_t c) { for (int i = 0; i < strip.numPixels(); i++) { strip.setPixelColor(i, c); } strip.show(); } void led_random() { for (int i = 0; i < strip.numPixels(); i++) { strip.setPixelColor(i, wheel(random(0, 255))); } strip.show(); } void showBuffer() { for (int i = 0; i < strip.numPixels(); i++) { pixelR[i] = pixelR_buffer[i]; pixelG[i] = pixelG_buffer[i]; pixelB[i] = pixelB_buffer[i]; strip.setPixelColor(i, pixelR[i], pixelG[i], pixelB[i]); } strip.show(); } uint8_t getAverage(uint8_t array[NUMPIXELS], uint8_t i, int x, int y) { uint16_t sum = 0; uint8_t count = 0; if (i >= 8) { //up sum += array[i - 8]; count++; } if (i < (64 - 8)) { //down sum += array[i + 8]; count++; } if (i >= 1) { //left sum += array[i - 1]; count++; } if (i < (64 - 1)) { //right sum += array[i + 1]; count++; } /* if (i>=(8+1)){ //up left sum+=array[i-8-1]; count++; } if (i<(64-8-1)){ //down left sum+=array[i+8-1]; count++; } if (i>=(8-1)){ //up right sum+=array[i-8+1]; count++; } if (i<(64-8+1)){ //down right sum+=array[i+8+1]; count++; }*/ return sum / count; } void led_smooth() { for (int i = 0; i < strip.numPixels(); i++) { //uint8_t avgbrightness=pixelR_buffer[i]/3+pixelG_buffer[i]/3+pixelB_buffer[i]/3; pixelR_buffer[i] = (smoothing / 100.0) * pixelR[i] + (1.0 - (smoothing / 100.0)) * getAverage(pixelR, i, 0, 0); pixelG_buffer[i] = (smoothing / 100.0) * pixelG[i] + (1.0 - (smoothing / 100.0)) * getAverage(pixelG, i, 0, 0); pixelB_buffer[i] = (smoothing / 100.0) * pixelB[i] + (1.0 - (smoothing / 100.0)) * getAverage(pixelB, i, 0, 0); } showBuffer(); } void led_movingPoint() { uint32_t c = wheel(wheelPos); wheelPosSlow += wheelSpeed; wheelPos = (wheelPos + (wheelPosSlow / 10) ) % 255; wheelPosSlow = wheelPosSlow % 16; uint8_t r = (uint8_t)(c >> 16); uint8_t g = (uint8_t)(c >> 8); uint8_t b = (uint8_t)c; movingPoint_x = movingPoint_x + 8 + random(-random(0, 1 + 1), random(0, 1 + 1) + 1); movingPoint_y = movingPoint_y + 8 + random(-random(0, 1 + 1), random(0, 1 + 1) + 1); if (movingPoint_x < 8) { movingPoint_x = 8 - movingPoint_x; } else if (movingPoint_x >= 16) { movingPoint_x = 22 - movingPoint_x; } else { movingPoint_x -= 8; } if (movingPoint_y < 8) { movingPoint_y = 8 - movingPoint_y; } else if (movingPoint_y >= 16) { movingPoint_y = 22 - movingPoint_y; } else { movingPoint_y -= 8; } uint8_t startx = movingPoint_x; uint8_t starty = movingPoint_y; for (int i = 0; i < strength; i++) { movingPoint_x = startx + 8 + random(-random(0, 2 + 1), random(0, 2 + 1) + 1); movingPoint_y = starty + 8 + random(-random(0, 2 + 1), random(0, 2 + 1) + 1); if (movingPoint_x < 8) { movingPoint_x = 8 - movingPoint_x; } else if (movingPoint_x >= 16) { movingPoint_x = 22 - movingPoint_x; } else { movingPoint_x -= 8; } if (movingPoint_y < 8) { movingPoint_y = 8 - movingPoint_y; } else if (movingPoint_y >= 16) { movingPoint_y = 22 - movingPoint_y; } else { movingPoint_y -= 8; } if (pixelR[xyToPos(movingPoint_x, movingPoint_y)] < r) { pixelR[xyToPos(movingPoint_x, movingPoint_y)]++; } else if (pixelR[xyToPos(movingPoint_x, movingPoint_y)] > r) { pixelR[xyToPos(movingPoint_x, movingPoint_y)]--; } if (pixelG[xyToPos(movingPoint_x, movingPoint_y)] < g) { pixelG[xyToPos(movingPoint_x, movingPoint_y)]++; } else if (pixelG[xyToPos(movingPoint_x, movingPoint_y)] > g) { pixelG[xyToPos(movingPoint_x, movingPoint_y)]--; } if (pixelB[xyToPos(movingPoint_x, movingPoint_y)] < b) { pixelB[xyToPos(movingPoint_x, movingPoint_y)]++; } else if (pixelB[xyToPos(movingPoint_x, movingPoint_y)] > b) { pixelB[xyToPos(movingPoint_x, movingPoint_y)]--; } } //pixelR[xyToPos(movingPoint_x,movingPoint_y)]=0.5*pixelR[xyToPos(movingPoint_x,movingPoint_y)]+0.5*r; //pixelG[xyToPos(movingPoint_x,movingPoint_y)]=0.5*pixelG[xyToPos(movingPoint_x,movingPoint_y)]+0.5*g; //pixelB[xyToPos(movingPoint_x,movingPoint_y)]=0.5*pixelB[xyToPos(movingPoint_x,movingPoint_y)]+0.5*b; movingPoint_x = startx; movingPoint_y = starty; } void bufferClear() { for (int i = 0; i < strip.numPixels(); i++) { pixelR_buffer[i] = 0; pixelG_buffer[i] = 0; pixelB_buffer[i] = 0; } } void led_chase() { Index += 1; if (Index > 255) { Index = 1; } // for (int j=0; j < 256; j++) { // cycle all 256 colors in the wheel for (int q = 0; q < 3; q++) { for (uint16_t i = 0; i < strip.numPixels(); i = i + 3) { strip.setPixelColor(i + q, wheel( (i + Index) % 255)); //turn every third pixel on } strip.show(); delay(49); for (uint16_t i = 0; i < strip.numPixels(); i = i + 3) { strip.setPixelColor(i + q, 0); //turn every third pixel off } } } void led_radar() { // "Sweep" in cirles... // line(0,0,950*cos(radians(iAngle)),-950*sin(radians(iAngle))); } // Calculate 50% dimmed version of a color (used by led_larson()) uint32_t DimColor(uint32_t color) { // Shift R, G and B components one bit to the right uint32_t Dimcolor = strip.Color(Red(color) >> 1, Green(color) >> 1, Blue(color) >> 1); return Dimcolor; } // Returns the Red component of a 32-bit color uint8_t Red(uint32_t color) { return (color >> 16) & 0xFF; } // Returns the Green component of a 32-bit color uint8_t Green(uint32_t color) { return (color >> 8) & 0xFF; } // Returns the Blue component of a 32-bit color uint8_t Blue(uint32_t color) { return color & 0xFF; } void Increment() { if (Direction == FORWARD) { Index++; if (Index >= (strip.numPixels() - 1) * 2) { Index = 0; } } else // Direction == REVERSE { --Index; if (Index <= 0) { Index = (strip.numPixels() - 1) * 2 - 1; } } } void led_larson() { int EyeSize = 5; uint32_t rgb[3] = {0}; wheelPos++; if (wheelPos >= 256) { wheelPos = 0; } int Color1 = wheel(wheelPos); for (int i = 0; i < strip.numPixels(); i++) { int realpos = numToSpiralPos(i); if (i == Index) // Scan Pixel to the right { strip.setPixelColor(realpos, Color1); } else if (i == ((strip.numPixels() - 1) * 2) - Index) // Scan Pixel to the left { strip.setPixelColor(realpos, Color1); } else // Fading tail { strip.setPixelColor(realpos, DimColor(strip.getPixelColor(realpos))); } } strip.show(); Increment(); } void led_spiral() { int every = 4; wheelPos++; int qp = Index % every; Index++; if (Index >= strip.numPixels()-1) { Index = 0; } int q = Index % every; for (uint16_t i = 0; i < strip.numPixels(); i = i + every) { strip.setPixelColor(numToSpiralPos(i + q), wheel( (i + Index * 4) % 255)); //turn every "every" pixel on } for (uint16_t i = 0; i < strip.numPixels(); i = i + every) { strip.setPixelColor(numToSpiralPos(i + qp), 0); //turn every "every" pixel off } strip.show(); } void led_randomfade() { for (int i = 0; i < strip.numPixels(); i++) { pixelR_buffer[i] += random(0, random(0, fadespeedmax + 1) + 1); //use buffer red channel for color wheel strip.setPixelColor(i, wheel(pixelR_buffer[i])); } strip.show(); } void led_icon(uint8_t fontchar, uint32_t iconcolor) { for (int i = 0; i < strip.numPixels(); i++) { uint64_t mask = 1LL << (uint64_t)i; if ( (font[fontchar]&mask) == 0) { strip.setPixelColor(numToPos(i), strip.Color(0, 0, 0)); //bit is 0 at pos i } else { float _brightness = 1.0 - ( (iconCountStart - iconCountdown) * 1.0 / iconCountStart ); uint8_t _r = (uint8_t)(iconcolor >> 16); uint8_t _g = (uint8_t)(iconcolor >> 8); uint8_t _b = (uint8_t)iconcolor; strip.setPixelColor(numToPos(i), strip.Color(_r * _brightness, _g * _brightness, _b * _brightness)); //bit is 1 at pos i } } strip.show(); } void set_randomBuffer() { for (int i = 0; i < strip.numPixels(); i++) { uint32_t c = wheel(random(0, 256)); pixelR_buffer[i] = (uint8_t)(c >> 16); pixelG_buffer[i] = (uint8_t)(c >> 8); pixelB_buffer[i] = (uint8_t)c; } } uint32_t parseColor(String value) { if (value.charAt(0) == '#') { //solid fill String color = value.substring(1); int number = (int) strtol( &color[0], NULL, 16); // Split them up into r, g, b values int r = number >> 16; int g = number >> 8 & 0xFF; int b = number & 0xFF; Homie.getLogger() << "r=" << r << " g=" << g << " b=" << b << endl; //Serial.print("r=");Serial.print(r); //Serial.print(" g=");Serial.print(g); //Serial.print(" b=");Serial.println(b); return strip.Color(r, g, b); } return 0; } bool effectHandler(const HomieRange& range, const String& value) { Homie.getLogger() << "-> " << value << endl; int sep = value.indexOf("|"); String command = value.substring(0, sep); String parameters = value.substring(sep + 1); Homie.getLogger() << "command=" << command << " parameters=" << parameters << endl; if (command.equals("fill")) { effect = EFFECT_NONE; led_fill(parseColor(parameters)); } else if (command.equals("off")) { effect = EFFECT_NONE; led_fill(strip.Color(0, 0, 0)); } else if (command.equals("random")) { effect = EFFECT_NONE; led_random(); } else if (command.equals("set")) { //example: set|37#ff003a effect = EFFECT_NONE; int x = parameters.substring(0, 1).toInt(); int y = parameters.substring(1, 2).toInt(); String cstr = parameters.substring(2, 9); strip.setPixelColor(xyToPos(x, y), parseColor(cstr)); strip.show(); } else if (command.equals("smooth")) { //example: smooth|[wheelspeed]|[smoothing]|[strength] wheelspeed=1-255, smoothing=0-100, strength=1-255 int sepparam = parameters.indexOf("|"); int p1 = parameters.substring(0, sepparam).toInt(); String parameters_part2 = parameters.substring(sepparam + 1); sepparam = parameters_part2.indexOf("|"); int p2 = parameters_part2.substring(0, sepparam).toInt(); int p3 = parameters_part2.substring(sepparam + 1).toInt(); wheelSpeed = 16; //default, speed=+1 /frame if (p1 > 0) { wheelSpeed = p1; } smoothing = 80; if (p2 > 0) { smoothing = p2; } strength = 50; if (p3 > 0) { strength = p3; } Homie.getLogger() << "-- p1=" << p1 << " p2=" << p2 << " p3=" << p3 << endl; effect = EFFECT_SMOOTH; bufferClear(); showBuffer(); strip.show(); } else if (command.equals("spiral")) { effect = EFFECT_SPIRAL; Index = 0; bufferClear(); showBuffer(); strip.show(); } else if (command.equals("clearbuffer")) { bufferClear(); showBuffer(); strip.show(); } else if (command.equals("randomfade")) { //example: randomfade|5 int sepparam = parameters.indexOf("|"); int p1 = parameters.substring(0, sepparam).toInt(); fadespeedmax = 5; if (p1 > 0) { fadespeedmax = p1; } effect = EFFECT_RANDOMFADE; set_randomBuffer(); //initialize random } else if (command.equals("randombuffer")) { set_randomBuffer(); //set random showBuffer(); } else if (command.equals("chase")) { effect = EFFECT_CHASE; bufferClear(); showBuffer(); strip.show(); } else if (command.equals("radar")) { effect = EFFECT_RADAR; Index = 0; bufferClear(); showBuffer(); strip.show(); } else if (command.equals("larson")) { effect = EFFECT_LARSON; Index = 0; bufferClear(); showBuffer(); strip.show(); } return true; } bool pixelsHandler(const HomieRange& range, const String& value) { String remaining = value; int i = 0; effect = EFFECT_NONE; do { String current = remaining.substring(0, 7); Homie.getLogger() << i << ":" << current << endl; uint32_t currentcolor = parseColor(current); strip.setPixelColor(i, currentcolor); i++; remaining = remaining.substring(7); } while (remaining.length() > 2 && (i < strip.numPixels())); Homie.getLogger() << " filling rest with black" << endl; while (i < strip.numPixels()) { strip.setPixelColor(numToPos(i), strip.Color(0, 0, 0)); i++; } strip.show(); return true; } bool iconHandler(const HomieRange& range, const String& value) { String _iconname = value; iconcolor = strip.Color(255, 255, 255); //default color if (value[0] == '#') { //color given iconcolor = parseColor(value.substring(0, 6)); _iconname = value.substring(7); // example: #ff00dc|A (pipe will be ignored) } iconCountStart = FPS * 2; iconCountdown = iconCountStart; if (_iconname.length() == 1) { //only one character iconchar = value[0]; } else { } strip.show(); return true; } void setup() { Serial.begin(115200); Serial << endl << endl; Serial << "Begin strip" << endl; strip.begin(); strip.show(); // Initialize all pixels to 'off' led_fill(strip.Color(100, 0, 0)); //delay(500); Serial << "Homie_setFirmware" << endl; Homie_setFirmware("pixelprojektor", "1.0.0"); Serial << "Homie node advertise" << endl; homieNode.advertise("effect").settable(effectHandler); homieNode.advertise("pixels").settable(pixelsHandler); homieNode.advertise("icon").settable(iconHandler); led_fill(strip.Color(0, 0, 0)); strip.setPixelColor(0, strip.Color(100, 0, 0)); strip.show(); Serial << "homie setup" << endl; Homie.setup(); // Hostname defaults to esp8266-[ChipID] ArduinoOTA.setHostname("pixelprojektor"); // No authentication by default // ArduinoOTA.setPassword((const char *)"ctdo2342"); ArduinoOTA.onStart([]() { Serial.println("Start"); led_fill(strip.Color(0, 0, 0)); // Clear }); ArduinoOTA.onEnd([]() { Serial.println("\nEnd"); }); ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { strip.setPixelColor(numToPos((progress / (total / strip.numPixels()))), strip.Color(255, 255, 255)); strip.show(); Serial.printf("Progress: %u%%\r", (progress / (total / 100))); }); ArduinoOTA.onError([](ota_error_t error) { Serial.printf("Error[%u]: ", error); if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed"); else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed"); else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed"); else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed"); else if (error == OTA_END_ERROR) Serial.println("End Failed"); }); ArduinoOTA.begin(); // led_fill(strip.Color(0, 0, 0)); // Initialer Effekt //effect = EFFECT_CHASE; //effect = EFFECT_SPIRAL; effect = EFFECT_LARSON; Serial << "Setup finished" << endl; } void loop() { Homie.loop(); ArduinoOTA.handle(); long currentMillis = millis(); if (lastMillis + fpsdelay < currentMillis) { if (iconCountdown > 0) { //icon effect active iconCountdown--; led_icon(iconchar, iconcolor); } else { switch (effect) { case EFFECT_SMOOTH: led_movingPoint(); led_smooth(); break; case EFFECT_SPIRAL: led_spiral(); break; case EFFECT_RANDOMFADE: led_randomfade(); break; case EFFECT_CHASE: led_chase(); break; case EFFECT_RADAR: led_radar(); break; case EFFECT_LARSON: led_larson(); break; } } lastMillis = currentMillis; } }