// #define DEBUG 1 #include #include #include #include #include // Define the array of leds CRGB leds[LED_COUNT]; Timezone CopenhagenTime; int hue = 0; int max_brightness = 255; bool on_state[SEGMENT_COUNT * DIGIT_COUNT] = {false}; typedef enum : uint8_t { seg_a = 1<<0, // 00000001 seg_b = 1<<1, // 00000010 seg_c = 1<<2, // 00000100 seg_d = 1<<3, // ... seg_e = 1<<4, seg_f = 1<<5, seg_g = 1<<6 } segments; uint8_t symbols[] = { /* 0 */ seg_a | seg_b | seg_c | seg_d | seg_e | seg_f, /* 1 */ seg_b | seg_c, /* 2 */ seg_a | seg_b | seg_g | seg_e | seg_d, /* 3 */ seg_a | seg_b | seg_g | seg_c | seg_d, /* 4 */ seg_f | seg_g | seg_c | seg_b, /* 5 */ seg_a | seg_f | seg_g | seg_c | seg_d, /* 6 */ seg_a | seg_f | seg_g | seg_c | seg_d | seg_e, /* 7 */ seg_a | seg_b | seg_c, /* 8 */ seg_a | seg_b | seg_c | seg_d | seg_e | seg_f | seg_g, /* 9 */ seg_g | seg_f | seg_a | seg_b | seg_c | seg_d, /* ° */ seg_a | seg_b | seg_g | seg_f, /* C */ seg_a | seg_f | seg_e | seg_d }; uint8_t spin_animation[] = { seg_a, seg_b, seg_c, seg_d, seg_e, seg_f, }; uint8_t dual_spin_animation[] = { seg_a | seg_d, seg_b | seg_e, seg_c | seg_f, seg_d | seg_a, seg_e | seg_b, seg_f | seg_c, }; void display_symbols(uint8_t *symbols_mask) { // Fades all at once for (int brightness = 0; brightness <= max_brightness; brightness ++) { for (uint8_t digit_index = 0; digit_index < DIGIT_COUNT; digit_index++) { // Counts up by bitshifting uint8_t seg_counter = 1; for (int segment_index = 0; segment_index < SEGMENT_COUNT; segment_index++) { int digit_offset = digit_index * SEGMENT_COUNT; int led_raw_index = digit_offset + segment_index; int led_index = START_OFFSET + led_raw_index; bool previously_on = on_state[led_raw_index]; // Compare the current segment bit with the mask for the symbol bool turn_on = symbols_mask[digit_index] & seg_counter; if (turn_on) { // Turn on LED if(previously_on){ // Previously on, allow hue change leds[led_index] = CHSV(hue, SATURATION, max_brightness); } else { leds[led_index] = CHSV(hue, SATURATION, brightness); } } else { // Turn off LED if(previously_on) { leds[led_index] = CHSV(hue, SATURATION, max_brightness - brightness); } } if (brightness == max_brightness) { on_state[led_raw_index] = turn_on; } seg_counter = seg_counter<<1; } } FastLED.show(); delay(2); } } void display_number(int number) { uint8_t symbols_mask[DIGIT_COUNT]; // Start from the last digit for (int digit_index = DIGIT_COUNT - 1; digit_index >= 0; digit_index--) { symbols_mask[digit_index] = symbols[number % 10]; number /= 10; } display_symbols(symbols_mask); } void display_symbol(int digit_index, uint8_t symbol) { // Counts up by bitshifting uint8_t seg_counter = 1; for (int segment_index = 0; segment_index < SEGMENT_COUNT; segment_index++) { int offset = digit_index * SEGMENT_COUNT; int led_index = START_OFFSET + offset + segment_index; // Compare the current segment bit with the mask for the symbol if (symbol & seg_counter) { // Turn on LED leds[led_index] = CHSV(hue, SATURATION, max_brightness); FastLED.show(); } seg_counter = seg_counter<<1; } } void play_animation(uint8_t *animation, int animation_size, int segment_delay) { for (int i = 0; i < animation_size; i++){ for (int digit_index = 0; digit_index < 4; digit_index++) { display_symbol(digit_index, animation[i]); } hue += 16; FastLED.show(); delay(segment_delay); } } void update_clock() { // Format the time as a 4-digit integer int timeInt = CopenhagenTime.dateTime("Hi").toInt(); // Sadly string format display_number(timeInt); hue++; } void setup() { FastLED.addLeds(leds, LED_COUNT).setCorrection( TypicalLEDStrip ); FastLED.addLeds(leds, LED_COUNT); // GRB ordering is assumed // Turn off all LEDs FastLED.clear(); #ifdef DEBUG Serial.begin(9600); #endif setServer(NTP_SERVER); WiFi.setHostname("7-segment-esp-ws2811"); //define hostname WiFi.begin(WIFI_SSID, WIFI_PASSWORD); Serial.print("Connecting"); while (WiFi.status() != WL_CONNECTED) { if (millis() < WIFI_CONN_ANI_TIMEOUT) { play_animation(spin_animation, sizeof(spin_animation), 100); } else { // Turn off all LEDs FastLED.clear(); } } delay(100); hue = 0; // Turn off all LEDs FastLED.clear(); Serial.println(); Serial.print("Connected, IP address: "); Serial.println(WiFi.localIP()); waitForSync(); Serial.println("UTC: " + UTC.dateTime()); CopenhagenTime.setLocation("Europe/Copenhagen"); Serial.println("Copenhagen time: " + CopenhagenTime.dateTime()); update_clock(); } /* // Test each segment int i = 0; void loop() { leds[i] = CRGB::Red; FastLED.show(); delay(500); leds[i] = CRGB::Black; FastLED.show(); delay(500); i++; if (i > LED_COUNT - 1) { i = 0; } } */ void loop() { // Only update on each hole minute to avoid flickering int seconds = CopenhagenTime.dateTime("s").toInt(); int minutes = CopenhagenTime.dateTime("i").toInt(); if(minutes == 59 && seconds >= 55) { //if(minutes == 59 && seconds >= 55) { play_animation(dual_spin_animation, sizeof(spin_animation), 50); FastLED.clear(); } if(seconds != 0) { delay(10); return; } update_clock(); delay(1000); }