#include "esphome.h" #include "NeoPixelBus.h" //#include "multi_effect_handler.h" #define SEGMENT_LENGTH 5 class NbsEffect { private: int goalRed = 0, goalGreen = 0, goalBlue = 0; double currentRed = 0, currentGreen = 0, currentBlue = 0; double calcRed = 0, calcGreen = 0, calcBlue = 0; int stepsPerSecond = 0; int totalSteps = 0; int currentStep = 0; bool finalStep = true; public: NbsEffect(int stepsPerSecond_){ stepsPerSecond = stepsPerSecond_; } void setGoalColorAsRgb(int red, int green, int blue, int timeInMilliseconds){ if(goalRed == red && goalGreen == green && goalBlue == blue){ return; } finalStep = false; currentRed = (double) goalRed; currentGreen = (double) goalGreen; currentBlue = (double) goalBlue; goalRed = red; goalGreen = green; goalBlue = blue; double timeInSeconds = (double) timeInMilliseconds / 1000.0; totalSteps = (int) (timeInSeconds * (double) stepsPerSecond); currentStep = totalSteps; calcRed = ((double) goalRed - currentRed) / totalSteps; calcGreen = ((double) goalGreen - currentGreen) / totalSteps; calcBlue = ((double) goalBlue - currentBlue) / totalSteps; currentStep = 0; } int debugGoalRed(){ return goalRed; } double debugCurrentRed(){ return currentRed; } double debugCalcRed(){ return calcRed; } bool step(){ if(currentStep >= totalSteps){ return false; } ++currentStep; if(currentStep == totalSteps){ currentRed = (double) goalRed; currentGreen = (double) goalGreen; currentBlue = (double) goalBlue; finalStep = true; return true; } currentRed += calcRed; currentGreen += calcGreen; currentBlue += calcBlue; return true; } bool wasFinalStep(){ return finalStep; } int getCurrentRed(){ return (int) currentRed; } int getCurrentGreen(){ return (int) currentGreen; } int getCurrentBlue(){ return (int) currentBlue; } }; NeoPixelBus nbsStrip(143, D5); class CustomNumberDisplayComponent : public Component, public CustomMQTTDevice { const int middleSegment = SEGMENT_LENGTH * 14; const int transitionLength = 500; // milliseconds const int middleLength = 2; const double brightness = 0.17; NbsEffect *colorAnimation[4][7]; NbsEffect *middleSegmentAnim; unsigned long lastUpdate = 0; bool shouldShowNBS = true; bool numbers[14][7] = { {0, 1, 1, 1, 1, 1, 1}, // 0 {0, 1, 0, 0, 0, 0, 1}, // 1 {1, 1, 1, 0, 1, 1, 0}, // 2 {1, 1, 1, 0, 0, 1, 1}, // 3 {1, 1, 0, 1, 0, 0, 1}, // 4 {1, 0, 1, 1, 0, 1, 1}, // 5 {1, 0, 1, 1, 1, 1, 1}, // 6 {0, 1, 1, 0, 0, 0, 1}, // 7 {1, 1, 1, 1, 1, 1, 1}, // 8 {1, 1, 1, 1, 0, 1, 1}, // 9 {0, 0, 0, 0, 0, 0, 0}, // No Display (10) {1, 0, 0, 0, 0, 0, 0}, // - (11) {1, 0, 0, 0, 1, 0, 1}, // n (12) {1, 0, 0, 1, 1, 1, 1} // b (13) }; public: void setup() override { // Segment mapping int segPixelCounter = 0; for(int i = 0; i < 4; ++i){ for(int j = 0; j < 7; ++j){ colorAnimation[i][j] = new NbsEffect(40); } } middleSegmentAnim = new NbsEffect(40); nbsStrip.Begin(); setNumber(0, 12, 1.0, 0.0, 0.0); setNumber(1, 13, 0.0, 1.0, 0.0); setNumber(2, 5, 0.0, 0.0, 1.0); setNumber(3, 11, 0.5, 0.5, 0.5); for(int led = middleSegment; led < middleSegment + 2; ++led){ nbsStrip.SetPixelColor(led, RgbColor(0, 0, 0)); } nbsStrip.Show(); lastUpdate = millis(); // also supports JSON messages subscribe_json("tabletenniscounter/display/number/command", &CustomNumberDisplayComponent::on_json_message, 2); } void on_json_message(const std::string &topic, JsonObject root) { //checker = 0; if(!root.containsKey("state")){ return; } if(root["state"] != "ON"){ setNumber(0, 10); setNumber(1, 10); setNumber(2, 10); setNumber(3, 10); turnSegmentOff(middleSegmentAnim); publish_json("tabletenniscounter/display/number/state", [=](JsonObject root2) { root2["state"] = "OFF"; }, 2, false); return; } if(shouldShowNBS){ shouldShowNBS = false; lastUpdate = millis(); } if (!root.containsKey("player1") || !root.containsKey("player2") || !(root.containsKey("player1Start") || root.containsKey("displayGreen"))){ setNumber(0, 12, 1.0, 0.0, 0.0); setNumber(1, 13, 0.0, 1.0, 0.0); setNumber(2, 5, 0.0, 0.0, 1.0); setNumber(3, 11, 0.5, 0.5, 0.5); turnSegmentOff(middleSegmentAnim); publish_json("tabletenniscounter/display/number/state", [=](JsonObject root2) { root2["state"] = "ON"; }, 2, false); return; } int player1 = root["player1"]; int player2 = root["player2"]; id(red_num).set(player1); id(blue_num).set(player2); bool player1Start = root["player1Start"]; // do something with Json Object if(player1 > 99) { player1 = 99; } else if(player1 < 0) { player1 = 0; } if(player2 > 99) { player2 = 99; } else if(player2 < 0) { player2 = 0; } int player1T = player1 / 10; int player1O = player1 % 10; int player2T = player2 / 10; int player2O = player2 % 10; int handleColorWinChange = 0; if(root.containsKey("player1Winner")){ if(root["player1Winner"]){ handleColorWinChange = 1; } else{ handleColorWinChange = 2; } } if(player1T == 0) setNumber(0, 10); else{ if(handleColorWinChange == 1){ setNumber(0, player1T, 1.0, 1.0, 0.0); }else{ setNumber(0, player1T); } } if(handleColorWinChange == 1){ setNumber(1, player1O, 1.0, 1.0, 0.0); }else{ setNumber(1, player1O); } if(player2T == 0){ if(handleColorWinChange == 2){ setNumber(2, player2O, 1.0, 1.0, 0.0); }else{ setNumber(2, player2O); } setNumber(3, 10); } else{ if(handleColorWinChange == 2){ setNumber(2, player2T, 1.0, 1.0, 0.0); setNumber(3, player2O, 1.0, 1.0, 0.0); }else{ setNumber(2, player2T); setNumber(3, player2O); } } if(root.containsKey("displayGreen")){ turnSegmentOn(middleSegmentAnim, brightness, 0.0, 1.0, 0.0); } else{ if(player1Start){ turnSegmentOn(middleSegmentAnim, brightness, 1.0, 0.0, 0.0); } else{ turnSegmentOn(middleSegmentAnim, brightness, 0.0, 0.0, 1.0); } } // publish JSON using lambda syntax publish_json("tabletenniscounter/display/number/state", [=](JsonObject root2) { root2["state"] = "ON"; root2["player1"] = player1; root2["player2"] = player2; root2["player1Start"] = player1Start; }, 2, false); } void loop() override{ if(lastUpdate + 25 >= millis()){ return; } if(lastUpdate + 50 < millis()){ //ESP_LOGW("custom_display", "LAGWARN, lastUpdate timer was reseted!"); lastUpdate = millis(); } lastUpdate += 25; bool didUpdate = false; for(int i = 0; i < 4; ++i){ for(int j = 0; j < 7; ++j){ NbsEffect *effect = colorAnimation[i][j]; int segStart = calculateSegStart(i, j); if(effect->step()){ didUpdate = true; for(int led = segStart; led < segStart + SEGMENT_LENGTH; ++led){ nbsStrip.SetPixelColor(led, RgbColor(effect->getCurrentRed(), effect->getCurrentGreen(), effect->getCurrentBlue())); } } } } if(colorAnimation[0][0]->wasFinalStep() && shouldShowNBS){ shouldShowNBS = false; lastUpdate = millis() + 5000; setNumber(0, 10, 0.0, 0.0, 0.0); setNumber(1, 10, 0.0, 0.0, 0.0); setNumber(2, 10, 0.0, 0.0, 0.0); setNumber(3, 10, 0.0, 0.0, 0.0); } if(middleSegmentAnim->step()){ didUpdate = true; //ESP_LOGD("custom", "#%d, Color Code: %d, %d, %d", checker++, middleSegmentAnim->getCurrentRed(), middleSegmentAnim->getCurrentGreen(), middleSegmentAnim->getCurrentBlue()); for(int led = middleSegment; led < middleSegment + 2; ++led){ nbsStrip.SetPixelColor(led, RgbColor(middleSegmentAnim->getCurrentRed(), middleSegmentAnim->getCurrentGreen(), middleSegmentAnim->getCurrentBlue())); } } if(didUpdate){ nbsStrip.Show(); } } int calculateSegStart(int digit, int segment){ int start = (digit * SEGMENT_LENGTH * 7) + (segment * SEGMENT_LENGTH); if(digit >= 2){ start += 2; } return start; } void turnSegmentOn(NbsEffect* segment, double brightness, double red, double green, double blue){ segment->setGoalColorAsRgb(makeColorAsInt(brightness, red, 220), makeColorAsInt(brightness, green, 170), makeColorAsInt(brightness, blue, 235), 500); } void turnSegmentOff(NbsEffect* segment){ segment->setGoalColorAsRgb(0, 0, 0, 500); } void setNumber(int digit, int number, double red, double green, double blue){ if(digit < 0 || digit > 3){ return; } for(int i = 0; i < 7; ++i){ if(numbers[number][i]){ turnSegmentOn(colorAnimation[digit][i], brightness, red, green, blue); } else{ turnSegmentOff(colorAnimation[digit][i]); } } } void setNumber(int digit, int number){ if(digit < 0 || digit > 3){ return; } double red = 0; double blue = 0; if(digit == 0 || digit == 1){ red = 1.0; blue = 0.0; } else { red = 0.0; blue = 1.0; } setNumber(digit, number, red, 0.0, blue); } int makeColorAsInt(double brightness, double value, double colorMax){ return (int) (colorMax * value * brightness); } };