Refactoring for stability, etc. Add new animation.

This commit is contained in:
2023-08-27 01:24:01 +02:00
parent dccfbf758e
commit 078fa74afb
17 changed files with 1845 additions and 932 deletions

View File

@@ -49,20 +49,22 @@
// DEBUG
uint32_t dbg_counter = 0; // TODO RM
const String state_names[] = {"Clock", "DiClock", "Spiral", "Tetris", "Snake", "PingPong"};
const String state_names[] = {"Clock", "DiClock", "Spiral", "Tetris", "Snake", "PingPong", "Hearts"};
// PERIODS for each state (different for stateAutoChange or Manual mode)
const uint16_t PERIODS[2][NUM_STATES] = {{PERIOD_TIME_VISU_UPDATE, // stateAutoChange = 0
PERIOD_TIME_VISU_UPDATE,
PERIOD_ANIMATION,
PERIOD_TETRIS,
PERIOD_SNAKE,
PERIOD_PONG},
PERIOD_PONG,
PERIOD_ANIMATION},
{PERIOD_TIME_VISU_UPDATE, // stateAutoChange = 1
PERIOD_TIME_VISU_UPDATE,
PERIOD_ANIMATION,
PERIOD_ANIMATION,
PERIOD_ANIMATION,
PERIOD_PONG}};
PERIOD_PONG,
PERIOD_ANIMATION}};
// ----------------------------------------------------------------------------------
// GLOBAL VARIABLES
@@ -85,6 +87,7 @@ UDPLogger logger;
// Statics
static bool spiral_direction = false;
static unsigned long last_led_direct = 0; // time of last direct LED command (=> fall back to normal mode after timeout)
static WiFiUDP wifi_udp;
static NTPClientPlus ntp_client = NTPClientPlus(wifi_udp, NTP_SERVER_URL, 1, true);
@@ -92,16 +95,6 @@ static Pong pong = Pong(&led_matrix, &logger);
static Snake snake = Snake(&led_matrix, &logger);
static Tetris tetris = Tetris(&led_matrix, &logger);
// Timestamp variables
static unsigned long button_press_start = 0; // time of push button press start
static unsigned long last_led_direct = 0; // time of last direct LED command (=> fall back to normal mode after timeout)
static unsigned long last_animation_step = millis(); // time of last Matrix update
static unsigned long last_heartbeat = millis(); // time of last heartbeat sending
static unsigned long last_nightmode_check = millis(); // time of last nightmode check
static unsigned long last_ntp_update = millis() - PERIOD_NTP_UPDATE - 5000; // time of last NTP update
static unsigned long last_state_change = millis(); // time of last state change
static unsigned long last_step = millis(); // time of last animation step
static bool night_mode = false; // stores state of nightmode
static bool state_auto_change = false; // stores state of automatic state change
static float filter_factor = DEFAULT_SMOOTHING_FACTOR; // stores smoothing factor for led transition
@@ -120,7 +113,6 @@ NightModeTimes_st night_mode_times = {
// ----------------------------------------------------------------------------------
// SETUP
// ----------------------------------------------------------------------------------
void setup()
{
// put your setup code here, to run once:
@@ -196,12 +188,12 @@ void setup()
if (resetInfo->reason != REASON_SOFT_RESTART)
{
// test quickly each LED
for (int r = 0; r < MATRIX_HEIGHT; r++)
for (int16_t row = 0; row < MATRIX_HEIGHT; row++)
{
for (int c = 0; c < MATRIX_WIDTH; c++)
for (int16_t col = 0; col < MATRIX_WIDTH; col++)
{
matrix.fillScreen(0);
matrix.drawPixel(c, r, LEDMatrix::color_24_to_16bit(colors_24bit[2]));
matrix.drawPixel(col, row, LEDMatrix::color_24_to_16bit(colors_24bit[2]));
matrix.show();
delay(10);
}
@@ -270,6 +262,7 @@ void setup()
{
night_mode_times.nightmode_end_min = NIGHTMODE_END_MIN;
}
logger.log_string("Nightmode starts at: " + String(night_mode_times.nightmode_start_hour) + ":" + String(night_mode_times.nightmode_start_min));
logger.log_string("Nightmode ends at: " + String(night_mode_times.nightmode_end_hour) + ":" + String(night_mode_times.nightmode_end_min));
@@ -286,35 +279,79 @@ void setup()
// ----------------------------------------------------------------------------------
// LOOP
// ----------------------------------------------------------------------------------
void loop()
{
unsigned long current_time_ms = CURRENT_TIME_MS;
// Timestamp variables
static unsigned long last_animation_step = 0; // time of last animation step
static unsigned long last_matrix_update = 0; // time of last Matrix update
static unsigned long last_heartbeat = 0; // time of last heartbeat sending
static unsigned long last_nightmode_check = 0; // time of last nightmode check
static unsigned long last_ntp_update = 0; // time of last NTP update
static unsigned long last_state_change = 0; // time of last state change
handleOTA(); // handle OTA
logger.log_string("After handleOTA");
yield();
webserver.handleClient(); // handle webserver
logger.log_string("After handleClient");
yield();
send_heartbeat(); // send heartbeat update
logger.log_string("After send_heartbeat");
// send regularly heartbeat messages via UDP multicast
if ((current_time_ms - last_heartbeat) > PERIOD_HEARTBEAT)
{
Serial.printf("a");
Serial.println();
send_heartbeat(); // send heartbeat update
last_heartbeat = CURRENT_TIME_MS;
yield();
Serial.printf("A");
}
handle_current_state(); // handle current state - main process
logger.log_string("After handle_current_state");
if (!night_mode && ((current_time_ms - last_animation_step) > PERIODS[state_auto_change][current_state]) && ((current_time_ms - last_led_direct) > TIMEOUT_LEDDIRECT))
{
Serial.printf("b");
handle_current_state(); // handle current state
last_animation_step = CURRENT_TIME_MS;
yield();
Serial.printf("B");
}
update_matrix(); // update matrix
logger.log_string("After update_matrix");
if ((current_time_ms - last_matrix_update) > PERIOD_MATRIX_UPDATE)
{
Serial.printf("c");
update_matrix(); // update matrix
last_matrix_update = CURRENT_TIME_MS;
yield();
Serial.printf("C");
}
handle_button(); // handle button press
logger.log_string("After handle_button");
update_state_machine(); // handle state changes
logger.log_string("After update_state_machine");
if (!night_mode && state_auto_change && (current_time_ms - last_state_change > PERIOD_STATE_CHANGE))
{
Serial.printf("d");
update_state_machine(); // handle state changes
last_state_change = CURRENT_TIME_MS;
yield();
Serial.printf("D");
}
ntp_time_update(); // ntp time update
logger.log_string("After ntp_time_update");
if ((current_time_ms - last_ntp_update) > PERIOD_NTP_UPDATE)
{
Serial.printf("e");
ntp_time_update(&last_ntp_update); // ntp time update
yield();
Serial.printf("E");
}
check_night_mode(); // check night mode
logger.log_string("After check_night_mode");
if ((current_time_ms - last_nightmode_check) > PERIOD_NIGHTMODE_CHECK)
{
Serial.printf("f");
check_night_mode(); // check night mode
last_nightmode_check = CURRENT_TIME_MS;
Serial.printf("F");
}
}
// ----------------------------------------------------------------------------------
@@ -323,101 +360,92 @@ void loop()
void update_state_machine()
{
unsigned long current_time_ms = millis();
if (state_auto_change && (current_time_ms - last_state_change > PERIOD_STATE_CHANGE) && !night_mode)
{
// increment state variable and trigger state change
state_change((current_state + 1) % NUM_STATES);
// save last automatic state change
last_state_change = current_time_ms;
}
// increment state variable and trigger state change
state_change((current_state + 1) % (uint8_t)NUM_STATES);
}
void handle_current_state()
{
unsigned long current_time_ms = millis();
// handle mode behaviours (trigger loopCycles of different modes depending on current mode)
if (!night_mode && ((current_time_ms - last_step) > PERIODS[state_auto_change][current_state]) && ((current_time_ms - last_led_direct) > TIMEOUT_LEDDIRECT))
switch (current_state)
{
switch (current_state)
{
case ST_CLOCK: // state clock
{
int hours = ntp_client.getHours24();
int minutes = ntp_client.getMinutes();
case ST_CLOCK: // state clock
{
int hours = ntp_client.getHours24();
int minutes = ntp_client.getMinutes();
(void)show_string_on_clock(time_to_string((uint8_t)hours, (uint8_t)minutes), main_color_clock);
draw_minute_indicator((uint8_t)minutes, main_color_clock);
break;
}
case ST_DICLOCK: // state diclock
(void)show_string_on_clock(time_to_string((uint8_t)hours, (uint8_t)minutes), main_color_clock);
draw_minute_indicator((uint8_t)minutes, main_color_clock);
break;
}
case ST_DICLOCK: // state diclock
{
int hours = ntp_client.getHours24();
int minutes = ntp_client.getMinutes();
show_digital_clock((uint8_t)hours, (uint8_t)minutes, main_color_clock);
break;
}
case ST_SPIRAL: // state spiral
{
int res = draw_spiral(false, spiral_direction, MATRIX_WIDTH - 6);
if (res && spiral_direction == 0)
{
int hours = ntp_client.getHours24();
int minutes = ntp_client.getMinutes();
show_digital_clock((uint8_t)hours, (uint8_t)minutes, main_color_clock);
break;
// change spiral direction to closing (draw empty leds)
spiral_direction = 1;
// init spiral with new spiral direction
draw_spiral(true, spiral_direction, MATRIX_WIDTH - 6);
}
case ST_SPIRAL: // state spiral
else if (res && spiral_direction == 1)
{
int res = draw_spiral(false, spiral_direction, MATRIX_WIDTH - 6);
if (res && spiral_direction == 0)
{
// change spiral direction to closing (draw empty leds)
spiral_direction = 1;
// init spiral with new spiral direction
draw_spiral(true, spiral_direction, MATRIX_WIDTH - 6);
}
else if (res && spiral_direction == 1)
{
// reset spiral direction to normal drawing leds
spiral_direction = 0;
// init spiral with new spiral direction
draw_spiral(true, spiral_direction, MATRIX_WIDTH - 6);
}
break;
// reset spiral direction to normal drawing leds
spiral_direction = 0;
// init spiral with new spiral direction
draw_spiral(true, spiral_direction, MATRIX_WIDTH - 6);
}
case ST_TETRIS: // state tetris
break;
}
case ST_TETRIS: // state tetris
{
if (state_auto_change)
{
if (state_auto_change)
{
random_tetris(false);
}
else
{
tetris.loopCycle();
}
break;
random_tetris(false);
}
case ST_SNAKE: // state snake
else
{
if (state_auto_change)
{
led_matrix.flush();
if (random_snake(false, 8, main_color_snake, -1))
{
// init snake for next run
random_snake(true, 8, main_color_snake, -1);
}
}
else
{
snake.loopCycle();
}
break;
tetris.loopCycle();
}
case ST_PINGPONG: // state ping pong
break;
}
case ST_SNAKE: // state snake
{
if (state_auto_change)
{
pong.loopCycle();
break;
led_matrix.flush();
if (random_snake(false, 8, main_color_snake, -1))
{
// init snake for next run
random_snake(true, 8, main_color_snake, -1);
}
}
default:
else
{
break;
snake.loopCycle();
}
}
last_step = millis();
break;
}
case ST_PINGPONG: // state ping pong
{
pong.loopCycle();
break;
}
case ST_HEARTS:
{
draw_heart_animation();
break;
}
default:
{
break;
}
}
}
@@ -428,14 +456,8 @@ void handle_current_state()
*/
void update_matrix()
{
unsigned long current_time_ms = millis();
// periodically write colors to matrix
if ((current_time_ms - last_animation_step) > PERIOD_MATRIX_UPDATE)
{
led_matrix.draw_on_matrix_smooth(filter_factor);
last_animation_step = current_time_ms;
}
led_matrix.draw_on_matrix_smooth(filter_factor);
}
/**
@@ -445,25 +467,17 @@ void update_matrix()
*/
void send_heartbeat()
{
unsigned long current_time_ms = millis();
// send regularly heartbeat messages via UDP multicast
if ((current_time_ms - last_heartbeat) > PERIOD_HEARTBEAT)
logger.log_string("Heartbeat, state: " + state_names[current_state] + ", FreeHeap: " + ESP.getFreeHeap() +
", HeapFrag: " + ESP.getHeapFragmentation() + ", MaxFreeBlock: " + ESP.getMaxFreeBlockSize() + "\nCounter: " +
dbg_counter + " , Hours: " + (float)(dbg_counter) / 3600.0 + "\n"); // TODO CHANGE
// Check wifi status
if (WiFi.status() != WL_CONNECTED)
{
logger.log_string("Heartbeat, state: " + state_names[current_state] + ", FreeHeap: " + ESP.getFreeHeap() + \
", HeapFrag: " + ESP.getHeapFragmentation() + ", MaxFreeBlock: " + ESP.getMaxFreeBlockSize() + "\nCounter: " + \
dbg_counter + " , Hours: " + (float)(dbg_counter) / 3600.0 + "\n"); // TODO CHANGE
last_heartbeat = current_time_ms;
// Check wifi status (only if no apmode)
if (WiFi.status() != WL_CONNECTED)
{
Serial.println("WiFi connection lost!");
led_matrix.grid_add_pixel(0, 5, colors_24bit[1]);
led_matrix.draw_on_matrix_instant();
}
dbg_counter++; // TODO RM
Serial.println("WiFi connection lost!");
led_matrix.grid_add_pixel(0, 5, colors_24bit[1]);
led_matrix.draw_on_matrix_instant();
}
dbg_counter++; // TODO RM
}
/**
@@ -473,23 +487,17 @@ void send_heartbeat()
*/
void check_night_mode()
{
unsigned long current_time_ms = millis();
// check if nightmode need to be activated
if ((current_time_ms - last_nightmode_check) > PERIOD_NIGHTMODE_CHECK)
{
int hours = ntp_client.getHours24();
int minutes = ntp_client.getMinutes();
int hours = ntp_client.getHours24();
int minutes = ntp_client.getMinutes();
if (hours == night_mode_times.nightmode_start_hour && minutes == night_mode_times.nightmode_start_min)
{
set_night_mode(true);
}
else if (hours == night_mode_times.nightmode_end_hour && minutes == night_mode_times.nightmode_end_min)
{
set_night_mode(false);
}
last_nightmode_check = current_time_ms;
if (hours == night_mode_times.nightmode_start_hour && minutes == night_mode_times.nightmode_start_min)
{
set_night_mode(true);
}
else if (hours == night_mode_times.nightmode_end_hour && minutes == night_mode_times.nightmode_end_min)
{
set_night_mode(false);
}
}
@@ -498,63 +506,59 @@ void check_night_mode()
*
* @param None
*/
void ntp_time_update()
void ntp_time_update(unsigned long *last_ntp_update)
{
unsigned long current_time_ms = millis();
// NTP time update
if ((current_time_ms - last_ntp_update) > PERIOD_NTP_UPDATE)
{
int ntp_retval = ntp_client.updateNTP();
switch (ntp_retval)
{
case NTP_UPDATE_SUCCESS:
{
ntp_client.calcDate();
logger.log_string("NTP-Update successful");
logger.log_string("Time: " + ntp_client.getFormattedTime());
logger.log_string("Date: " + ntp_client.getFormattedDate());
logger.log_string("TimeOffset (seconds): " + String(ntp_client.getTimeOffset()));
logger.log_string("Summertime: " + String(ntp_client.updateSWChange()));
last_ntp_update = millis();
watchdog_counter = 30;
break;
}
case NTP_UPDATE_TIMEOUT:
{
logger.log_string("NTP-Update not successful. Reason: Timeout");
last_ntp_update += 10000;
watchdog_counter--;
break;
}
case NTP_UPDATE_DIFFTOOHIGH:
{
logger.log_string("NTP-Update not successful. Reason: Too large time difference");
logger.log_string("Time: " + ntp_client.getFormattedTime());
logger.log_string("Date: " + ntp_client.getFormattedDate());
logger.log_string("TimeOffset (seconds): " + String(ntp_client.getTimeOffset()));
logger.log_string("Summertime: " + String(ntp_client.updateSWChange()));
last_ntp_update += 10000;
watchdog_counter--;
break;
}
case NTP_UPDATE_TIME_INVALID:
default:
{
logger.log_string("NTP-Update not successful. Reason: NTP time not valid (<1970)");
last_ntp_update += 10000;
watchdog_counter--;
break;
}
}
int ntp_retval = ntp_client.updateNTP();
logger.log_string("Watchdog Counter: " + String(watchdog_counter));
if (watchdog_counter <= 0)
{
logger.log_string("Trigger restart due to watchdog...");
delay(100);
ESP.restart();
}
switch (ntp_retval)
{
case NTP_UPDATE_SUCCESS:
{
ntp_client.calcDate();
logger.log_string("NTP-Update successful");
logger.log_string("Time: " + ntp_client.getFormattedTime());
logger.log_string("Date: " + ntp_client.getFormattedDate());
logger.log_string("TimeOffset (seconds): " + String(ntp_client.getTimeOffset()));
logger.log_string("Summertime: " + String(ntp_client.updateSWChange()));
*last_ntp_update = CURRENT_TIME_MS;
watchdog_counter = 30;
break;
}
case NTP_UPDATE_TIMEOUT:
{
logger.log_string("NTP-Update not successful. Reason: Timeout");
*last_ntp_update += 10000;
watchdog_counter--;
break;
}
case NTP_UPDATE_DIFFTOOHIGH:
{
logger.log_string("NTP-Update not successful. Reason: Too large time difference");
logger.log_string("Time: " + ntp_client.getFormattedTime());
logger.log_string("Date: " + ntp_client.getFormattedDate());
logger.log_string("TimeOffset (seconds): " + String(ntp_client.getTimeOffset()));
logger.log_string("Summertime: " + String(ntp_client.updateSWChange()));
*last_ntp_update += 10000;
watchdog_counter--;
break;
}
case NTP_UPDATE_TIME_INVALID:
default:
{
logger.log_string("NTP-Update not successful. Reason: NTP time not valid (<1970)");
*last_ntp_update += 10000;
watchdog_counter--;
break;
}
}
logger.log_string("Watchdog Counter: " + String(watchdog_counter));
if (watchdog_counter <= 0)
{
logger.log_string("Trigger restart due to watchdog...");
delay(100);
ESP.restart();
}
}
@@ -631,10 +635,9 @@ void state_change(uint8_t newState)
{
set_night_mode(false); // deactivate Nightmode
}
// first clear matrix
led_matrix.flush();
// set new state
current_state = newState;
led_matrix.flush(); // first clear matrix
current_state = newState; // set new state
on_state_entry(current_state);
logger.log_string("State change to: " + state_names[current_state]);
delay(5);
@@ -680,7 +683,7 @@ void handle_led_direct()
}
led_matrix.draw_on_matrix_instant();
last_led_direct = millis();
last_led_direct = CURRENT_TIME_MS;
}
webserver.send(200, "text/plain", message);
}
@@ -692,28 +695,28 @@ void handle_led_direct()
*/
void handle_button()
{
static bool lastButtonState = false;
bool buttonPressed = !digitalRead(BUTTON_PIN);
bool button_pressed = !digitalRead(BUTTON_PIN);
static bool last_button_state = false;
static unsigned long button_press_start = 0; // time of push button press start
// check rising edge
if (buttonPressed == true && lastButtonState == false)
if (button_pressed == true && last_button_state == false)
{
// button press start
logger.log_string("Button press started");
button_press_start = millis();
button_press_start = CURRENT_TIME_MS;
}
// check falling edge
if (buttonPressed == false && lastButtonState == true)
if (button_pressed == false && last_button_state == true)
{
// button press ended
if ((millis() - button_press_start) > LONG_PRESS_MS)
if ((CURRENT_TIME_MS - button_press_start) > LONG_PRESS_MS)
{
// longpress -> nightmode
logger.log_string("Button press ended - long press");
set_night_mode(true);
}
else if ((millis() - button_press_start) > SHORT_PRESS_MS)
else if ((CURRENT_TIME_MS - button_press_start) > SHORT_PRESS_MS)
{
// shortpress -> state change
logger.log_string("Button press ended - short press");
@@ -724,11 +727,11 @@ void handle_button()
}
else
{
state_change((current_state + 1) % NUM_STATES);
state_change((current_state + 1) % (uint8_t)NUM_STATES);
}
}
}
lastButtonState = buttonPressed;
last_button_state = button_pressed;
}
/**
@@ -755,7 +758,8 @@ void load_main_color()
uint8_t red = EEPROM.read(ADR_MC_RED);
uint8_t green = EEPROM.read(ADR_MC_GREEN);
uint8_t blue = EEPROM.read(ADR_MC_BLUE);
if (int(red) + int(green) + int(blue) < 50)
if ((int(red) + int(green) + int(blue)) < 50)
{
main_color_clock = colors_24bit[2];
}
@@ -821,6 +825,10 @@ void handle_command()
{
state_change(ST_PINGPONG);
}
else if (mode_str.equals("hearts"))
{
state_change(ST_HEARTS);
}
}
else if (webserver.argName(0).equals("nightmode"))
{
@@ -946,6 +954,7 @@ void handle_command()
pong.initGame(1);
}
}
webserver.send(204, "text/plain", "No Content"); // this page doesn't send back content --> 204
}