The code is loosely based on https://github.com/leinich/ha-wordclock-esphome
222 lines
9.2 KiB
C++
222 lines
9.2 KiB
C++
#include "esphome.h"
|
|
#include <FastLED.h>
|
|
|
|
// By now only loosely based on https://github.com/leinich/ha-wordclock-esphome
|
|
|
|
// esphome dependencies:
|
|
// needs: esphome time --> id: current_time
|
|
// needs: esphome fastled --> id: fastledlight
|
|
|
|
///// Random Stuff /////
|
|
|
|
#ifndef WORDCLOCK_NUM_LEDS
|
|
#define WORDCLOCK_NUM_LEDS 198
|
|
#endif
|
|
|
|
#ifndef WORDCLOCK_DATA_PIN
|
|
#define WORDCLOCK_DATA_PIN 22
|
|
#endif
|
|
|
|
/* NOTE:
|
|
This section is about mapping LED indices to
|
|
positions in the grid.
|
|
|
|
On EVEN rows, the LED strip is running along the
|
|
matrix indices, but the physical positions of the
|
|
LEDs on the strip did not align with the letter
|
|
cutouts.
|
|
|
|
On ODD rows, the string is running backwards, because
|
|
it is snaking its way back and forth. */
|
|
int mapping_even[] = {0, 2, 4, 6,8,10,11,13,15,17};
|
|
int mapping_odd[] = {17,15,13,11,9, 7, 6, 4, 2, 0};
|
|
int map_coords_to_strip(int row, int column) {
|
|
if (column % 2) {
|
|
return (10 - column) * 18 + mapping_odd[row];
|
|
} else {
|
|
return (10 - column) * 18 + mapping_even [row];
|
|
}
|
|
}
|
|
|
|
///// Word Table /////
|
|
int WORD_IT_IS[5][2] = {{4,0}, {0,0}, {0,1}, {0,3}, {0,4}};
|
|
|
|
int WORD_QUARTER[8][2] = {{7,0}, {1,2}, {1,3}, {1,4}, {1,5}, {1,6}, {1,7}, {1,8}};
|
|
int WORD_TWENTY[7][2] = {{6,0}, {2,0}, {2,1}, {2,2}, {2,3}, {2,4}, {2,5}};
|
|
int WORD_FIVE_MINUTES[5][2] = {{4,0}, {2,6}, {2,7}, {2,8}, {2,9}};
|
|
int WORD_HALF[5][2] = {{4,0}, {3,0}, {3,1}, {3,2}, {3,3}};
|
|
int WORD_TEN_MINUTES[4][2] = {{3,0}, {3,5}, {3,6}, {3,7}};
|
|
|
|
int WORD_TO[3][2] = {{2,0}, {3,9}, {3,10}};
|
|
int WORD_PAST[5][2] = {{4,0}, {4,0}, {4,1}, {4,2}, {4,3}};
|
|
|
|
int WORD_NINE[5][2] = {{4,0}, {4,7}, {4,8}, {4,9}, {4,10}};
|
|
int WORD_ONE[4][2] = {{3,0}, {5,0}, {5,1}, {5,2}};
|
|
int WORD_SIX[4][2] = {{3,0}, {5,3}, {5,4}, {5,5}};
|
|
int WORD_THREE[6][2] = {{5,0}, {5,6}, {5,7}, {5,8}, {5,9}, {5,10}};
|
|
int WORD_FOUR[5][2] = {{4,0}, {6,0}, {6,1}, {6,2}, {6,3}};
|
|
int WORD_FIVE[5][2] = {{4,0}, {6,4}, {6,5}, {6,6}, {6,7}};
|
|
int WORD_TWO[4][2] = {{3,0}, {6,8}, {6,9}, {6,10}};
|
|
int WORD_EIGHT[6][2] = {{5,0}, {7,0}, {7,1}, {7,2}, {7,3}, {7,4}};
|
|
int WORD_ELEVEN[7][2] = {{6,0}, {7,5}, {7,6}, {7,7}, {7,8}, {7,9}, {7,10}};
|
|
int WORD_SEVEN[6][2] = {{5,0}, {8,0}, {8,1}, {8,2}, {8,3}, {8,4}};
|
|
int WORD_TWELFE[7][2] = {{6,0}, {8,5}, {8,6}, {8,7}, {8,8}, {8,9}, {8,10}};
|
|
int WORD_TEN[4][2] = {{3,0}, {9,0}, {9,1}, {9,2}};
|
|
|
|
int WORD_OCLOCK[7][2] = {{6,0}, {9,5}, {9,6}, {9,7}, {9,8}, {9,9}, {9,10}};
|
|
|
|
// TODO: It's probably unnecessary to have the "API" in CustomAPIDevice, since this
|
|
// component doesn't actually register any services anymore.
|
|
class Wordclock : public Component, public CustomAPIDevice {
|
|
public:
|
|
CRGB leds[WORDCLOCK_NUM_LEDS];
|
|
int hour = -1;
|
|
int minute = -1;
|
|
int red = 124;
|
|
int green = 124;
|
|
int blue = 124;
|
|
int brightness = 50;
|
|
|
|
void setup() override {
|
|
FastLED.addLeds<NEOPIXEL, WORDCLOCK_DATA_PIN>(leds, WORDCLOCK_NUM_LEDS);
|
|
FastLED.setBrightness(brightness);
|
|
clear_all_leds();
|
|
FastLED.show();
|
|
// TODO: Set up some kind of initialization sequence. But it should be based on an effect or similarly supporting the
|
|
// cooperative multithreading. delay() calls are uncalled for. ^^
|
|
}
|
|
|
|
void clear_all_leds() {
|
|
for(int i = 0; i < WORDCLOCK_NUM_LEDS; i++) {
|
|
leds[i].setRGB(0, 0, 0);
|
|
}
|
|
}
|
|
|
|
void display_word(const int word[][2], const CRGB& c) {
|
|
for (int i=1; i < word[0][0] + 1; i++) {
|
|
leds[map_coords_to_strip(word[i][0], word[i][1])].setRGB(c.r, c.g, c.b);
|
|
}
|
|
}
|
|
|
|
void display_minutes(int minutes, const CRGB& color) {
|
|
int five_minute_chunk = minutes / 5;
|
|
|
|
switch (five_minute_chunk)
|
|
{
|
|
case 0: // sharp
|
|
display_word(WORD_OCLOCK, color); ESP_LOGD("minute", "oclock "); break;
|
|
case 1: // five past
|
|
display_word(WORD_FIVE_MINUTES, color); ESP_LOGD("minute", "five past "); break;
|
|
case 2: // ten past
|
|
display_word(WORD_TEN_MINUTES, color); ESP_LOGD("minute", "ten past "); break;
|
|
case 3: // quarter past
|
|
display_word(WORD_QUARTER, color); ESP_LOGD("minute", "quarter past "); break;
|
|
case 4: // twenty past
|
|
display_word(WORD_TWENTY, color); ESP_LOGD("minute", "twenty past "); break;
|
|
case 5: // twenty five past
|
|
display_word(WORD_TWENTY, color); display_word(WORD_FIVE_MINUTES, color);
|
|
ESP_LOGD("minute", "twenty five past "); break;
|
|
case 6: // half past
|
|
display_word(WORD_HALF, color); ESP_LOGD("minute", "half past "); break;
|
|
case 7: // twenty five to
|
|
display_word(WORD_TWENTY, color); display_word(WORD_FIVE_MINUTES, color);
|
|
ESP_LOGD("minute", "twenty five to "); break;
|
|
case 8: // twenty to
|
|
display_word(WORD_TWENTY, color); ESP_LOGD("minute", "twenty to "); break;
|
|
case 9: // quarter to
|
|
display_word(WORD_QUARTER, color); ESP_LOGD("minute", "quarter to "); break;
|
|
case 10: // ten to
|
|
display_word(WORD_TEN_MINUTES, color); ESP_LOGD("minute", "ten to "); break;
|
|
case 11: // five to
|
|
display_word(WORD_FIVE_MINUTES, color); ESP_LOGD("minute", "five to "); break;
|
|
default:
|
|
break;
|
|
}
|
|
if (five_minute_chunk > 6) {
|
|
display_word(WORD_TO, color);
|
|
} else if (five_minute_chunk > 0) {
|
|
display_word(WORD_PAST, color);
|
|
}
|
|
}
|
|
|
|
void display_hour(int hour, int minutes, const CRGB& color) {
|
|
int five_minute_chunk = minutes / 5;
|
|
if (five_minute_chunk > 6) {
|
|
hour += 1;
|
|
}
|
|
|
|
switch (hour % 12)
|
|
{
|
|
case 0: // twelve
|
|
display_word(WORD_TWELFE, color); ESP_LOGD("hour", "twelve "); break;
|
|
case 1: // one
|
|
display_word(WORD_ONE, color); ESP_LOGD("hour", "one "); break;
|
|
case 2: // two
|
|
display_word(WORD_TWO, color); ESP_LOGD("hour", "two "); break;
|
|
case 3: // three
|
|
display_word(WORD_THREE, color); ESP_LOGD("hour", "three "); break;
|
|
case 4: // four
|
|
display_word(WORD_FOUR, color); ESP_LOGD("hour", "four "); break;
|
|
case 5: // five
|
|
display_word(WORD_FIVE, color); ESP_LOGD("hour", "five "); break;
|
|
case 6: // six
|
|
display_word(WORD_SIX, color); ESP_LOGD("hour", "six "); break;
|
|
case 7: // seven
|
|
display_word(WORD_SEVEN, color); ESP_LOGD("hour", "seven "); break;
|
|
case 8: // eight
|
|
display_word(WORD_EIGHT, color); ESP_LOGD("hour", "eight "); break;
|
|
case 9: // nine
|
|
display_word(WORD_NINE, color); ESP_LOGD("hour", "nine "); break;
|
|
case 10: // ten
|
|
display_word(WORD_TEN, color); ESP_LOGD("hour", "ten "); break;
|
|
case 11: // eleven
|
|
display_word(WORD_ELEVEN, color); ESP_LOGD("hour", "eleven "); break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void display_time(int hour, int minutes, const CRGB& color) {
|
|
display_word(WORD_IT_IS, color);
|
|
display_hour(hour, minutes, color);
|
|
display_minutes(minutes, color);
|
|
}
|
|
|
|
void loop() override {
|
|
auto time = id(current_time).now();
|
|
// https://www.esphome.io/api/classesphome_1_1light_1_1_light_color_values.html LightColorValues Class
|
|
auto fastledlight2 = id(fastledlight).current_values;
|
|
//convert float 0.0 till 1.0 into int 0 till 255
|
|
red = (int) (fastledlight2.get_red() * 125);
|
|
green = (int) (fastledlight2.get_green() * 125);
|
|
blue = (int) (fastledlight2.get_blue() * 125);
|
|
brightness = 0;
|
|
//check if light is on and set brightness
|
|
if (fastledlight2.get_state() > 0 ) {
|
|
brightness = (int) (fastledlight2.get_brightness()*125);
|
|
} else {
|
|
ESP_LOGD("loop", "fastledlight state off - b: %i rgb %i %i %i", brightness, red, green, blue); delay(100);
|
|
}
|
|
|
|
FastLED.setBrightness(brightness);
|
|
FastLED.show();
|
|
//check if valid time. Blink red,green,blue until valid time is present
|
|
if (!time.is_valid() == false) {
|
|
// do something to show that the clock isn't dead. Maybe I can instantiate and effect and use that for this.
|
|
}
|
|
else {
|
|
if(time.hour != hour || time.minute != minute) {
|
|
hour = time.hour;
|
|
minute = time.minute;
|
|
if (hour >= 0 && time.is_valid() == true){
|
|
|
|
clear_all_leds();
|
|
display_time(time.hour, time.minute, CRGB(red, green, blue));
|
|
FastLED.show();
|
|
|
|
ESP_LOGE("loop", "Update Time: %i:%i Brightness: %i RGB: %i-%i-%i", time.hour, time.minute, brightness, red, green, blue);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|