diff --git a/include/led_matrix.h b/include/led_matrix.h index 5b57598..ee0457b 100644 --- a/include/led_matrix.h +++ b/include/led_matrix.h @@ -1,24 +1,28 @@ #ifndef LEDMATRIX_H #define LEDMATRIX_H +#ifndef FASTLED_INTERNAL +#define FASTLED_INTERNAL +#endif + #include #include -#include +#include #include "wordclock_constants.h" #include "udp_logger.h" #define DEFAULT_CURRENT_LIMIT 9999 extern const uint32_t colors_24bit[NUM_COLORS]; - class LEDMatrix { public: - LEDMatrix(Adafruit_NeoMatrix * matrix, uint8_t brightness, UDPLogger * logger); + LEDMatrix(FastLED_NeoMatrix *matrix, uint8_t brightness, UDPLogger *logger); static uint16_t color_24_to_16bit(uint32_t color24bit); static uint32_t color_24bit(uint8_t r, uint8_t g, uint8_t b); static uint32_t interpolate_color_24bit(uint32_t color1, uint32_t color2, float factor); static uint32_t wheel(uint8_t WheelPos); + uint16_t get_fps(void); void draw_on_matrix_instant(); void draw_on_matrix_smooth(float factor); void flush(void); @@ -31,17 +35,17 @@ public: void setup_matrix(); private: - Adafruit_NeoMatrix * _neomatrix; - UDPLogger * _logger; + FastLED_NeoMatrix *_neomatrix; + UDPLogger *_logger; uint8_t _brightness; uint16_t _current_limit; // target representation of matrix as 2D array - uint32_t _target_grid[MATRIX_HEIGHT][MATRIX_WIDTH] = {0}; + uint32_t _target_grid[MATRIX_HEIGHT][MATRIX_WIDTH]; // current representation of matrix as 2D array - uint32_t _current_grid[MATRIX_HEIGHT][MATRIX_WIDTH] = {0}; + uint32_t _current_grid[MATRIX_HEIGHT][MATRIX_WIDTH]; // target representation of minutes indicator LEDs uint32_t _target_minute_indicators[4] = {0, 0, 0, 0}; @@ -53,4 +57,4 @@ private: uint16_t _calc_estimated_led_current(uint32_t color); }; -#endif /* LEDMATRIX_H */ \ No newline at end of file +#endif /* LEDMATRIX_H */ \ No newline at end of file diff --git a/include/wordclock_constants.h b/include/wordclock_constants.h index a031a45..15cf7b4 100644 --- a/include/wordclock_constants.h +++ b/include/wordclock_constants.h @@ -16,7 +16,7 @@ #define HTTP_PORT (80) // Standard HTTP port // ESP8266 Pins -#define NEOPIXEL_PIN (14) // pin to which the NeoPixels are attached +#define FASTLED_PIN (14) // pin to which the LEDs are attached #define BUTTON_PIN (5) // pin to which the button is attached // Time limits @@ -36,8 +36,8 @@ #define PERIOD_ANIMATION_US (200 * 1000) // 200ms #define PERIOD_CLOCK_UPDATE_US (1 * 1000 * 1000) // Must be 1s! Do not change! #define PERIOD_HEARTBEAT_US (1 * 1000 * 1000) // 1s -#define PERIOD_MATRIX_UPDATE_US (100 * 1000) // 100ms -#define PERIOD_NIGHTMODE_CHECK_US (20 * 1000 * 1000) // 20s +#define PERIOD_MATRIX_UPDATE_US (33 * 1000) // 33ms +#define PERIOD_NIGHTMODE_CHECK_US (30 * 1000 * 1000) // 30s #define PERIOD_TIME_UPDATE_US (1 * 1000 * 1000) // 1000ms #define PERIOD_PONG_US (10 * 1000) // 10ms #define PERIOD_SNAKE_US (50 * 1000) // 50ms @@ -63,10 +63,12 @@ // Number of colors in colors array #define NUM_COLORS (7) +#define COLOR_ORDER GRB // WS2812B color order // LED matrix size #define MATRIX_WIDTH (11) #define MATRIX_HEIGHT (11) +#define NUM_MATRIX (MATRIX_WIDTH * (MATRIX_HEIGHT + 1)) // NTP macros #define BUILD_YEAR (__DATE__ + 7) // Will expand to current year at compile time as string. diff --git a/src/matrix/led_matrix.cpp b/src/matrix/led_matrix.cpp index 7f73c4e..261849c 100644 --- a/src/matrix/led_matrix.cpp +++ b/src/matrix/led_matrix.cpp @@ -21,7 +21,7 @@ const uint32_t colors_24bit[NUM_COLORS] = { * @param mybrightness the initial brightness of the leds * @param mylogger pointer to the UDPLogger object */ -LEDMatrix::LEDMatrix(Adafruit_NeoMatrix *matrix, uint8_t brightness, UDPLogger *logger) +LEDMatrix::LEDMatrix(FastLED_NeoMatrix *matrix, uint8_t brightness, UDPLogger *logger) { _neomatrix = matrix; _brightness = brightness; @@ -181,6 +181,11 @@ void LEDMatrix::flush(void) _target_minute_indicators[3] = 0; } +uint16_t LEDMatrix::get_fps(void) +{ + return FastLED.getFPS(); +} + /** * @brief Write target pixels directly to leds * diff --git a/src/wordclock_esp8266.cpp b/src/wordclock_esp8266.cpp index 94ed253..b3381f6 100644 --- a/src/wordclock_esp8266.cpp +++ b/src/wordclock_esp8266.cpp @@ -23,17 +23,17 @@ #include #include "wordclock_esp8266.h" -#include // https://github.com/adafruit/Adafruit-GFX-Library -#include // https://github.com/adafruit/Adafruit_NeoMatrix -#include // NeoPixel library used to run the NeoPixel LEDs: https://github.com/adafruit/Adafruit_NeoPixel +#include // https://github.com/adafruit/Adafruit-GFX-Library #include -#include // from ESP8266 Arduino Core (automatically installed when ESP8266 was installed via Boardmanager) +#include // from ESP8266 Arduino Core (automatically installed when ESP8266 was installed via Boardmanager) #include #include +#include #include // https://github.com/tzapu/WiFiManager WiFi Configuration Magic // own libraries #include "animation_functions.h" +#include "diagnosis.h" #include "led_matrix.h" #include "littlefs_wrapper.h" #include "ota_functions.h" @@ -49,10 +49,11 @@ // GLOBAL VARIABLES // ---------------------------------------------------------------------------------- UDPLogger logger; // Global UDP logger instance -Adafruit_NeoMatrix matrix = Adafruit_NeoMatrix(MATRIX_WIDTH, MATRIX_HEIGHT + 1, NEOPIXEL_PIN, - NEO_MATRIX_TOP + NEO_MATRIX_LEFT + NEO_MATRIX_ROWS + NEO_MATRIX_ZIGZAG, - NEO_GRB + NEO_KHZ800); // NeoMatrix -LEDMatrix led_matrix = LEDMatrix(&matrix, DEFAULT_BRIGHTNESS, &logger); // NeoMatrix wrapper + +CRGB leds[NUM_MATRIX]; // LED array for FastLED +FastLED_NeoMatrix matrix = FastLED_NeoMatrix(leds, MATRIX_WIDTH, (MATRIX_HEIGHT + 1), + NEO_MATRIX_TOP + NEO_MATRIX_LEFT + NEO_MATRIX_ROWS + NEO_MATRIX_ZIGZAG); +LEDMatrix led_matrix = LEDMatrix(&matrix, DEFAULT_BRIGHTNESS, &logger); // FastLED_NeoMatrix wrapper ESP8266WebServer webserver(HTTP_PORT); // Webserver // ---------------------------------------------------------------------------------- @@ -78,7 +79,7 @@ static bool flg_reset_wifi_creds = false; // Used to reset stored static float filter_factor = DEFAULT_SMOOTHING_FACTOR; // Stores smoothing factor for led transition, value of 1 represents no smoothing. static uint32_t main_color_clock = colors_24bit[2]; // Color of the clock and digital clock static uint8_t current_brightness = DEFAULT_BRIGHTNESS; // Current brightness of LEDs -static ClockState_en current_state = ST_CLOCK; // Stores current state +static ClockState_en current_state = ST_CLOCK; // Stores current state // Other variables static uint32 last_led_direct_us = 0; // Time of last direct LED command (=> fall back to normal mode after timeout) @@ -98,7 +99,6 @@ static const uint32_t period_timings[NUM_STATES] = {PERIOD_TIME_UPDATE_US, PERIO PERIOD_ANIMATION_US, PERIOD_TETRIS_US, PERIOD_SNAKE_US, PERIOD_PONG_US, PERIOD_ANIMATION_US}; - // ---------------------------------------------------------------------------------- // SETUP // ---------------------------------------------------------------------------------- @@ -118,6 +118,9 @@ void setup() Serial.printf("Reset address: %u\n", reset_info->excvaddr); Serial.println(); + // Init FastLED + FastLED.addLeds(leds, NUM_MATRIX); + // Init EEPROM EEPROM.begin(EEPROM_SIZE); @@ -234,7 +237,6 @@ void loop() { send_heartbeat(); // send heartbeat update last_heartbeat_us = system_get_time(); - delay(10); } if (!flg_night_mode && ((current_time_us - last_animation_step_us) > period_timings[current_state]) && @@ -242,7 +244,6 @@ void loop() { handle_current_state(); // handle current state last_animation_step_us = system_get_time(); - delay(10); } if ((current_time_us - last_brightness_update_us) >= PERIOD_BRIGHTNESS_UPDATE_US) @@ -250,14 +251,12 @@ void loop() current_brightness = update_brightness(); // update brightness logger.log_string("Brightness: " + String(((uint16_t)current_brightness * 100) / UINT8_MAX) + "%"); last_brightness_update_us = system_get_time(); - delay(10); } if ((current_time_us - last_matrix_update_us) >= PERIOD_MATRIX_UPDATE_US) { update_matrix(); // update matrix last_matrix_update_us = system_get_time(); - delay(10); } if ((current_time_us - last_time_update_us) >= PERIOD_TIME_UPDATE_US) @@ -271,7 +270,7 @@ void loop() ESP.restart(); } - last_time_update_us = system_get_time(); + last_time_update_us = system_get_time(); } if ((current_time_us - last_nightmode_check_us) >= PERIOD_NIGHTMODE_CHECK_US) @@ -347,87 +346,87 @@ void handle_current_state() { switch (current_state) { - case ST_CLOCK: // state clock + case ST_CLOCK: // state clock + { + if (tm_mgr.tm_state() == TM_NORMAL) { - if (tm_mgr.tm_state() == TM_NORMAL) - { - (void)show_string_on_clock(time_to_string((uint8_t)tm_mgr.hour(), (uint8_t)tm_mgr.minute()), main_color_clock); - draw_minute_indicator((uint8_t)tm_mgr.minute(), main_color_clock); - } - else if (tm_mgr.ntp_sync_overdue()) // if NTP sync is overdue - { - (void)show_string_on_clock(time_to_string((uint8_t)tm_mgr.hour(), (uint8_t)tm_mgr.minute()), main_color_clock); - draw_minute_indicator((uint8_t)tm_mgr.minute(), colors_24bit[6]); // in blue to indicate a network problem - } - else // if no NTP sync has been done, only show 4 blue minute indicators - { - // clear matrix - led_matrix.flush(); - // Turn on minutes LEDs (blue) - led_matrix.set_min_indicator((uint8_t)0b1111, colors_24bit[6]); - led_matrix.draw_on_matrix_instant(); - } - break; + (void)show_string_on_clock(time_to_string((uint8_t)tm_mgr.hour(), (uint8_t)tm_mgr.minute()), main_color_clock); + draw_minute_indicator((uint8_t)tm_mgr.minute(), main_color_clock); } - case ST_DICLOCK: // state diclock + else if (tm_mgr.ntp_sync_overdue()) // if NTP sync is overdue { - if (tm_mgr.ntp_sync_successful()) - { - show_digital_clock((uint8_t)tm_mgr.hour(), (uint8_t)tm_mgr.minute(), main_color_clock); - } - else - { - // clear matrix - led_matrix.flush(); - // Turn on minutes LEDs (blue) - led_matrix.set_min_indicator((uint8_t)0b1111, colors_24bit[6]); - led_matrix.draw_on_matrix_instant(); - } - break; + (void)show_string_on_clock(time_to_string((uint8_t)tm_mgr.hour(), (uint8_t)tm_mgr.minute()), main_color_clock); + draw_minute_indicator((uint8_t)tm_mgr.minute(), colors_24bit[6]); // in blue to indicate a network problem } - case ST_SPIRAL: // state spiral + else // if no NTP sync has been done, only show 4 blue minute indicators { - int res = draw_spiral(false, spiral_direction, MATRIX_WIDTH - 2); - if ((bool)res && spiral_direction == 0) - { - // change spiral direction to closing (draw empty LEDs) - spiral_direction = true; - // init spiral with new spiral direction - draw_spiral(true, spiral_direction, MATRIX_WIDTH - 1); - } - else if (res && spiral_direction == 1) - { - // reset spiral direction to normal drawing LEDs - spiral_direction = false; - // init spiral with new spiral direction - draw_spiral(true, spiral_direction, MATRIX_WIDTH - 1); - } - break; + // clear matrix + led_matrix.flush(); + // Turn on minutes LEDs (blue) + led_matrix.set_min_indicator((uint8_t)0b1111, colors_24bit[6]); + led_matrix.draw_on_matrix_instant(); } - case ST_TETRIS: // state tetris + break; + } + case ST_DICLOCK: // state diclock + { + if (tm_mgr.ntp_sync_successful()) { - tetris.loopCycle(); - break; + show_digital_clock((uint8_t)tm_mgr.hour(), (uint8_t)tm_mgr.minute(), main_color_clock); } - case ST_SNAKE: // state snake + else { - snake.loopCycle(); - break; + // clear matrix + led_matrix.flush(); + // Turn on minutes LEDs (blue) + led_matrix.set_min_indicator((uint8_t)0b1111, colors_24bit[6]); + led_matrix.draw_on_matrix_instant(); } - case ST_PINGPONG: // state ping pong + break; + } + case ST_SPIRAL: // state spiral + { + int res = draw_spiral(false, spiral_direction, MATRIX_WIDTH - 2); + if ((bool)res && spiral_direction == 0) { - pong.loopCycle(); - break; + // change spiral direction to closing (draw empty LEDs) + spiral_direction = true; + // init spiral with new spiral direction + draw_spiral(true, spiral_direction, MATRIX_WIDTH - 1); } - case ST_HEARTS: + else if (res && spiral_direction == 1) { - draw_heart_animation(); - break; - } - default: - { - break; + // reset spiral direction to normal drawing LEDs + spiral_direction = false; + // init spiral with new spiral direction + draw_spiral(true, spiral_direction, MATRIX_WIDTH - 1); } + break; + } + case ST_TETRIS: // state tetris + { + tetris.loopCycle(); + break; + } + case ST_SNAKE: // state snake + { + snake.loopCycle(); + break; + } + case ST_PINGPONG: // state ping pong + { + pong.loopCycle(); + break; + } + case ST_HEARTS: + { + draw_heart_animation(); + break; + } + default: + { + break; + } } } @@ -503,34 +502,34 @@ void on_state_entry(uint8_t state) filter_factor = DEFAULT_SMOOTHING_FACTOR; switch (state) { - case ST_SPIRAL: - { - spiral_direction = 0; // Init spiral with normal drawing mode - draw_spiral(true, spiral_direction, MATRIX_WIDTH - 1); - break; - } - case ST_TETRIS: - { - filter_factor = 1.0f; // no smoothing - tetris.ctrlStart(); - break; - } - case ST_SNAKE: - { - filter_factor = 1.0f; // no smoothing - snake.initGame(); - break; - } - case ST_PINGPONG: - { - filter_factor = 1.0f; // no smoothing - pong.initGame(1); - break; - } - default: - { - break; - } + case ST_SPIRAL: + { + spiral_direction = 0; // Init spiral with normal drawing mode + draw_spiral(true, spiral_direction, MATRIX_WIDTH - 1); + break; + } + case ST_TETRIS: + { + filter_factor = 1.0f; // no smoothing + tetris.ctrlStart(); + break; + } + case ST_SNAKE: + { + filter_factor = 1.0f; // no smoothing + snake.initGame(); + break; + } + case ST_PINGPONG: + { + filter_factor = 1.0f; // no smoothing + pong.initGame(1); + break; + } + default: + { + break; + } } } @@ -546,7 +545,7 @@ void state_change(ClockState_en new_state) set_night_mode(false); // deactivate Nightmode } - led_matrix.flush(); // first clear matrix + led_matrix.flush(); // first clear matrix current_state = new_state; // set new state on_state_entry((uint8_t)current_state); logger.log_string("State change to: " + state_names[(uint8_t)current_state]); @@ -691,13 +690,16 @@ void draw_main_color() */ void handle_command() { + bool send204 = true; // flag to send 204 response // receive command and handle accordingly +#ifdef SERIAL_DEBUG for (uint8_t i = 0; i < webserver.args(); i++) { Serial.print(webserver.argName(i)); Serial.print(F(": ")); Serial.println(webserver.arg(i)); } +#endif /* SERIAL_DEBUG */ if (webserver.argName(0).equals("led")) // the parameter which was sent to this server is led color { @@ -798,7 +800,6 @@ void handle_command() else if (webserver.argName(0).equals("tetris")) { String cmd_str = webserver.arg(0); - // logger.log_string("Tetris cmd via Webserver to: " + cmd_str); if (cmd_str.equals("up")) { tetris.ctrlUp(); @@ -827,7 +828,6 @@ void handle_command() else if (webserver.argName(0).equals("snake")) { String cmd_str = webserver.arg(0); - // logger.log_string("Snake cmd via Webserver to: " + cmd_str); if (cmd_str.equals("up")) { snake.ctrlUp(); @@ -852,7 +852,6 @@ void handle_command() else if (webserver.argName(0).equals("pong")) { String cmd_str = webserver.arg(0); - // logger.log_string("Pong cmd via Webserver to: " + cmd_str); if (cmd_str.equals("up")) { pong.ctrlUp(1); @@ -866,8 +865,18 @@ void handle_command() pong.initGame(1); } } + else if (webserver.argName(0).equals("diag")) + { + String cmd_str = webserver.arg(0); + Diagnosis diag(&logger, &led_matrix); + webserver.send(200, "text/plain", diag.handle_command(cmd_str)); + send204 = false; + } - webserver.send(204, "text/plain", "No Content"); // this page doesn't send back content --> 204 + if (send204) + { + webserver.send(204, "text/plain", "No Content"); // this page doesn't send back content --> 204 + } } /** @@ -876,6 +885,7 @@ void handle_command() */ void handle_data_request() { +#ifdef SERIAL_DEBUG // receive data request and handle accordingly for (uint8_t i = 0; i < webserver.args(); i++) { @@ -883,7 +893,7 @@ void handle_data_request() Serial.print(F(": ")); Serial.println(webserver.arg(i)); } - +#endif /* SERIAL_DEBUG */ if (webserver.argName(0).equals("key")) { String message = "{";