2022-05-25 08:58:02 +00:00
|
|
|
#include "esphome.h"
|
|
|
|
#include "NeoPixelBus.h"
|
|
|
|
//#include "multi_effect_handler.h"
|
|
|
|
#define SEGMENT_LENGTH 5
|
|
|
|
|
2024-05-01 10:33:02 +00:00
|
|
|
namespace esphome {
|
|
|
|
namespace ws2812_table_tennis {
|
|
|
|
|
2022-05-25 08:58:02 +00:00
|
|
|
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<NeoGrbFeature, NeoEsp8266BitBangWs2812xMethod> nbsStrip(143, D5);
|
2024-05-01 10:33:02 +00:00
|
|
|
class CustomNumberDisplayComponent : public Component, public mqtt::CustomMQTTDevice {
|
|
|
|
protected:
|
|
|
|
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)
|
|
|
|
};
|
|
|
|
|
|
|
|
template_::TemplateNumber *redNum;
|
|
|
|
template_::TemplateNumber *blueNum;
|
2022-05-25 08:58:02 +00:00
|
|
|
|
|
|
|
public:
|
2024-05-01 10:33:02 +00:00
|
|
|
|
|
|
|
void set_red_number(template_::TemplateNumber *redNum){
|
|
|
|
this->redNum = redNum;
|
|
|
|
}
|
|
|
|
void set_blue_number(template_::TemplateNumber *blueNum){
|
|
|
|
this->blueNum = blueNum;
|
|
|
|
}
|
|
|
|
|
2022-05-25 08:58:02 +00:00
|
|
|
void setup() override {
|
|
|
|
|
2024-05-01 10:33:02 +00:00
|
|
|
|
|
|
|
|
2022-05-25 08:58:02 +00:00
|
|
|
// 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);
|
|
|
|
|
2024-05-01 10:33:02 +00:00
|
|
|
ESP_LOGD("TableTennisCounter", "Ready");
|
2022-05-25 08:58:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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"];
|
|
|
|
|
2024-05-01 10:33:02 +00:00
|
|
|
redNum->publish_state(player1);
|
|
|
|
blueNum->publish_state(player2);
|
2022-05-25 08:58:02 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2024-05-01 10:33:02 +00:00
|
|
|
}
|
|
|
|
}
|