Replace base64 files with library. Major refactoring.

This commit is contained in:
2024-04-03 01:10:15 +02:00
parent ef6061fc21
commit 0543b9c0c7
9 changed files with 220 additions and 342 deletions

View File

@@ -26,7 +26,8 @@
#include <Adafruit_GFX.h> // https://github.com/adafruit/Adafruit-GFX-Library
#include <Adafruit_NeoMatrix.h> // https://github.com/adafruit/Adafruit_NeoMatrix
#include <Adafruit_NeoPixel.h> // NeoPixel library used to run the NeoPixel LEDs: https://github.com/adafruit/Adafruit_NeoPixel
#include <EEPROM.h> //from ESP8266 Arduino Core (automatically installed when ESP8266 was installed via Boardmanager)
#include <base64.hpp>
#include <EEPROM.h> //from ESP8266 Arduino Core (automatically installed when ESP8266 was installed via Boardmanager)
#include <ESP8266WebServer.h>
#include <ESP8266WiFi.h>
#include <time.h>
@@ -34,7 +35,6 @@
// own libraries
#include "animation_functions.h"
#include "base64_wrapper.h" // copied from https://github.com/Xander-Electronics/Base64
#include "led_matrix.h"
#include "littlefs_wrapper.h"
#include "ota_functions.h"
@@ -54,43 +54,43 @@ Adafruit_NeoMatrix matrix = Adafruit_NeoMatrix(MATRIX_WIDTH, MATRIX_HEIGHT + 1,
NEO_GRB + NEO_KHZ800); // NeoMatrix
ESP8266WebServer webserver(HTTP_PORT); // Webserver
LEDMatrix led_matrix = LEDMatrix(&matrix, DEFAULT_BRIGHTNESS, &logger); // NeoMatrix wrapper
struct tm time_info; // Structure tm holds time information
time_t time_now; // Seconds since Epoch (1970) - UTC
// ----------------------------------------------------------------------------------
// STATIC VARIABLES
// ----------------------------------------------------------------------------------
// EEPROM values
static EepromLayout_st eeprom_buffer = {{0, 0, 0, 0}, {0U, 0U, 0U, false}, {0U, 0U, 0U, 0U}};
static Brightness_st *brightness_ps = &eeprom_buffer.brightness_values;
static Color_st *colors_ps = &eeprom_buffer.color_values;
static NightModeTimes_st *night_mode_times_ps = &eeprom_buffer.night_mode_times;
static Brightness_st *const brightness_ps = &eeprom_buffer.brightness_values;
static Color_st *const colors_ps = &eeprom_buffer.color_values;
static NightModeTimes_st *const night_mode_times_ps = &eeprom_buffer.night_mode_times;
// Games
static Pong pong = Pong(&led_matrix, &logger);
static Snake snake = Snake(&led_matrix, &logger);
static Tetris tetris = Tetris(&led_matrix, &logger);
static uint32 last_ntp_update_us = 0; // Time of last NTP update
static char strftime_buf[64]; // Time string buffer
// Time
static struct tm time_info; // Structure tm holds time information
static time_t time_now; // Seconds since Epoch (1970) - UTC
static bool flg_night_mode = false; // State of nightmode
static bool flg_reset_wifi_creds = false; // Used to reset stored wifi credentials
static bool spiral_direction = false;
// NTP
static uint32 last_ntp_update_us = 0; // Time of last NTP update
// State variables
static bool flg_night_mode = false; // State of nightmode
static bool flg_reset_wifi_creds = false; // Used to reset stored wifi credentials
static float filter_factor = DEFAULT_SMOOTHING_FACTOR; // Stores smoothing factor for led transition, value of 1 represents no smoothing.
static int watchdog_counter = 30; // Watchdog counter to trigger restart if NTP update was not possible 30 times in a row (5min)
static uint32 last_led_direct_us = 0; // Time of last direct LED command (=> fall back to normal mode after timeout)
static uint32_t heartbeat_counter = 0; // Heartbeat on-time in seconds
static uint32_t main_color_clock = colors_24bit[2]; // Color of the clock and digital clock
static uint32_t main_color_snake = colors_24bit[1]; // Color of the random snake animation
static uint8_t current_brightness = DEFAULT_BRIGHTNESS; // Current brightness of LEDs
static uint8_t current_state = (uint8_t)ST_CLOCK; // Stores current state
static const String state_names[NUM_STATES] = {"Clock", "DiClock", "Spiral", "Tetris", "Snake", "PingPong", "Hearts"};
static const uint32_t period_timings[NUM_STATES] = {PERIOD_CLOCK_UPDATE_US, PERIOD_CLOCK_UPDATE_US,
PERIOD_ANIMATION_US, PERIOD_TETRIS_US, PERIOD_SNAKE_US,
PERIOD_PONG_US, PERIOD_ANIMATION_US};
// Other variables
static uint32 last_led_direct_us = 0; // Time of last direct LED command (=> fall back to normal mode after timeout)
static uint32_t heartbeat_counter = 0; // Heartbeat on-time in seconds
// Quarterly brightness factor for dynamic brightness (4 quarters a 24 hours)
static const float qtly_brightness_factor[96] = {
// Const definitions
static const String state_names[NUM_STATES] = {"Clock", "DiClock", "Spiral", "Tetris", "Snake", "PingPong", "Hearts"}; // all clock states
static const float qtly_brightness_factor[96] = { // Quarterly brightness factor for dynamic brightness (4 quarters a 24 hours)
0.0f, 0.0f, 0.0f, 0.001f, 0.003f, 0.007f, 0.014f, 0.026f, 0.044f, 0.069f, 0.101f, 0.143f, 0.194f, 0.253f, 0.32f,
0.392f, 0.468f, 0.545f, 0.62f, 0.691f, 0.755f, 0.811f, 0.858f, 0.896f, 0.927f, 0.949f, 0.966f, 0.978f, 0.986f,
0.991f, 0.995f, 0.997f, 0.998f, 0.999f, 0.999f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
@@ -98,7 +98,9 @@ static const float qtly_brightness_factor[96] = {
0.998f, 0.997f, 0.995f, 0.991f, 0.986f, 0.978f, 0.966f, 0.949f, 0.927f, 0.896f, 0.858f, 0.811f, 0.755f, 0.691f,
0.62f, 0.545f, 0.468f, 0.392f, 0.32f, 0.253f, 0.194f, 0.143f, 0.101f, 0.069f, 0.044f, 0.026f, 0.014f, 0.007f,
0.003f, 0.001f, 0.0f, 0.0f};
static const uint32_t period_timings[NUM_STATES] = {PERIOD_CLOCK_UPDATE_US, PERIOD_CLOCK_UPDATE_US,
PERIOD_ANIMATION_US, PERIOD_TETRIS_US, PERIOD_SNAKE_US,
PERIOD_PONG_US, PERIOD_ANIMATION_US};
// ----------------------------------------------------------------------------------
// SETUP
// ----------------------------------------------------------------------------------
@@ -112,10 +114,10 @@ void setup()
Serial.println();
// Reset info
rst_info *resetInfo = ESP.getResetInfoPtr();
Serial.printf("Reset reason: %u\n", resetInfo->reason);
Serial.printf("Reset cause: %u\n", resetInfo->exccause);
Serial.printf("Reset address: %u\n", resetInfo->excvaddr);
rst_info *reset_info = ESP.getResetInfoPtr();
Serial.printf("Reset reason: %u\n", reset_info->reason);
Serial.printf("Reset cause: %u\n", reset_info->exccause);
Serial.printf("Reset address: %u\n", reset_info->excvaddr);
Serial.println();
// Init EEPROM
@@ -134,7 +136,7 @@ void setup()
led_matrix.setup_matrix();
led_matrix.set_current_limit(CURRENT_LIMIT_LED);
// Turn on minutes leds (blue)
// Turn on minutes LEDs (blue)
led_matrix.set_min_indicator(15, colors_24bit[6]);
led_matrix.draw_on_matrix_instant();
@@ -148,11 +150,12 @@ void setup()
// If you get here you have connected to the WiFi
Serial.printf("Connected, IP address: ");
Serial.println(WiFi.localIP());
// ESP8266 tries to reconnect automatically when the connection is lost
WiFi.setAutoReconnect(true);
WiFi.persistent(true);
// Turn off minutes leds
// Turn off minutes LEDs
led_matrix.set_min_indicator(15, 0);
led_matrix.draw_on_matrix_instant();
@@ -169,44 +172,10 @@ void setup()
// create UDP Logger to send logging messages via UDP multicast
logger = UDPLogger(WiFi.localIP(), LOGGER_MULTICAST_IP, LOGGER_MULTICAST_PORT, "Wordclock 2.0");
logger.log_string("Start program\n");
logger.log_string("Sketchname: " + String(__FILE__));
logger.log_string("Build: " + String(__TIMESTAMP__));
logger.log_string("IP: " + WiFi.localIP().toString());
logger.log_string("Reset Reason: " + ESP.getResetReason());
if (resetInfo->reason != REASON_SOFT_RESTART) // only if there was a cold start/hard reset
if (reset_info->reason != REASON_SOFT_RESTART) // only if there was a cold start/hard reset
{
// quickly test each LED
for (int16_t row = 0; row < MATRIX_HEIGHT; row++)
{
for (int16_t col = 0; col < MATRIX_WIDTH; col++)
{
matrix.fillScreen(0);
matrix.drawPixel(col, row, LEDMatrix::color_24_to_16bit(colors_24bit[2]));
matrix.show();
delay(10);
}
}
// clear Matrix
matrix.fillScreen(0);
matrix.show();
delay(200);
// display IP
uint8_t address = WiFi.localIP()[3];
led_matrix.print_char(1, 0, 'I', main_color_clock);
led_matrix.print_char(5, 0, 'P', main_color_clock);
led_matrix.print_number(0, 6, (address / 100), main_color_clock);
led_matrix.print_number(4, 6, (address / 10) % 10, main_color_clock);
led_matrix.print_number(8, 6, address % 10, main_color_clock);
led_matrix.draw_on_matrix_instant();
delay(2000);
// clear matrix
led_matrix.flush();
led_matrix.draw_on_matrix_instant();
cold_start_setup();
}
// setup NTP
@@ -230,12 +199,11 @@ void setup()
// Set range limits
limit_value_ranges();
logger.log_string("Nightmode starts at: " + String(night_mode_times_ps->start_hour) + ":" + String(night_mode_times_ps->start_min));
logger.log_string("Nightmode ends at: " + String(night_mode_times_ps->end_hour) + ":" + String(night_mode_times_ps->end_min));
// Update brightness
current_brightness = update_brightness();
logger.log_string("Brightness: " + String(((uint16_t)current_brightness * 100) / UINT8_MAX) + "%");
// Send logging data
log_data();
}
// ----------------------------------------------------------------------------------
@@ -305,89 +273,155 @@ void loop()
// ----------------------------------------------------------------------------------
// OTHER FUNCTIONS
// ----------------------------------------------------------------------------------
/**
* @brief Log information
*
*/
void log_data()
{
logger.log_string("Start program\n");
logger.log_string("Sketchname: " + String(__FILE__));
logger.log_string("Build: " + String(__TIMESTAMP__));
logger.log_string("IP: " + WiFi.localIP().toString());
logger.log_string("Reset Reason: " + ESP.getResetReason());
logger.log_string("Nightmode starts at: " + String(night_mode_times_ps->start_hour) + ":" + String(night_mode_times_ps->start_min));
logger.log_string("Nightmode ends at: " + String(night_mode_times_ps->end_hour) + ":" + String(night_mode_times_ps->end_min));
logger.log_string("Brightness: " + String(((uint16_t)current_brightness * 100) / UINT8_MAX) + "%");
}
/**
* @brief Test all LEDs and display IP address
*
*/
void cold_start_setup()
{
// quickly test each LED
for (int16_t row = 0; row < MATRIX_HEIGHT; row++)
{
for (int16_t col = 0; col < MATRIX_WIDTH; col++)
{
matrix.fillScreen(0);
matrix.drawPixel(col, row, LEDMatrix::color_24_to_16bit(colors_24bit[2]));
matrix.show();
delay(10);
}
}
// clear Matrix
matrix.fillScreen(0);
matrix.show();
delay(200);
// display IP
uint8_t address = WiFi.localIP()[3];
led_matrix.print_char(1, 0, 'I', main_color_clock);
led_matrix.print_char(5, 0, 'P', main_color_clock);
led_matrix.print_number(0, 6, (address / 100), main_color_clock);
led_matrix.print_number(4, 6, (address / 10) % 10, main_color_clock);
led_matrix.print_number(8, 6, address % 10, main_color_clock);
led_matrix.draw_on_matrix_instant();
delay(2000);
// clear matrix
led_matrix.flush();
led_matrix.draw_on_matrix_instant();
}
/**
* @brief Updates the NTP time
*
* @return boolean - true if NTP update was successful, false otherwise
*/
bool get_ntp_time(uint32 usec)
bool get_ntp_time(uint32 timeout)
{
uint32 start_time_us = system_get_time();
do
{
time(&time_now);
localtime_r(&time_now, &time_info);
delay(10);
} while (((system_get_time() - start_time_us) <= usec) && (time_info.tm_year < (NTP_MININUM_RX_YEAR - NTP_MININUM_YEAR)));
yield();
} while (((system_get_time() - start_time_us) <= timeout) && (time_info.tm_year < (NTP_MININUM_RX_YEAR - NTP_MININUM_YEAR)));
logger.log_string(String("NTP-Update duration: " + String(system_get_time() - start_time_us) + String("us")));
return ((time_info.tm_year <= (NTP_MININUM_RX_YEAR - NTP_MININUM_YEAR)) ? false : true);
}
/**
* @brief Log local_time.
*
* @param local_time
*/
void log_time(tm local_time)
{
char strftime_buf[64]; // Time string buffer
strftime(strftime_buf, sizeof(strftime_buf), "%c", &time_info);
logger.log_string(String(strftime_buf));
}
/**
* @brief Update and control word clock states.
*/
void handle_current_state()
{
switch (current_state)
{
case ST_CLOCK: // state clock
case ST_CLOCK: // state clock
{
(void)show_string_on_clock(time_to_string((uint8_t)time_info.tm_hour, (uint8_t)time_info.tm_min), main_color_clock);
draw_minute_indicator((uint8_t)time_info.tm_min, main_color_clock);
break;
}
case ST_DICLOCK: // state diclock
{
show_digital_clock((uint8_t)time_info.tm_hour, (uint8_t)time_info.tm_min, main_color_clock);
break;
}
case ST_SPIRAL: // state spiral
{
int res = draw_spiral(false, spiral_direction, MATRIX_WIDTH - 2);
if ((bool)res && spiral_direction == 0)
{
(void)show_string_on_clock(time_to_string((uint8_t)time_info.tm_hour, (uint8_t)time_info.tm_min), main_color_clock);
draw_minute_indicator((uint8_t)time_info.tm_min, main_color_clock);
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_DICLOCK: // state diclock
else if (res && spiral_direction == 1)
{
show_digital_clock((uint8_t)time_info.tm_hour, (uint8_t)time_info.tm_min, main_color_clock);
break;
}
case ST_SPIRAL: // state spiral
{
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;
}
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;
// 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;
}
}
}
@@ -456,8 +490,8 @@ void check_night_mode()
*/
void ntp_time_update(uint32 max_update_time)
{
// NTP time update
bool ntp_retval = get_ntp_time(max_update_time);
static int watchdog_counter = NTP_WATCHDOG_COUNTER_INIT; // Watchdog counter to trigger restart if NTP update was not possible 30 times in a row (5min)
bool ntp_retval = get_ntp_time(max_update_time); // NTP time update
if (ntp_retval == true)
{
@@ -491,34 +525,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;
}
}
}
@@ -566,10 +600,9 @@ void handle_led_direct()
// base64 decoding
char base64data[dataLength];
data.toCharArray(base64data, dataLength);
int base64dataLen = (int)dataLength;
int decodedLength = Base64.decodedLength(base64data, base64dataLen);
char byteArray[decodedLength];
Base64.decode(byteArray, base64data, base64dataLen);
unsigned int decodedLength = decode_base64_length((unsigned char *)base64data, dataLength);
unsigned char byteArray[decodedLength];
decode_base64((unsigned char *)base64data, dataLength, byteArray);
for (unsigned int i = 0; i < dataLength; i += 4)
{
@@ -645,11 +678,11 @@ void handle_button()
void set_main_color(uint8_t red, uint8_t green, uint8_t blue)
{
main_color_clock = LEDMatrix::color_24bit(red, green, blue);
// Update colors and save color settings to EEPROM
colors_ps->blue = blue;
colors_ps->red = red;
colors_ps->green = green;
// save color settings to EEPROM
write_settings_to_EEPROM();
}