Compare commits

...

6 Commits

10 changed files with 451 additions and 382 deletions

3
.gitignore vendored
View File

@@ -11,6 +11,9 @@
# Own folders # Own folders
_unused/ _unused/
# Own files
log.txt
# Prerequisites # Prerequisites
*.d *.d

View File

@@ -29,7 +29,7 @@ class NTPClientPlus
public: public:
NTPClientPlus(UDP &udp, const char *pool_server_name, int utcx, bool sw_change); NTPClientPlus(UDP &udp, const char *pool_server_name, int utcx, bool sw_change);
bool is_leap_year(unsigned int year); bool is_leap_year(unsigned int year);
bool update_sw_change(); bool check_daylight_saving_time();
int get_hours_12() const; int get_hours_12() const;
int get_hours_24() const; int get_hours_24() const;
int get_minutes() const; int get_minutes() const;

View File

@@ -1,66 +1,20 @@
#ifndef OWNFONT_H #ifndef OWN_FONT_H
#define OWNFONT_H #define OWN_FONT_H
uint8_t numbers_font[10][5] = { {0b00000111, #include <Arduino.h>
0b00000101,
0b00000101,
0b00000101,
0b00000111},
{0b00000001,
0b00000001,
0b00000001,
0b00000001,
0b00000001},
{0b00000111,
0b00000001,
0b00000111,
0b00000100,
0b00000111},
{0b00000111,
0b00000001,
0b00000111,
0b00000001,
0b00000111},
{0b00000101,
0b00000101,
0b00000111,
0b00000001,
0b00000001},
{0b00000111,
0b00000100,
0b00000111,
0b00000001,
0b00000111},
{0b00000111,
0b00000100,
0b00000111,
0b00000101,
0b00000111},
{0b00000111,
0b00000001,
0b00000001,
0b00000001,
0b00000001},
{0b00000111,
0b00000101,
0b00000111,
0b00000101,
0b00000111},
{0b00000111,
0b00000101,
0b00000111,
0b00000001,
0b00000111}};
uint8_t chars_font[2][5] = { {0b00000010, uint8_t numbers_font[10][5] = {{0b00000111, 0b00000101, 0b00000101, 0b00000101, 0b00000111},
0b00000010, {0b00000001, 0b00000001, 0b00000001, 0b00000001, 0b00000001},
0b00000010, {0b00000111, 0b00000001, 0b00000111, 0b00000100, 0b00000111},
0b00000010, {0b00000111, 0b00000001, 0b00000111, 0b00000001, 0b00000111},
0b00000010}, {0b00000101, 0b00000101, 0b00000111, 0b00000001, 0b00000001},
{0b00000111, {0b00000111, 0b00000100, 0b00000111, 0b00000001, 0b00000111},
0b00000101, {0b00000111, 0b00000100, 0b00000111, 0b00000101, 0b00000111},
0b00000111, {0b00000111, 0b00000001, 0b00000001, 0b00000001, 0b00000001},
0b00000100, {0b00000111, 0b00000101, 0b00000111, 0b00000101, 0b00000111},
0b00000100}}; {0b00000111, 0b00000101, 0b00000111, 0b00000001, 0b00000111}};
#endif /* OWNFONT_H */ uint8_t chars_font[2][5] = {{0b00000010, 0b00000010, 0b00000010, 0b00000010, 0b00000010},
{0b00000111, 0b00000101, 0b00000111, 0b00000100, 0b00000100}};
#endif /* OWN_FONT_H */

View File

@@ -10,34 +10,29 @@
#define NTP_SERVER_URL "de.pool.ntp.org" // NTP server address #define NTP_SERVER_URL "de.pool.ntp.org" // NTP server address
#define HOSTNAME (String("wordclock")) // Local hostname #define HOSTNAME (String("wordclock")) // Local hostname
#define LOGGER_MULTICAST_IP (IPAddress(230, 120, 10, 2)) // IP for UDP server #define LOGGER_MULTICAST_IP (IPAddress(230, 120, 10, 2)) // IP for UDP server
#define LOGGER_MULTICAST_PORT 8123 // Port for UDP server #define LOGGER_MULTICAST_PORT (8123) // Port for UDP server
#define HTTP_PORT 80 // Standard HTTP port #define HTTP_PORT (80) // Standard HTTP port
// EEPROM layout // ESP8266 Pins
typedef enum #define NEOPIXEL_PIN (14) // pin to which the NeoPixels are attached
{ #define BUTTON_PIN (5) // pin to which the button is attached
ADR_NM_START_H = 0,
ADR_NM_END_H = 4,
ADR_NM_START_M = 8,
ADR_NM_END_M = 12,
ADR_BRIGHTNESS = 16,
ADR_MC_RED = 20,
ADR_MC_GREEN = 22,
ADR_MC_BLUE = 24,
EEPROM_SIZE = 30
} EepromLayout_en;
#define NEOPIXEL_PIN 14 // pin to which the NeoPixels are attached // Time limits
#define BUTTON_PIN 5 // pin to which the button is attached #define HOUR_MAX (23)
#define MINUTE_MAX (59)
#define MINUTES_IN_HOUR (60)
#define HOURS_IN_DAY (24)
// Night mode // Night mode
#define NIGHTMODE_START_HR 23 #define NIGHTMODE_START_HR (23)
#define NIGHTMODE_START_MIN 0 #define NIGHTMODE_START_MIN (0)
#define NIGHTMODE_END_HR 7 #define NIGHTMODE_END_HR (7)
#define NIGHTMODE_END_MIN 0 #define NIGHTMODE_END_MIN (0)
// Timings in us // Timings in us
#define PERIOD_ANIMATION_US (200 * 1000) // 200ms #define PERIOD_ANIMATION_US (200 * 1000) // 200ms
#define PERIOD_CLOCK_UPDATE_US (1 * 1000 * 1000) // 1s
#define PERIOD_HEARTBEAT_US (1 * 1000 * 1000) // 1s #define PERIOD_HEARTBEAT_US (1 * 1000 * 1000) // 1s
#define PERIOD_MATRIX_UPDATE_US (100 * 1000) // 100ms #define PERIOD_MATRIX_UPDATE_US (100 * 1000) // 100ms
#define PERIOD_NIGHTMODE_CHECK_US (20 * 1000 * 1000) // 20s #define PERIOD_NIGHTMODE_CHECK_US (20 * 1000 * 1000) // 20s
@@ -46,7 +41,6 @@ typedef enum
#define PERIOD_SNAKE_US (50 * 1000) // 50ms #define PERIOD_SNAKE_US (50 * 1000) // 50ms
#define PERIOD_STATE_CHANGE_US (10 * 1000 * 1000) // 10s #define PERIOD_STATE_CHANGE_US (10 * 1000 * 1000) // 10s
#define PERIOD_TETRIS_US (50 * 1000) // 50ms #define PERIOD_TETRIS_US (50 * 1000) // 50ms
#define PERIOD_TIME_VISU_UPDATE_US (1 * 1000 * 1000) // 1s
#define TIMEOUT_LEDDIRECT_US (5 * 1000 * 1000) // 5s #define TIMEOUT_LEDDIRECT_US (5 * 1000 * 1000) // 5s
#define SHORT_PRESS_US (100 * 1000) // 100ms #define SHORT_PRESS_US (100 * 1000) // 100ms
@@ -54,16 +48,21 @@ typedef enum
#define VERY_LONG_PRESS_US (10 * 1000 * 1000) // 10s #define VERY_LONG_PRESS_US (10 * 1000 * 1000) // 10s
// Current limit // Current limit
#define CURRENT_LIMIT_LED 2500 // limit the total current consumed by LEDs (mA) #define CURRENT_LIMIT_LED (2500) // limit the total current consumed by LEDs (mA)
// Brightness ranges range: 0 - 255
#define DEFAULT_BRIGHTNESS (40)
#define MIN_BRIGHTNESS (10)
#define MAX_BRIGHTNESS UINT8_MAX
// LED smoothing // LED smoothing
#define DEFAULT_SMOOTHING_FACTOR 0.5 #define DEFAULT_SMOOTHING_FACTOR (0.5f)
// Number of colors in colors array // Number of colors in colors array
#define NUM_COLORS 7 #define NUM_COLORS (7)
// LED matrix size // LED matrix size
#define MATRIX_WIDTH 11 #define MATRIX_WIDTH (11)
#define MATRIX_HEIGHT 11 #define MATRIX_HEIGHT (11)
#endif /* WORDCLOCK_CONSTANTS_H */ #endif /* WORDCLOCK_CONSTANTS_H */

View File

@@ -6,9 +6,10 @@
#include "led_matrix.h" #include "led_matrix.h"
#include "udp_logger.h" #include "udp_logger.h"
extern UDPLogger logger; #define RANGE_LIMIT(X, MIN, MAX) (((X) < (MIN)) ? (MIN) : (((X) > (MAX)) ? (MAX) : (X)))
extern LEDMatrix led_matrix; #define RANGE_LIMIT_SUB(X, MIN, MAX, SUB) (((X) < (MIN)) ? (SUB) : (((X) > (MAX)) ? (SUB) : (X)))
extern ESP8266WebServer webserver;
#define EEPROM_SIZE (sizeof(EepromLayout_st) / sizeof(uint8_t))
typedef struct typedef struct
{ {
@@ -18,6 +19,29 @@ typedef struct
int end_min; int end_min;
} NightModeTimes_st; } NightModeTimes_st;
typedef struct
{
uint8_t red;
uint8_t green;
uint8_t blue;
uint8_t alpha;
} Color_st;
typedef struct
{
uint8_t static_brightness; // user-controlled static brightness of LEDs
uint8_t dyn_brightness_min; // user-controlled min brightness of LEDs
uint8_t dyn_brightness_max; // user-controlled max brightness of LEDs
bool flg_dynamic_brightness; // flag if user wants to use daytime dynamic brightness
} Brightness_st;
typedef struct
{
NightModeTimes_st night_mode_times;
Brightness_st brightness_values;
Color_st color_values;
} EepromLayout_st;
typedef enum typedef enum
{ {
ST_CLOCK, ST_CLOCK,
@@ -30,25 +54,28 @@ typedef enum
NUM_STATES NUM_STATES
} ClockState_en; } ClockState_en;
int EEPROM_read_address(int address);
String leading_zero2digit(int value); String leading_zero2digit(int value);
uint8_t calculate_dynamic_brightness(uint8_t min_brightness, uint8_t max_brightness, int hours, int minutes, bool summertime);
uint8_t update_brightness(void);
void check_night_mode(void); void check_night_mode(void);
void check_wifi_status(void); void check_wifi_status(void);
void EEPROM_write_to_address(int address, int value); void draw_main_color(void);
void handle_button(void); void handle_button(void);
void handle_command(void); void handle_command(void);
void handle_current_state(void); void handle_current_state(void);
void handle_data_request(void); void handle_data_request(void);
void handle_led_direct(void); void handle_led_direct(void);
void load_main_color(void); void limit_value_ranges(void);
void ntp_time_update(uint32 *last_ntp_update_us); void ntp_time_update(uint32 *last_ntp_update_us);
void on_state_entry(uint8_t state); void on_state_entry(uint8_t state);
void read_settings_from_EEPROM(void);
void reset_wifi_credentials(void); void reset_wifi_credentials(void);
void send_heartbeat(void); void send_heartbeat(void);
void set_dynamic_brightness(bool state);
void set_main_color(uint8_t red, uint8_t green, uint8_t blue); void set_main_color(uint8_t red, uint8_t green, uint8_t blue);
void set_night_mode(bool on); void set_night_mode(bool on);
void state_change(uint8_t newState); void state_change(uint8_t newState);
void update_matrix(void); void update_matrix(void);
void update_state_machine(void); void write_settings_to_EEPROM(void);
#endif /* WORDCLOCK_ESP8266_H */ #endif /* WORDCLOCK_ESP8266_H */

View File

@@ -251,15 +251,23 @@
} }
.show { .show {
height: 200px; height: 250px;
transition: height 1s; transition: height 1s;
} }
.show-container {
display: inline
}
.hide-container {
display: none
}
.number-container { .number-container {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-content: left; justify-content: left;
align-items: center; /* align-items: center; */
margin: 10px; margin: 10px;
} }
@@ -279,9 +287,21 @@
<h1 id="headline">WORDCLOCK 2.0</h1> <h1 id="headline">WORDCLOCK 2.0</h1>
<div id="settings-container" class="settings-container"> <div id="settings-container" class="settings-container">
<div class="number-container"> <div class="visible-brightness-container" id="static_brightness_container">
<label for="brightness">Brightness:</label> <div class="number-container">
<input type="range" id="brightness" name="volume" min="10" max="255"> <label for="static_brightness">Static brightness:</label>
<input type="range" id="static_brightness" name="static_brightness" min="10" max="255">
</div>
</div>
<div class="hidden-brightness-container" id="dyn_brightness_container">
<div class="number-container">
<label for="min_brightness">Min. brightness:</label>
<input type="range" id="min_brightness" name="min_brightness" min="10" max="255">
</div>
<div class="number-container">
<label for="max_brightness">Max. brightness:</label>
<input type="range" id="max_brightness" name="max_brightness" min="10" max="255">
</div>
</div> </div>
<div class="number-container"> <div class="number-container">
<label for="nm_start" style="align-self: flex-start">Nightmode Start Time: </label> <label for="nm_start" style="align-self: flex-start">Nightmode Start Time: </label>
@@ -332,14 +352,12 @@
<input name="Nightmode" id="Nightmode" type="checkbox" class="toggle"> <input name="Nightmode" id="Nightmode" type="checkbox" class="toggle">
</div> </div>
</div> </div>
<div class="checkbox-container"> <div class="checkbox-container" onclick="toggleBrightnessOptions()">
<label for="AutoChange" style="align-self: flex-start">Automatic mode change</label> <label for="DynBrightness" style="align-self: flex-start">Dynamic brightness</label>
<div> <div>
<input name="AutoChange" id="AutoChange" type="checkbox" class="toggle"> <input name="DynBrightness" id="DynBrightness" type="checkbox" class="toggle">
</div> </div>
</div> </div>
<div class="main-container hidden" id="colorcontainer"> <div class="main-container hidden" id="colorcontainer">
<div class="verticalline"> <div class="verticalline">
</div> </div>
@@ -503,44 +521,47 @@
} }
modebuttons[state].classList.add("active"); modebuttons[state].classList.add("active");
// set checkbox states // set Nightmode checkbox states
var ckb_nightmode = document.querySelector('input[id="Nightmode"]'); var ckb_nightmode = document.querySelector('input[id="Nightmode"]');
if (myVar.nightMode == "1") { if (myVar.night_mode == "1") {
console.log("nightMode == 1"); console.log("night_mode == 1");
ckb_nightmode.checked = true; ckb_nightmode.checked = true;
} }
else { else {
console.log("nightMode == 0"); console.log("night_mode == 0");
ckb_nightmode.checked = false; ckb_nightmode.checked = false;
} }
ckb_nightmode.addEventListener('change', () => { ckb_nightmode.addEventListener('change', () => {
if (ckb_nightmode.checked) { if (ckb_nightmode.checked) {
sendCommand("./cmd?nightmode=1"); sendCommand("./cmd?night_mode=1");
} else { } else {
sendCommand("./cmd?nightmode=0"); sendCommand("./cmd?night_mode=0");
} }
}); });
var ckb_stateautochange = document.querySelector('input[id="AutoChange"]'); // set DynBrightness checkbox states
if (myVar.stateAutoChange == "1") { var ckb_dynbrightness = document.querySelector('input[id="DynBrightness"]');
console.log("stateAutoChange == 1"); if (myVar.dyn_brightness == "1") {
ckb_stateautochange.checked = true; console.log("dyn_brightness == 1");
ckb_dynbrightness.checked = true;
} }
else { else {
console.log("stateAutoChange == 0"); console.log("dyn_brightness == 0");
ckb_stateautochange.checked = false; ckb_dynbrightness.checked = false;
} }
ckb_stateautochange.addEventListener('change', () => { ckb_dynbrightness.addEventListener('change', () => {
if (ckb_stateautochange.checked) { if (ckb_dynbrightness.checked) {
sendCommand("./cmd?stateautochange=1"); sendCommand("./cmd?dyn_brightness=1");
} else { } else {
sendCommand("./cmd?stateautochange=0"); sendCommand("./cmd?dyn_brightness=0");
} }
}); });
document.getElementById("nm_start").value = myVar.nightModeStart.replace("-", ":"); document.getElementById("nm_start").value = myVar.nightModeStart.replace("-", ":");
document.getElementById("nm_end").value = myVar.nightModeEnd.replace("-", ":"); document.getElementById("nm_end").value = myVar.nightModeEnd.replace("-", ":");
document.getElementById("brightness").value = parseInt(myVar.brightness); document.getElementById("static_brightness").value = parseInt(myVar.static_brightness);
document.getElementById("min_brightness").value = parseInt(myVar.min_brightness);
document.getElementById("max_brightness").value = parseInt(myVar.max_brightness);
updateDisplay(parseInt(myVar.modeid)); updateDisplay(parseInt(myVar.modeid));
console.log(myVar); console.log(myVar);
@@ -566,7 +587,7 @@
for (const element of maincontainer) { for (const element of maincontainer) {
element.classList.add("hidden"); element.classList.add("hidden");
} }
if (myVar != null && myVar.stateAutoChange == "0") { if (myVar != null) {
switch (modeid) { switch (modeid) {
case 0: // clock case 0: // clock
document.getElementById("colorcontainer").classList.remove("hidden"); document.getElementById("colorcontainer").classList.remove("hidden");
@@ -603,7 +624,9 @@
function saveSettings() { function saveSettings() {
var nmStart = document.getElementById("nm_start"); var nmStart = document.getElementById("nm_start");
var nmEnd = document.getElementById("nm_end"); var nmEnd = document.getElementById("nm_end");
var brightnessElmt = document.getElementById("brightness"); var staticBrightnessElmt = document.getElementById("static_brightness");
var minBrightnessElmt = document.getElementById("min_brightness");
var maxBrightnessElmt = document.getElementById("max_brightness");
var resetWifi = document.getElementById("reset_wifi"); var resetWifi = document.getElementById("reset_wifi");
var cmdstr = "./cmd?setting="; var cmdstr = "./cmd?setting=";
@@ -611,25 +634,68 @@
cmdstr += "-"; cmdstr += "-";
cmdstr += nmEnd.value.replace(":", "-"); cmdstr += nmEnd.value.replace(":", "-");
cmdstr += "-"; cmdstr += "-";
cmdstr += brightnessElmt.value; cmdstr += staticBrightnessElmt.value;
cmdstr += "-"; cmdstr += "-";
cmdstr += Number(resetWifi.checked); cmdstr += Number(resetWifi.checked);
cmdstr += "-";
cmdstr += minBrightnessElmt.value;
cmdstr += "-";
cmdstr += maxBrightnessElmt.value;
console.log(cmdstr); console.log(cmdstr);
sendCommand(cmdstr); sendCommand(cmdstr);
toggleSettings(); toggleSettings();
} }
function toggleBrightnessOptions() {
var static_b = document.getElementById("static_brightness_container");
var dyn_b = document.getElementById("dyn_brightness_container");
var ckb_dynbrightness = document.querySelector('input[id="DynBrightness"]');
if (ckb_dynbrightness.checked == true) {
static_b.classList.remove("show-container");
static_b.classList.add("hide-container")
dyn_b.classList.remove("hide-container");
dyn_b.classList.add("show-container")
}
else {
dyn_b.classList.remove("show-container");
dyn_b.classList.add("hide-container");
static_b.classList.remove("hide-container");
static_b.classList.add("show-container");
}
}
function toggleSettings() { function toggleSettings() {
var container = document.getElementById("settings-container"); var container = document.getElementById("settings-container");
if (container.classList.contains("show")) { if (container.classList.contains("show")) {
container.classList.remove("show"); container.classList.remove("show");
} }
else { else {
toggleBrightnessOptions();
container.classList.add("show"); container.classList.add("show");
} }
} }
function SetMax() {
var slider = document.getElementById("mySlider");
if ("max" in slider) { // Google Chrome, Safari from version 5 and Opera
slider.min = 500;
} else {
// Safari before version 5
slider.setAttribute("max", 500);
}
OnSliderChanged(slider);
}
function OnSliderChanged(slider) {
var sliderValue = document.getElementById("sliderValue");
sliderValue.innerHTML = slider.value;
}
</script> </script>
</body> </body>

View File

@@ -302,7 +302,7 @@ void NTPClientPlus::calc_date()
// calc if summer time active // calc if summer time active
this->update_sw_change(); this->check_daylight_saving_time();
} }
/** /**
@@ -555,7 +555,7 @@ void NTPClientPlus::set_summertime(bool summertime)
* *
* @returns bool summertime active * @returns bool summertime active
*/ */
bool NTPClientPlus::update_sw_change() bool NTPClientPlus::check_daylight_saving_time()
{ {
unsigned int day_of_week = this->_day_of_week; unsigned int day_of_week = this->_day_of_week;
unsigned int date_day = this->_date_day; unsigned int date_day = this->_date_day;

View File

@@ -20,13 +20,14 @@ void UDPLogger::set_name(String name)
void UDPLogger::log_string(String message) void UDPLogger::log_string(String message)
{ {
// wait 5 milliseconds if last send was less than 5 milliseconds before // wait 5 milliseconds if last send was less than 10 milliseconds before
if ((system_get_time() / 1000) < (_lastSend + 5)) if ((system_get_time() / 1000) < (_lastSend + 10))
{ {
delay(5); delay(10);
} }
message = _name + ": " + message; message = _name + ": " + message;
Serial.println(message); Serial.println(message);
delay(10);
_udp.beginPacketMulticast(_multicastAddr, _port, _interfaceAddr); _udp.beginPacketMulticast(_multicastAddr, _port, _interfaceAddr);
message.toCharArray(_packetBuffer, 100); message.toCharArray(_packetBuffer, 100);
_udp.print(_packetBuffer); _udp.print(_packetBuffer);

View File

@@ -305,6 +305,8 @@ int random_tetris(bool init)
if (init || gameover) if (init || gameover)
{ {
logger.log_string("Init Tetris: init=" + String(init) + ", gameover=" + String(gameover)); logger.log_string("Init Tetris: init=" + String(init) + ", gameover=" + String(gameover));
delay(10);
// clear local game screen // clear local game screen
for (int h = 0; h < MATRIX_HEIGHT + 3; h++) for (int h = 0; h < MATRIX_HEIGHT + 3; h++)
{ {
@@ -364,6 +366,8 @@ int random_tetris(bool init)
{ {
// no more moving blocks -> check if game over or spawn new block // no more moving blocks -> check if game over or spawn new block
logger.log_string("Tetris: No more Mover"); logger.log_string("Tetris: No more Mover");
delay(10);
gameover = false; gameover = false;
// check if game was lost -> one pixel active in 4rd row (top row on the led grid) // check if game was lost -> one pixel active in 4rd row (top row on the led grid)
for (int s = 0; s < MATRIX_WIDTH; s++) for (int s = 0; s < MATRIX_WIDTH; s++)

View File

@@ -34,81 +34,68 @@
// own libraries // own libraries
#include "animation_functions.h" #include "animation_functions.h"
#include "pong.h" #include "base64_wrapper.h" // copied from https://github.com/Xander-Electronics/Base64
#include "snake.h"
#include "tetris.h"
#include "led_matrix.h" #include "led_matrix.h"
#include "littlefs_wrapper.h" #include "littlefs_wrapper.h"
#include "base64_wrapper.h" // copied from https://github.com/Xander-Electronics/Base64
#include "ntp_client_plus.h" #include "ntp_client_plus.h"
#include "ota_functions.h" #include "ota_functions.h"
#include "pong.h"
#include "render_functions.h"
#include "snake.h"
#include "tetris.h"
#include "udp_logger.h" #include "udp_logger.h"
#include "wordclock_constants.h" #include "wordclock_constants.h"
#include "render_functions.h"
const String state_names[] = {"Clock", "DiClock", "Spiral", "Tetris", "Snake", "PingPong", "Hearts"};
// PERIODS for each state (different for stateAutoChange or Manual mode)
const uint32_t PERIODS[2][NUM_STATES] = {{PERIOD_TIME_VISU_UPDATE_US, // stateAutoChange = 0
PERIOD_TIME_VISU_UPDATE_US,
PERIOD_ANIMATION_US,
PERIOD_TETRIS_US,
PERIOD_SNAKE_US,
PERIOD_PONG_US,
PERIOD_ANIMATION_US},
{PERIOD_TIME_VISU_UPDATE_US, // stateAutoChange = 1
PERIOD_TIME_VISU_UPDATE_US,
PERIOD_ANIMATION_US,
PERIOD_ANIMATION_US,
PERIOD_ANIMATION_US,
PERIOD_PONG_US,
PERIOD_ANIMATION_US}};
// ---------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------
// GLOBAL VARIABLES // GLOBAL VARIABLES
// ---------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------
// Webserver
ESP8266WebServer webserver(HTTP_PORT);
// When we setup the NeoPixel library, we tell it how many pixels, and which pin to use to send signals.
// Note that for older NeoPixel strips you might need to change the third parameter--see the strandtest
// example for more information on possible values.
Adafruit_NeoMatrix matrix = Adafruit_NeoMatrix(MATRIX_WIDTH, MATRIX_HEIGHT + 1, NEOPIXEL_PIN, 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_MATRIX_TOP + NEO_MATRIX_LEFT + NEO_MATRIX_ROWS + NEO_MATRIX_ZIGZAG,
NEO_GRB + NEO_KHZ800); NEO_GRB + NEO_KHZ800); // NeoMatrix
UDPLogger logger; // Logger
ESP8266WebServer webserver(HTTP_PORT); // Webserver
LEDMatrix led_matrix = LEDMatrix(&matrix, DEFAULT_BRIGHTNESS, &logger); // NeoMatrix wrapper
WiFiUDP wifi_udp;
NTPClientPlus ntp_client = NTPClientPlus(wifi_udp, NTP_SERVER_URL, 1, true);
// Globals // ----------------------------------------------------------------------------------
uint8_t brightness = 40; // current brightness of LEDs // STATIC VARIABLES
LEDMatrix led_matrix = LEDMatrix(&matrix, brightness, &logger); // ----------------------------------------------------------------------------------
UDPLogger logger; 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;
// Statics
static bool spiral_direction = false;
static uint32 last_led_direct_us = 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);
static Pong pong = Pong(&led_matrix, &logger); static Pong pong = Pong(&led_matrix, &logger);
static Snake snake = Snake(&led_matrix, &logger); static Snake snake = Snake(&led_matrix, &logger);
static Tetris tetris = Tetris(&led_matrix, &logger); static Tetris tetris = Tetris(&led_matrix, &logger);
static bool night_mode = false; // stores state of nightmode static bool flg_night_mode = false; // state of nightmode
static bool state_auto_change = false; // stores state of automatic state change static bool flg_reset_wifi_creds = false; // used to reset stored wifi credentials
static bool reset_wifi_creds = false; // used to reset stored wifi credentials static bool spiral_direction = false;
static float filter_factor = DEFAULT_SMOOTHING_FACTOR; // stores smoothing factor for led transition static float filter_factor = DEFAULT_SMOOTHING_FACTOR; // stores smoothing factor for led transition
static uint32_t main_color_clock = colors_24bit[2]; // color of the clock and digital clock static int watchdog_counter = 30; // Watchdog counter to trigger restart if NTP update was not possible 30 times in a row (5min)
static uint32_t main_color_snake = colors_24bit[1]; // color of the random snake animation static uint32 last_led_direct_us = 0; // time of last direct LED command (=> fall back to normal mode after timeout)
static uint8_t current_state = (uint8_t)ST_CLOCK; // stores current state static uint32_t heartbeat_counter = 0; // for heartbeat on-time in seconds
static int watchdog_counter = 30; // Watchdog counter to trigger restart if NTP update was not possible 30 times in a row (5min) 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
// Nightmode time settings static const String state_names[NUM_STATES] = {"Clock", "DiClock", "Spiral", "Tetris", "Snake", "PingPong", "Hearts"};
NightModeTimes_st night_mode_times = { static const uint32_t period_timings[NUM_STATES] = {PERIOD_CLOCK_UPDATE_US, PERIOD_CLOCK_UPDATE_US,
NIGHTMODE_START_HR, PERIOD_ANIMATION_US, PERIOD_TETRIS_US, PERIOD_SNAKE_US,
NIGHTMODE_START_MIN, PERIOD_PONG_US, PERIOD_ANIMATION_US};
NIGHTMODE_END_HR,
NIGHTMODE_END_MIN};
uint32_t heartbeat_counter = 0; // for heartbeat on-time in seconds // Quarterly brightness factor for dynamic brightness
static const float qtly_brightness_factor[96] = {
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,
1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.999f, 0.999f,
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};
// ---------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------
// SETUP // SETUP
@@ -132,8 +119,11 @@ void setup()
// Init EEPROM // Init EEPROM
EEPROM.begin(EEPROM_SIZE); EEPROM.begin(EEPROM_SIZE);
// Load color for clock from EEPROM // Read global settings from EEPROM
load_main_color(); read_settings_from_EEPROM();
// draw color on clock
draw_main_color();
// configure button pin as input // configure button pin as input
pinMode(BUTTON_PIN, INPUT_PULLUP); pinMode(BUTTON_PIN, INPUT_PULLUP);
@@ -147,7 +137,7 @@ void setup()
led_matrix.draw_on_matrix_instant(); led_matrix.draw_on_matrix_instant();
/* Use WiFiMaanger for handling initial Wifi setup */ /* Use WiFiMaanger for handling initial Wifi setup */
WiFiManager wifi_manager; // Local intialization. Once its business is done, there is no need to keep it around WiFiManager wifi_manager; // Local initialization. Once its business is done, there is no need to keep it around
/* fetches ssid and pass from eeprom and tries to connect. if it does not connect it starts an access point with /* fetches ssid and pass from eeprom and tries to connect. if it does not connect it starts an access point with
* the specified name and goes into a blocking loop awaiting configuration. */ * the specified name and goes into a blocking loop awaiting configuration. */
@@ -189,7 +179,7 @@ void setup()
if (resetInfo->reason != REASON_SOFT_RESTART) if (resetInfo->reason != REASON_SOFT_RESTART)
{ {
// test quickly each LED // quickly test each LED
for (int16_t row = 0; row < MATRIX_HEIGHT; row++) for (int16_t row = 0; row < MATRIX_HEIGHT; row++)
{ {
for (int16_t col = 0; col < MATRIX_WIDTH; col++) for (int16_t col = 0; col < MATRIX_WIDTH; col++)
@@ -243,40 +233,15 @@ void setup()
// init random tetris // init random tetris
random_tetris(true); random_tetris(true);
// Read nightmode setting from EEPROM // Range limits
night_mode_times.start_hour = EEPROM_read_address(ADR_NM_START_H); limit_value_ranges();
night_mode_times.start_min = EEPROM_read_address(ADR_NM_START_M);
night_mode_times.end_hour = EEPROM_read_address(ADR_NM_END_H);
night_mode_times.end_min = EEPROM_read_address(ADR_NM_END_M);
if (night_mode_times.start_hour < 0 || night_mode_times.start_hour > 23) 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));
night_mode_times.start_hour = NIGHTMODE_START_HR;
}
if (night_mode_times.start_min < 0 || night_mode_times.start_min > 59)
{
night_mode_times.start_min = NIGHTMODE_START_MIN;
}
if (night_mode_times.end_hour < 0 || night_mode_times.end_hour > 23)
{
night_mode_times.end_hour = NIGHTMODE_END_HR;
}
if (night_mode_times.end_min < 0 || night_mode_times.end_min > 59)
{
night_mode_times.end_min = NIGHTMODE_END_MIN;
}
logger.log_string("Nightmode starts at: " + String(night_mode_times.start_hour) + ":" + String(night_mode_times.start_min)); // Update brightness
logger.log_string("Nightmode ends at: " + String(night_mode_times.end_hour) + ":" + String(night_mode_times.end_min)); current_brightness = update_brightness();
logger.log_string("Brightness: " + String(((uint16_t)current_brightness * 100) / UINT8_MAX) + "%");
// Read brightness setting from EEPROM, lower limit is 10 so that the LEDs are not completely off
brightness = EEPROM_read_address(ADR_BRIGHTNESS);
if (brightness < 10)
{
brightness = 10;
}
logger.log_string("Brightness: " + String(brightness));
led_matrix.set_brightness(brightness);
} }
// ---------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------
@@ -292,7 +257,6 @@ void loop()
static uint32 last_heartbeat_us = 0; // time of last heartbeat sending static uint32 last_heartbeat_us = 0; // time of last heartbeat sending
static uint32 last_nightmode_check_us = 0; // time of last nightmode check static uint32 last_nightmode_check_us = 0; // time of last nightmode check
static uint32 last_ntp_update_us = 0; // time of last NTP update static uint32 last_ntp_update_us = 0; // time of last NTP update
static uint32 last_state_change_us = 0; // time of last state change
handleOTA(); // handle OTA handleOTA(); // handle OTA
@@ -306,7 +270,8 @@ void loop()
delay(10); delay(10);
} }
if (!night_mode && ((current_time_us - last_animation_step_us) > PERIODS[state_auto_change][current_state]) && ((current_time_us - last_led_direct_us) > TIMEOUT_LEDDIRECT_US)) if (!flg_night_mode && ((current_time_us - last_animation_step_us) > period_timings[current_state]) &&
((current_time_us - last_led_direct_us) > TIMEOUT_LEDDIRECT_US))
{ {
handle_current_state(); // handle current state handle_current_state(); // handle current state
last_animation_step_us = system_get_time(); last_animation_step_us = system_get_time();
@@ -322,18 +287,18 @@ void loop()
handle_button(); // handle button press handle_button(); // handle button press
if (state_auto_change && !night_mode && ((current_time_us - last_state_change_us) > PERIOD_STATE_CHANGE_US)) // this feature could be removed...
{
update_state_machine(); // handle state changes
last_state_change_us = system_get_time();
}
if ((current_time_us - last_ntp_update_us) > PERIOD_NTP_UPDATE_US) if ((current_time_us - last_ntp_update_us) > PERIOD_NTP_UPDATE_US)
{ {
check_wifi_status(); // check WiFi status before NTP update check_wifi_status(); // check WiFi status before NTP update
delay(10); delay(10);
ntp_time_update(&last_ntp_update_us); // ntp time update ntp_time_update(&last_ntp_update_us); // NTP time update
delay(10);
current_brightness = update_brightness(); // update brightness every PERIOD_NTP_UPDATE_US
delay(10);
logger.log_string("Brightness: " + String(((uint16_t)current_brightness * 100) / UINT8_MAX) + "%");
delay(10); delay(10);
} }
@@ -345,15 +310,9 @@ void loop()
} }
// ---------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------
// OTHER FUNCTIONS // OTHER FUNCTIONS
// ---------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------
void update_state_machine()
{
// increment state variable and trigger state change
state_change((current_state + 1) % (uint8_t)NUM_STATES);
}
void handle_current_state() void handle_current_state()
{ {
switch (current_state) switch (current_state)
@@ -376,50 +335,31 @@ void handle_current_state()
} }
case ST_SPIRAL: // state spiral case ST_SPIRAL: // state spiral
{ {
int res = draw_spiral(false, spiral_direction, MATRIX_WIDTH - 6); int res = draw_spiral(false, spiral_direction, MATRIX_WIDTH - 1);
if (res && spiral_direction == 0) if (res && spiral_direction == 0)
{ {
// change spiral direction to closing (draw empty leds) // change spiral direction to closing (draw empty leds)
spiral_direction = 1; spiral_direction = 1;
// init spiral with new spiral direction // init spiral with new spiral direction
draw_spiral(true, spiral_direction, MATRIX_WIDTH - 6); draw_spiral(true, spiral_direction, MATRIX_WIDTH - 1);
} }
else if (res && spiral_direction == 1) else if (res && spiral_direction == 1)
{ {
// reset spiral direction to normal drawing leds // reset spiral direction to normal drawing leds
spiral_direction = 0; spiral_direction = 0;
// init spiral with new spiral direction // init spiral with new spiral direction
draw_spiral(true, spiral_direction, MATRIX_WIDTH - 6); draw_spiral(true, spiral_direction, MATRIX_WIDTH - 1);
} }
break; break;
} }
case ST_TETRIS: // state tetris case ST_TETRIS: // state tetris
{ {
if (state_auto_change) tetris.loopCycle();
{
random_tetris(false);
}
else
{
tetris.loopCycle();
}
break; break;
} }
case ST_SNAKE: // state snake case ST_SNAKE: // state snake
{ {
if (state_auto_change) snake.loopCycle();
{
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; break;
} }
case ST_PINGPONG: // state ping pong case ST_PINGPONG: // state ping pong
@@ -487,11 +427,11 @@ void check_night_mode()
int hours = ntp_client.get_hours_24(); int hours = ntp_client.get_hours_24();
int minutes = ntp_client.get_minutes(); int minutes = ntp_client.get_minutes();
if (hours == night_mode_times.start_hour && minutes == night_mode_times.start_min) if ((hours == night_mode_times_ps->start_hour) && (minutes == night_mode_times_ps->start_min))
{ {
set_night_mode(true); set_night_mode(true);
} }
else if (hours == night_mode_times.end_hour && minutes == night_mode_times.end_min) else if ((hours == night_mode_times_ps->end_hour) && (minutes == night_mode_times_ps->end_min))
{ {
set_night_mode(false); set_night_mode(false);
} }
@@ -530,7 +470,7 @@ void ntp_time_update(uint32 *last_ntp_update_us)
logger.log_string("Time: " + ntp_client.get_formatted_time()); logger.log_string("Time: " + ntp_client.get_formatted_time());
logger.log_string("Date: " + ntp_client.get_formatted_date()); logger.log_string("Date: " + ntp_client.get_formatted_date());
logger.log_string("TimeOffset (seconds): " + String(ntp_client.get_time_offset())); logger.log_string("TimeOffset (seconds): " + String(ntp_client.get_time_offset()));
logger.log_string("Summertime: " + String(ntp_client.update_sw_change())); logger.log_string("Summertime: " + String(ntp_client.check_daylight_saving_time()));
*last_ntp_update_us += 10000000; *last_ntp_update_us += 10000000;
watchdog_counter--; watchdog_counter--;
break; break;
@@ -561,52 +501,31 @@ void ntp_time_update(uint32 *last_ntp_update_us)
*/ */
void on_state_entry(uint8_t state) void on_state_entry(uint8_t state)
{ {
filter_factor = 0.5; filter_factor = 0.5f;
switch (state) switch (state)
{ {
case ST_SPIRAL: case ST_SPIRAL:
{ // Init spiral with normal drawing mode {
spiral_direction = 0; spiral_direction = 0; // Init spiral with normal drawing mode
draw_spiral(true, spiral_direction, MATRIX_WIDTH - 6); draw_spiral(true, spiral_direction, MATRIX_WIDTH - 1);
break; break;
} }
case ST_TETRIS: case ST_TETRIS:
{ {
filter_factor = 1.0; // no smoothing filter_factor = 1.0f; // no smoothing
if (state_auto_change) tetris.ctrlStart();
{
random_tetris(true);
}
else
{
tetris.ctrlStart();
}
break; break;
} }
case ST_SNAKE: case ST_SNAKE:
{ {
if (state_auto_change) filter_factor = 1.0f; // no smoothing
{ snake.initGame();
random_snake(true, 8, colors_24bit[1], -1);
}
else
{
filter_factor = 1.0; // no smoothing
snake.initGame();
}
break; break;
} }
case ST_PINGPONG: case ST_PINGPONG:
{ {
if (state_auto_change) filter_factor = 1.0f; // no smoothing
{ pong.initGame(1);
pong.initGame(2);
}
else
{
filter_factor = 1.0; // no smoothing
pong.initGame(1);
}
break; break;
} }
default: default:
@@ -623,7 +542,7 @@ void on_state_entry(uint8_t state)
*/ */
void state_change(uint8_t newState) void state_change(uint8_t newState)
{ {
if (night_mode) if (flg_night_mode)
{ {
set_night_mode(false); // deactivate Nightmode set_night_mode(false); // deactivate Nightmode
} }
@@ -632,7 +551,6 @@ void state_change(uint8_t newState)
current_state = newState; // set new state current_state = newState; // set new state
on_state_entry(current_state); on_state_entry(current_state);
logger.log_string("State change to: " + state_names[current_state]); logger.log_string("State change to: " + state_names[current_state]);
delay(5);
logger.log_string("FreeMemory=" + String(ESP.getFreeHeap())); logger.log_string("FreeMemory=" + String(ESP.getFreeHeap()));
} }
@@ -719,7 +637,7 @@ void handle_button()
// shortpress -> state change // shortpress -> state change
logger.log_string("Button press ended - short press"); logger.log_string("Button press ended - short press");
if (night_mode) if (flg_night_mode)
{ {
set_night_mode(false); set_night_mode(false);
} }
@@ -740,10 +658,12 @@ void handle_button()
void set_main_color(uint8_t red, uint8_t green, uint8_t blue) void set_main_color(uint8_t red, uint8_t green, uint8_t blue)
{ {
main_color_clock = LEDMatrix::color_24bit(red, green, blue); main_color_clock = LEDMatrix::color_24bit(red, green, blue);
EEPROM.put(ADR_MC_RED, red); colors_ps->blue = blue;
EEPROM.put(ADR_MC_GREEN, green); colors_ps->red = red;
EEPROM.put(ADR_MC_BLUE, blue); colors_ps->green = green;
EEPROM.commit();
// save color settings to EEPROM
write_settings_to_EEPROM();
} }
/** /**
@@ -751,11 +671,11 @@ void set_main_color(uint8_t red, uint8_t green, uint8_t blue)
* *
*/ */
void load_main_color() void draw_main_color()
{ {
uint8_t red = EEPROM.read(ADR_MC_RED); uint8_t red = colors_ps->red;
uint8_t green = EEPROM.read(ADR_MC_GREEN); uint8_t green = colors_ps->green;
uint8_t blue = EEPROM.read(ADR_MC_BLUE); uint8_t blue = colors_ps->blue;
if ((int(red) + int(green) + int(blue)) < 50) if ((int(red) + int(green) + int(blue)) < 50)
{ {
@@ -828,69 +748,58 @@ void handle_command()
state_change(ST_HEARTS); state_change(ST_HEARTS);
} }
} }
else if (webserver.argName(0).equals("nightmode")) else if (webserver.argName(0).equals("night_mode"))
{ {
String mode_str = webserver.arg(0); String mode_str = webserver.arg(0);
logger.log_string("Nightmode change via Webserver to: " + mode_str); logger.log_string("Nightmode change via Webserver to: " + mode_str);
mode_str.equals("1") ? set_night_mode(true) : set_night_mode(false); mode_str.equals("1") ? set_night_mode(true) : set_night_mode(false);
} }
else if (webserver.argName(0).equals("dyn_brightness"))
{
String mode_str = webserver.arg(0);
logger.log_string("Dynamic brightness change via Webserver to: " + mode_str);
mode_str.equals("1") ? set_dynamic_brightness(true) : set_dynamic_brightness(false);
// Update brightness
current_brightness = update_brightness();
logger.log_string("Brightness: " + String(((uint16_t)current_brightness * 100) / UINT8_MAX) + "%");
}
else if (webserver.argName(0).equals("setting")) else if (webserver.argName(0).equals("setting"))
{ {
String cmd_str = webserver.arg(0) + "-"; String cmd_str = webserver.arg(0) + "-";
logger.log_string("Nightmode setting change via Webserver to: " + cmd_str); logger.log_string("Nightmode setting change via Webserver to: " + cmd_str);
night_mode_times.start_hour = split(cmd_str, '-', 0).toInt(); night_mode_times_ps->start_hour = (int)split(cmd_str, '-', 0).toInt();
night_mode_times.start_min = split(cmd_str, '-', 1).toInt(); night_mode_times_ps->start_min = (int)split(cmd_str, '-', 1).toInt();
night_mode_times.end_hour = split(cmd_str, '-', 2).toInt(); night_mode_times_ps->end_hour = (int)split(cmd_str, '-', 2).toInt();
night_mode_times.end_min = split(cmd_str, '-', 3).toInt(); night_mode_times_ps->end_min = (int)split(cmd_str, '-', 3).toInt();
brightness = split(cmd_str, '-', 4).toInt(); brightness_ps->static_brightness = (uint8_t)split(cmd_str, '-', 4).toInt();
reset_wifi_creds = split(cmd_str, '-', 5).toInt() > 0 ? true : false; flg_reset_wifi_creds = split(cmd_str, '-', 5).toInt() > 0 ? true : false;
brightness_ps->dyn_brightness_min = (uint8_t)split(cmd_str, '-', 6).toInt();
brightness_ps->dyn_brightness_max = (uint8_t)split(cmd_str, '-', 7).toInt();
if (brightness < 10) if (flg_reset_wifi_creds == true)
{
brightness = 10; // minimum brightness
}
if (night_mode_times.start_hour < 0 || night_mode_times.start_hour > 23)
{
night_mode_times.start_hour = NIGHTMODE_START_HR;
}
if (night_mode_times.start_min < 0 || night_mode_times.start_min > 59)
{
night_mode_times.start_min = NIGHTMODE_START_MIN;
}
if (night_mode_times.end_hour < 0 || night_mode_times.end_hour > 23)
{
night_mode_times.end_hour = NIGHTMODE_END_HR;
}
if (night_mode_times.end_min < 0 || night_mode_times.end_min > 59)
{
night_mode_times.end_min = NIGHTMODE_END_MIN;
}
if (reset_wifi_creds == true)
{ {
reset_wifi_credentials(); // this function will not return reset_wifi_credentials(); // this function will not return
} }
EEPROM_write_to_address(ADR_NM_START_H, night_mode_times.start_hour); limit_value_ranges();
EEPROM_write_to_address(ADR_NM_START_M, night_mode_times.start_min);
EEPROM_write_to_address(ADR_NM_END_H, night_mode_times.end_hour);
EEPROM_write_to_address(ADR_NM_END_M, night_mode_times.end_min);
EEPROM_write_to_address(ADR_BRIGHTNESS, brightness);
logger.log_string("Nightmode starts at: " + String(night_mode_times.start_hour) + ":" + String(night_mode_times.start_min)); // Update EEPROM with new settings
logger.log_string("Nightmode ends at: " + String(night_mode_times.end_hour) + ":" + String(night_mode_times.end_min)); write_settings_to_EEPROM();
logger.log_string("Brightness: " + String(brightness));
led_matrix.set_brightness(brightness); logger.log_string("Nightmode starts at: " + String(night_mode_times_ps->start_hour) + ":" + String(night_mode_times_ps->start_min));
} delay(10);
else if (webserver.argName(0).equals("stateautochange")) logger.log_string("Nightmode ends at: " + String(night_mode_times_ps->end_hour) + ":" + String(night_mode_times_ps->end_min));
{ delay(10);
String mode_str = webserver.arg(0);
logger.log_string("stateAutoChange change via Webserver to: " + mode_str); // Update brightness
mode_str.equals("1") ? state_auto_change = true : state_auto_change = false; current_brightness = update_brightness();
logger.log_string("Brightness: " + String(((uint16_t)current_brightness * 100) / UINT8_MAX) + "%");
} }
else if (webserver.argName(0).equals("tetris")) else if (webserver.argName(0).equals("tetris"))
{ {
String cmd_str = webserver.arg(0); String cmd_str = webserver.arg(0);
logger.log_string("Tetris cmd via Webserver to: " + cmd_str); // logger.log_string("Tetris cmd via Webserver to: " + cmd_str);
if (cmd_str.equals("up")) if (cmd_str.equals("up"))
{ {
tetris.ctrlUp(); tetris.ctrlUp();
@@ -919,7 +828,7 @@ void handle_command()
else if (webserver.argName(0).equals("snake")) else if (webserver.argName(0).equals("snake"))
{ {
String cmd_str = webserver.arg(0); String cmd_str = webserver.arg(0);
logger.log_string("Snake cmd via Webserver to: " + cmd_str); // logger.log_string("Snake cmd via Webserver to: " + cmd_str);
if (cmd_str.equals("up")) if (cmd_str.equals("up"))
{ {
snake.ctrlUp(); snake.ctrlUp();
@@ -944,7 +853,7 @@ void handle_command()
else if (webserver.argName(0).equals("pong")) else if (webserver.argName(0).equals("pong"))
{ {
String cmd_str = webserver.arg(0); String cmd_str = webserver.arg(0);
logger.log_string("Pong cmd via Webserver to: " + cmd_str); // logger.log_string("Pong cmd via Webserver to: " + cmd_str);
if (cmd_str.equals("up")) if (cmd_str.equals("up"))
{ {
pong.ctrlUp(1); pong.ctrlUp(1);
@@ -976,7 +885,7 @@ void handle_data_request()
Serial.println(webserver.arg(i)); Serial.println(webserver.arg(i));
} }
if (webserver.argName(0).equals("key")) // the parameter which was sent to this server is led color if (webserver.argName(0).equals("key"))
{ {
String message = "{"; String message = "{";
String keystr = webserver.arg(0); String keystr = webserver.arg(0);
@@ -986,15 +895,19 @@ void handle_data_request()
message += ","; message += ",";
message += "\"modeid\":\"" + String(current_state) + "\""; message += "\"modeid\":\"" + String(current_state) + "\"";
message += ","; message += ",";
message += "\"stateAutoChange\":\"" + String(state_auto_change) + "\""; message += "\"night_mode\":\"" + String(flg_night_mode) + "\"";
message += ","; message += ",";
message += "\"night_mode\":\"" + String(night_mode) + "\""; message += "\"nightModeStart\":\"" + leading_zero2digit(night_mode_times_ps->start_hour) + "-" + leading_zero2digit(night_mode_times_ps->start_min) + "\"";
message += ","; message += ",";
message += "\"nightModeStart\":\"" + leading_zero2digit(night_mode_times.start_hour) + "-" + leading_zero2digit(night_mode_times.start_min) + "\""; message += "\"nightModeEnd\":\"" + leading_zero2digit(night_mode_times_ps->end_hour) + "-" + leading_zero2digit(night_mode_times_ps->end_min) + "\"";
message += ","; message += ",";
message += "\"nightModeEnd\":\"" + leading_zero2digit(night_mode_times.end_hour) + "-" + leading_zero2digit(night_mode_times.end_min) + "\""; message += "\"static_brightness\":\"" + String(brightness_ps->static_brightness) + "\"";
message += ","; message += ",";
message += "\"brightness\":\"" + String(brightness) + "\""; message += "\"dyn_brightness\":\"" + String(brightness_ps->flg_dynamic_brightness) + "\"";
message += ",";
message += "\"min_brightness\":\"" + String(brightness_ps->dyn_brightness_min) + "\"";
message += ",";
message += "\"max_brightness\":\"" + String(brightness_ps->dyn_brightness_max) + "\"";
} }
message += "}"; message += "}";
webserver.send(200, "application/json", message); webserver.send(200, "application/json", message);
@@ -1010,32 +923,17 @@ void set_night_mode(bool state)
{ {
led_matrix.flush(); led_matrix.flush();
led_matrix.draw_on_matrix_smooth(0.2); led_matrix.draw_on_matrix_smooth(0.2);
night_mode = state; flg_night_mode = state;
} }
/** /**
* @brief Write value to EEPROM * @brief Set the dynamic brightness state
* *
* @param address address to write the value * @param state true -> nightmode on
* @param value value to write
*/ */
void EEPROM_write_to_address(int address, int value) void set_dynamic_brightness(bool state)
{ {
EEPROM.put(address, value); brightness_ps->flg_dynamic_brightness = state;
EEPROM.commit();
}
/**
* @brief Read value from EEPROM
*
* @param address address
* @return int value
*/
int EEPROM_read_address(int address)
{
int value;
EEPROM.get(address, value);
return value;
} }
/** /**
@@ -1066,3 +964,120 @@ void reset_wifi_credentials()
delay(200); delay(200);
ESP.restart(); ESP.restart();
} }
/**
* @brief Calculate dynamic brightness value based on daytime.
*
* @param min_brightness max brightness value set by the user
* @param max_brightness min brightness value set by the user
* @param hours current hour value
* @param minutes current minute value
* @param summertime indicates if summertime
* @return dynamic brightness
*/
uint8_t calculate_dynamic_brightness(uint8_t min_brightness, uint8_t max_brightness,
int hours, int minutes, bool summertime)
{
uint8_t calc_brightness = 0;
uint8_t factor_index = 0;
// Calculate index based on current time, respecting array length (and wrap)
factor_index = (uint8_t)(((hours + (int)summertime) * MINUTES_IN_HOUR + minutes) /
(MINUTES_IN_HOUR / ((sizeof(qtly_brightness_factor) / sizeof(float)) / HOURS_IN_DAY))) %
(sizeof(qtly_brightness_factor) / sizeof(float));
// function for calc_brightness: f(x) = min + (max - min)(1 - cos((t * 2 * Pi) / (96))) / 2
calc_brightness = min_brightness +
(uint8_t)(((float)(max_brightness - min_brightness)) * qtly_brightness_factor[factor_index]);
// ouput limits
calc_brightness = RANGE_LIMIT(calc_brightness, MIN_BRIGHTNESS, UINT8_MAX);
return calc_brightness;
}
/**
* @brief Init function. Reads (global) user settings from EEPROM. Call this after EEPROM init!
*
* @return void
*/
void read_settings_from_EEPROM()
{
EEPROM.get(0, eeprom_buffer);
}
/**
* @brief Writes (global) user settings to EEPROM
*
* @return void
*/
void write_settings_to_EEPROM()
{
// Copy EEPROM buffer
EEPROM.put(0, eeprom_buffer);
// Commit changes
EEPROM.commit();
}
/**
* @brief Updates brightness based on chosen mode.
*
* @return newly calculated and set brightness
*/
uint8_t update_brightness()
{
uint8_t new_brightness = 0;
if (brightness_ps->flg_dynamic_brightness == true)
{
new_brightness = calculate_dynamic_brightness(brightness_ps->dyn_brightness_min,
brightness_ps->dyn_brightness_max,
ntp_client.get_hours_24(),
ntp_client.get_minutes(),
ntp_client.check_daylight_saving_time());
}
else // use static brightness
{
new_brightness = brightness_ps->static_brightness;
}
// now set new brightness
led_matrix.set_brightness(new_brightness);
return new_brightness;
}
/**
* @brief Limits values ranges of user settings
*
* @return void
*/
void limit_value_ranges()
{
// Range limits
brightness_ps->dyn_brightness_min = RANGE_LIMIT(brightness_ps->dyn_brightness_min,
MIN_BRIGHTNESS,
brightness_ps->dyn_brightness_max - 1); // minimum brightness
brightness_ps->dyn_brightness_max = RANGE_LIMIT(brightness_ps->dyn_brightness_max,
brightness_ps->dyn_brightness_min + 1,
MAX_BRIGHTNESS); // maximum brightness
brightness_ps->static_brightness = RANGE_LIMIT(brightness_ps->static_brightness,
MIN_BRIGHTNESS,
MAX_BRIGHTNESS); // static brightness
night_mode_times_ps->start_hour = RANGE_LIMIT_SUB(night_mode_times_ps->start_hour,
0,
HOUR_MAX,
NIGHTMODE_START_HR);
night_mode_times_ps->start_min = RANGE_LIMIT_SUB(night_mode_times_ps->start_min,
0,
MINUTE_MAX,
NIGHTMODE_START_MIN);
night_mode_times_ps->end_hour = RANGE_LIMIT_SUB(night_mode_times_ps->end_hour,
0,
HOUR_MAX,
NIGHTMODE_END_HR);
night_mode_times_ps->end_min = RANGE_LIMIT_SUB(night_mode_times_ps->end_min,
0,
MINUTE_MAX,
NIGHTMODE_END_MIN);
}