feat: 🎉 add first try at an esphome component for this wordclock firmware
The code is loosely based on https://github.com/leinich/ha-wordclock-esphome
This commit is contained in:
parent
34656ff942
commit
4c612fc462
2 changed files with 283 additions and 0 deletions
222
wordclock.h
Normal file
222
wordclock.h
Normal file
|
|
@ -0,0 +1,222 @@
|
|||
#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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
61
wordclock.yaml
Normal file
61
wordclock.yaml
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
esphome:
|
||||
name: "${devicename}"
|
||||
platformio_options:
|
||||
build_flags:
|
||||
- -DWORDCLOCK_DATA_PIN=22
|
||||
- -DWORDCLOCK_NUM_LEDS=198
|
||||
includes:
|
||||
- wordclock.h
|
||||
|
||||
esp32:
|
||||
board: wemos_d1_mini32
|
||||
framework:
|
||||
type: arduino
|
||||
|
||||
substitutions:
|
||||
devicename: wordclock
|
||||
friendly_name: "Wordclock"
|
||||
light_friendly_name: "Wordclock Light"
|
||||
|
||||
wifi:
|
||||
ssid: !secret wifi_ssid
|
||||
password: !secret wifi_password
|
||||
ap:
|
||||
ssid: "${devicename}"
|
||||
password: !secret ap_password
|
||||
manual_ip:
|
||||
static_ip: !secret manualip_static_ip
|
||||
gateway: !secret manualip_gateway
|
||||
subnet: !secret manualip_subnet
|
||||
|
||||
api:
|
||||
|
||||
ota:
|
||||
password: "${devicename}"
|
||||
|
||||
logger:
|
||||
# esp8266_store_log_strings_in_flash: false
|
||||
web_server:
|
||||
port: 80
|
||||
|
||||
light:
|
||||
- platform: fastled_clockless
|
||||
id: fastledlight
|
||||
chipset: WS2812
|
||||
pin: 23
|
||||
num_leds: 198
|
||||
rgb_order: GRB
|
||||
name: ${light_friendly_name}
|
||||
restore_mode: ALWAYS_ON
|
||||
|
||||
time:
|
||||
- platform: sntp
|
||||
id: current_time
|
||||
timezone: !secret timezone
|
||||
|
||||
custom_component:
|
||||
- lambda: |-
|
||||
auto wordclock = new Wordclock();
|
||||
return {wordclock};
|
||||
components:
|
||||
- id: wordclock
|
||||
Loading…
Add table
Add a link
Reference in a new issue