Compare commits

...

13 Commits

19 changed files with 474 additions and 400 deletions

13
.gitignore vendored
View File

@@ -65,4 +65,15 @@ log.txt
modules.order
Module.symvers
Mkfile.old
dkms.conf
dkms.conf
# Python
venv
.venv
# Logs
*.log
# Local backup
*.*_
*.*bak*

View File

@@ -1,96 +1,49 @@
# Important Note:
This project has been unofficially forked from https://github.com/techniccontroller/wordclock_esp8266 which was initially created by techniccontroller. Copyright and licensing is respected.
This project has been unofficially forked from https://github.com/techniccontroller/wordclock_esp8266 which was initially created by techniccontroller. Copyright and licensing is respected. Very many thanks for the initial code!
# Wordclock 2.0
Wordclock 2.0 with ESP8266 and NTP time
More details on my website: https://techniccontroller.com/word-clock-with-wifi-and-neopixel/
More details on techniccontroller's website: https://techniccontroller.com/word-clock-with-wifi-and-neopixel/
**Languages**
The Wordclock is available in **German**, **English** and **Italian** language. By default the language is German.
To use the English or Italian language please replace the file *wordclockfunctions.ino* with *wordclockfunctions.ino_english* or *wordclockfunctions.ino_italian*.
The code compiles only with one file named *wordclockfunctions.ino*. So please rename the file you want to use to *wordclockfunctions.ino* and replace the existing file.
The Wordclock is available in **German** language.
## Features
- 6 modes (Clock, Digital Clock, SPIRAL animation, TETRIS, SNAKE, PONG)
- time update via NTP server
- automatic summer/wintertime change
- easy WIFI setup with WifiManager
- configurable color
- configurable night mode (start and end time)
- configurable brightness
- automatic mode change
- webserver interface for configuration and control
- physical button to change mode or enable night mode without webserver
- automatic current limiting of LEDs
- Time update via NTP server
- Games: Pong, Snake, Tetris, ...
- Automatic summer/wintertime change
- Easy wifi setup with WifiManager
- Configurable color
- Configurable night mode (start and end time)
- Configurable brightness
- Automatic mode change
- Webserver interface for configuration and control (web address: wordclock.local)
- Automatic current limiting of LEDs
## Pictures of clock
![modes_images2](https://user-images.githubusercontent.com/36072504/156947689-dd90874d-a887-4254-bede-4947152d85c1.png)
## Screenshots of webserver UI
![screenshots_UI](https://user-images.githubusercontent.com/36072504/158478447-d828e460-d4eb-489e-981e-216e08d4b129.png)
## Quickstart
1. Clone the project into the sketch folder of the Arduino IDE,
2. Rename the file "example_secrets.h" to "secrets.h". You don't need to change anything in the file if you want uses the normal WiFi setup with WiFiManager (see section "Remark about the WiFi setup").
3. Install the additional libraries and flash it to the ESP8266 as usual (See section [*Upload program to ESP8266*](https://github.com/techniccontroller/wordclock_esp8266/blob/main/README.md#upload-program-to-esp8266-with-arduino-ide) below).
4. The implemented WiFiManager helps you to set up a WiFi connection with your home WiFi -> on the first startup it will create a WiFi access point named "WordclockAP". Connect your phone to this access point and follow the steps which will be shown to you.
5. After a successful WiFi setup, open the browser and enter the IP address of your ESP8266 to access the interface of the webserver.
6. Here you can then upload all files located in the folder "data". Please make sure all icons stay in the folder "icons" also on the webserver.
<img src="https://techniccontroller.com/wp-content/uploads/filemanager1-1.png" height="300px" /> <img src="https://techniccontroller.com/wp-content/uploads/filemanager2-1.png" height="300px" /> <img src="https://techniccontroller.com/wp-content/uploads/filemanager3-1.png" height="300px" />
## Install needed Libraries
Please download all these libraries as ZIP from GitHub, and extract them in the *libraries* folder of your Sketchbook location (see **File -> Preferences**):
- https://github.com/adafruit/Adafruit-GFX-Library
- https://github.com/adafruit/Adafruit_NeoMatrix
- https://github.com/adafruit/Adafruit_NeoPixel
- https://github.com/tzapu/WiFiManager
- https://github.com/adafruit/Adafruit_BusIO
1. Clone the project into the a directory.
2. Open directory in VSCode with PlatformIO extension installed.
3. Current dependencies (2024-07-21):
- Platform espressif8266 @ 2.6.3 (required: espressif8266 @ 2.6.3)
- Libraries
- ├── FastLED @ 3.7.0 (required: fastled/FastLED @ 3.7.0)
- ├── FastLED NeoMatrix @ 1.2.0 (required: marcmerlin/FastLED NeoMatrix @ ^1.2)
- │⠀⠀⠀└── Framebuffer GFX @ 1.1.0 (required: Framebuffer GFX)
- │⠀⠀⠀│⠀⠀⠀├── Adafruit GFX Library @ 1.11.9 (required: Adafruit GFX Library)
- │⠀⠀⠀│⠀⠀⠀│⠀⠀⠀└── Adafruit BusIO @ 1.16.1 (required: Adafruit BusIO)
- ├── WiFiManager @ 0.16.0 (required: tzapu/WiFiManager @ ^0.16.0)
- └── base64 @ 1.4.0 (required: densaugeo/base64 @ ^1.4.0)
## Remark about the WiFi setup
Regarding the Wifi setting, I have actually implemented two variants:
1. By default the WifiManager is activated. That is, the word clock makes the first time its own WiFi (should be called "WordclockAP"). There you simply connect to the cell phone and you can perform configuration of the WiFi settings conveniently as with a SmartHome devices (Very elegant 😊)
2. Another (traditional) variant is to define the wifi credentials in the code (in secrets.h).
- For this you have to comment out lines 230 to 251 in the code of the file *wordclock_esp8266.ino* (/\* before and \*/ after)
- and comment out lines 257 to 305 (/\* and \*/ remove)
## Remark about Logging
The wordclock send continuously log messages to the serial port and via multicast UDP. If you want to see these messages, you have to
- open the serial monitor in the Arduino IDE (Tools -> Serial Monitor). The serial monitor must be set to 115200 baud.
OR
- run the following steps for the multicast UDP logging:
1. starting situation: wordclock is connected to WLAN, a computer with installed Python (https://www.python.org/downloads/) is in the same local area network (WLAN or LAN doesn't matter).
3. open the file **multicastUDP_receiver.py** in a text editor and in line 81 enter the IP address of the computer (not the wordclock!).
```python
# ip address of network interface
MCAST_IF_IP = '192.168.0.7'
```
4. execute the script with following command:
```bash
python multicastUDP_receiver_analyzer.py
```
5. now you should see the log messages of the word clock (every 5 seconds a heartbeat message and the currently displayed time).
If this is not the case, there could be a problem with the network settings of the computer, then recording is unfortunately not possible.
6. If special events (failed NTP update, reboot) occur, a section of the log is saved in a file called *log.txt*.
In principle, the events are not critical and will occur from time to time, but should not be too frequent.
By default the WifiManager is activated. That is, the word clock makes the first time its own WiFi (should be called "WordclockAP"). There you simply connect to the cell phone and you can perform configuration of the WiFi settings conveniently as with SmartHome devices.

26
include/diagnosis.h Normal file
View File

@@ -0,0 +1,26 @@
#ifndef DIAGNOSIS_H
#define DIAGNOSIS_H
#include <Arduino.h>
#include "led_matrix.h"
#include "udp_logger.h"
class Diagnosis
{
public:
Diagnosis(); // constructor
Diagnosis(UDPLogger *logger, LEDMatrix *matrix); // constructor
String handle_command(const String &command);
String print_device_info();
String print_sketch_info();
String print_last_reset_details();
String print_matrix_fps();
private:
UDPLogger *_logger;
LEDMatrix * _matrix;
void print(const String &s);
};
#endif // DIAGNOSIS_H

View File

@@ -1,24 +1,28 @@
#ifndef LEDMATRIX_H
#define LEDMATRIX_H
#ifndef FASTLED_INTERNAL
#define FASTLED_INTERNAL
#endif
#include <Arduino.h>
#include <Adafruit_GFX.h>
#include <Adafruit_NeoMatrix.h>
#include <FastLED_NeoMatrix.h>
#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 */
#endif /* LEDMATRIX_H */

View File

@@ -79,8 +79,8 @@ private:
UDPLogger *_logger;
uint8_t _gameState = 0;
uint8_t _numBots = 0;
uint8_t _playerMovement[PLAYER_AMOUNT] = {0};
Coords _paddles[PLAYER_AMOUNT][PADDLE_WIDTH] = {0};
uint8_t _playerMovement[PLAYER_AMOUNT];
Coords _paddles[PLAYER_AMOUNT][PADDLE_WIDTH];
Coords _ball = {0, 0};
Coords _ball_old = {0, 0};
int _ballMovement[2] = {0, 0};

View File

@@ -76,7 +76,7 @@ private:
uint8_t _userDirection = 0;
uint8_t _gameState = 0;
Coords _head = {0, 0};
Coords _tail[MAX_TAIL_LENGTH] = {0};
Coords _tail[MAX_TAIL_LENGTH];
Coords _food = {0, 0};
unsigned long _lastDrawUpdate = 0;
unsigned long _lastButtonClick = 0;

View File

@@ -7,53 +7,54 @@
typedef enum
{
NTP_UPDATE_FAILED = 0,
NTP_UPDATE_OK = 1,
NTP_UPDATE_PENDING = 2,
NTP_UPDATE_RETRY_DELAY = 3,
NTP_UPDATE_TOO_EARLY = 4,
NTP_UPDATE_SETUP_FAILED = 5,
} NtpUpdateState;
TIME_UPDATE_FAILED = 0,
TIME_UPDATE_OK = 1,
TIME_UPDATE_PENDING = 2,
} TimeUpdateState;
typedef enum
{
TM_INIT = 0,
TM_INITIAL_SYNC = 1,
TM_RETRY_SYNC = 2,
TM_NORMAL = 3,
TM_PROLONGED_SYNC_FAIL = 4,
TM_NORMAL = 2,
TM_SYNC_OVERDUE = 3,
TM_SYNC_TIMEOUT = 4,
TM_SETUP_FAILED = 5,
} TimeManagerState;
class TimeManager
{
#define NTP_MAX_UPDATE_TIME_US (500 * 1000) // 500ms max update time
#define NTP_MAX_UPDATE_TIME_US (5 * 1000 * 1000) // 5000ms max update time
public:
// constructor
// constructors
TimeManager();
TimeManager(const char *tz,
const char *ntp_server,
uint32 ntp_update_period_s,
uint32 ntp_retry_delay_us,
bool (*is_wifi_connected)(void),
uint32 ntp_max_offline_time_s,
UDPLogger *logger);
// ntp methods
bool ntp_sync_successful(void) const; // was there a NTP sync once?
bool ntp_update_failed_prolonged(void); // indicates if maximum time since last NTP update was too long
NtpUpdateState ntp_time_update(); // main NTP time update method, called in loop
// init
void init();
// ISR method
void increment_time_now_local(void); // should be called by timer ISR
// callback
void time_set_cb(void); // callback which is called when NTP time was set
// ntp methods
bool ntp_sync_successful(void) const; // was there a NTP sync once?
bool ntp_sync_overdue(void); // function to check if NTP sync is overdue
bool ntp_sync_timeout(void); // function to check if maximum time since last NTP sync has been reached
TimeUpdateState get_time(); // main time update method, called in loop
// getter for time values
bool tm_isdst(void); // true if summertime
int tm_day(void);
int tm_hour(void);
int tm_min(void);
int tm_mon(void);
int tm_year(void);
struct tm time_info(void);
bool isdst(void) const; // true if summertime (daylight saving time)
int day(void) const;
int hour(void) const;
int minute(void) const;
int month(void) const;
int year(void) const;
struct tm time_info(void) const;
// getter
TimeManagerState tm_state(void) const; // get current state
@@ -63,26 +64,17 @@ public:
void log_time(struct tm time_info) const; // log argument time_info
private:
// setup methods
void _set_up_ntp(void); // set up NTP server
void _set_up_timer_isr(void); // set up timer interrupt
void _set_up_ntp(void); // set up NTP server
bool (*_is_wifi_connected)(void); // function to check if wifi is connected
const char *_ntp_server = "pool.ntp.org"; // ntp server address
const char *_tz; // timezone
const char *_ntp_server; // ntp server address
UDPLogger *_logger; // logger instance
TimeManagerState _tm_state = TM_INIT; // Main state
struct tm _time_info = {0, 0, 0, 0, 0, 0, 0, 0, 0}; // structure tm holds time information
time_t _time_now_local = 0; // local timer value, updated by timer interrupt and synced by NTP when needed
time_t _time_now_ntp = 0; // NTP timer value, seconds since Epoch (1970) - UTC, only synced by NTP request.
time_t _now = 0; // local time value
time_t _ntp_sync_timestamp_s = 0; // timestamp of last successful ntp sync
TimeManagerState _tm_state = TM_INIT; // Main state
UDPLogger *_logger; // logger instance
uint32 _ntp_max_offline_time_s; // maximum time in seconds which is considered ok since last NTP update
uint32 _ntp_update_period_s; // NTP request update period in seconds
uint32 _ntp_retry_delay_us; // minimum retry delay in us between two NTP requests
uint32 _ntp_sync_timestamp_us = 0; // timestamp of last successful ntp update
};
inline void TimeManager::increment_time_now_local(void)
{
_time_now_local++;
}
#endif /* TIME_MANAGER_H */

View File

@@ -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,9 +36,9 @@
#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_TIME_UPDATE_US (500 * 1000) // 500ms
#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
#define PERIOD_STATE_CHANGE_US (10 * 1000 * 1000) // 10s
@@ -63,17 +63,17 @@
// 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.
#define NTP_MININUM_RX_YEAR (atoi(BUILD_YEAR) - 1) // Will expand to current year minus one at compile time.
#define NTP_START_YEAR (1900) // NTP minimum year is 1900
#define NTP_UPDATE_PERIOD_S (6 * 3600) // 6h period between NTP updates
#define NTP_RETRY_DELAY_US (10 * 1000 * 1000) // 10s retry delay time between failed NTP requests
#define NTP_MAX_OFFLINE_TIME_S (7 * 24 * 3600) // Watchdog value, maxmimum offline time before a restart is triggered
#define BUILD_YEAR (__DATE__ + 7) // Will expand to current year at compile time as string.
#define NTP_MINIMUM_RX_YEAR (atoi(BUILD_YEAR) - 1) // Will expand to current year minus one at compile time.
#define NTP_START_YEAR (1900) // NTP minimum year is 1900
#define NTP_MAX_OFFLINE_TIME_S (7 * 24 * 3600) // Watchdog value, maximum offline time before a restart is triggered
#endif /* WORDCLOCK_CONSTANTS_H */

View File

@@ -82,7 +82,7 @@ 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_night_mode(bool on);
void state_change(uint8_t newState);
void state_change(ClockState_en new_state);
void update_matrix(void);
void write_settings_to_EEPROM(void);

View File

@@ -12,17 +12,16 @@
default_envs = nodemcuv2
[env]
platform = espressif8266
platform = espressif8266@2.6.3
board = nodemcuv2
build_flags = -DUSING_TIM_DIV16=1
framework = arduino
lib_deps =
adafruit/Adafruit BusIO@^1.15.0
adafruit/Adafruit NeoMatrix@^1.3.0
adafruit/Adafruit NeoPixel@^1.11.0
densaugeo/base64@^1.4.0
khoih-prog/ESP8266TimerInterrupt@^1.6.0
tzapu/WiFiManager@^0.16.0
fastled/FastLED@3.7.6
marcmerlin/FastLED NeoMatrix@^1.2
tzapu/WiFiManager@^2.0.17
build_flags =
-DFASTLED_ESP8266_RAW_PIN_ORDER
[env:nodemcuv2]
monitor_speed = 115200

13
scripts/http_diagnosis.py Normal file
View File

@@ -0,0 +1,13 @@
import requests
r = requests.get("http://wordclock.local/cmd?diag=reset_info")
print(r.status_code)
print(r.text)
r = requests.get("http://wordclock.local/cmd?diag=sketch_info")
print(r.status_code)
print(r.text)
r = requests.get("http://wordclock.local/cmd?diag=device_info")
print(r.status_code)
print(r.text)

View File

@@ -4,15 +4,17 @@ import queue
import socket
import struct
import sys
from pathlib import Path
LOG_PATH = Path("C:/temp/wordclock_log.txt")
def setup_logging():
FORMAT = "%(asctime)s %(message)s"
logging.basicConfig(format=FORMAT, level=logging.INFO)
logger = logging.getLogger()
#handler = logging.StreamHandler(sys.stdout)
#handler.setLevel(logging.INFO)
#logger.addHandler(handler)
handler = logging.FileHandler(LOG_PATH)
handler.setLevel(logging.INFO)
logger.addHandler(handler)
return logger
@@ -42,33 +44,9 @@ mreq = struct.pack("4s4s", group, socket.inet_aton(get_ip_address()))
sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
logger.info("Ready")
saveCounter = 0
buffer = queue.Queue(20)
# Receive/respond loop
while True:
data, address = sock.recvfrom(1024)
data_str = data.decode("utf-8").strip()
logger.info(data_str)
data_str = datetime.now().strftime("%b-%d-%Y_%H%M%S") + ": " + data_str
buffer.put(data_str)
if buffer.full():
buffer.get()
if "NTP-Update not successful" in data_str or "Start program" in data_str:
f = open("log.txt", "a")
while not buffer.empty():
f.write(buffer.get())
f.write("\n")
f.close()
saveCounter = 20
if saveCounter > 0:
f = open("log.txt", "a")
f.write(data_str)
f.write("\n")
if saveCounter == 1:
f.write("\n")
f.close()
saveCounter -= 1

BIN
scripts/requirements.txt Normal file

Binary file not shown.

View File

@@ -0,0 +1,93 @@
#include "diagnosis.h"
Diagnosis::Diagnosis()
{
_logger = nullptr;
}
Diagnosis::Diagnosis(UDPLogger *logger, LEDMatrix *matrix) // constructor
{
_logger = logger;
_matrix = matrix;
}
String Diagnosis::handle_command(const String &command)
{
if (command == "device_info")
{
return print_device_info();
}
else if (command == "sketch_info")
{
return print_sketch_info();
}
else if (command == "reset_info")
{
return print_last_reset_details();
}
else if (command == "matrix_fps")
{
return print_matrix_fps();
}
else
{
// Handle unknown command
String unknown_command = "Diagnosis: Unknown command!\n";
print(unknown_command);
return unknown_command;
}
}
String Diagnosis::print_device_info()
{
// Retrieve and print device information
String device_info = "Device Information:\n";
device_info += "Chip ID: " + String(ESP.getChipId()) + "\n";
device_info += "Flash Chip ID: " + String(ESP.getFlashChipId()) + "\n";
device_info += "Flash Chip Size: " + String(ESP.getFlashChipSize()) + " bytes\n";
device_info += "Free Heap Size: " + String(ESP.getFreeHeap()) + " bytes\n";
device_info += "Free Sketch Space: " + String(ESP.getFreeSketchSpace()) + " bytes\n";
device_info += "SDK Version: " + String(ESP.getSdkVersion()) + "\n";
print(device_info);
return device_info;
}
String Diagnosis::print_sketch_info()
{
// Retrieve and print sketch information
String sketch_info = "Sketch Information:\n";
sketch_info += "Sketch Size: " + String(ESP.getSketchSize()) + " bytes\n";
sketch_info += "Sketch MD5: " + String(ESP.getSketchMD5()) + "\n";
print(sketch_info);
return sketch_info;
}
String Diagnosis::print_last_reset_details()
{
// Retrieve and print last reset details
String reset_info = "Last Reset Details:\n";
reset_info += "Reset Reason: " + String(ESP.getResetReason()) + "\n";
reset_info += "Reset Info: " + String(ESP.getResetInfo()) + "\n";
print(reset_info);
return reset_info;
}
String Diagnosis::print_matrix_fps()
{
// Retrieve and print matrix FPS
String matrix_fps = "Matrix FPS: " + String(_matrix->get_fps()) + "\n";
print(matrix_fps);
return matrix_fps;
}
void Diagnosis::print(const String &s)
{
if (_logger != nullptr)
{
_logger->log_string(s);
}
else
{
Serial.println(s);
}
}

View File

@@ -1,41 +1,76 @@
#include "time_manager.h"
#include <ESP8266TimerInterrupt.h> // https://github.com/khoih-prog/ESP8266TimerInterrupt
#include "wordclock_constants.h"
#include "time.h"
#include <coredecls.h> // required for settimeofday_cb()
#include <sntp.h>
#include <time.h>
extern UDPLogger logger; // logging instance
extern ESP8266Timer ITimer; // ESP8266 Timer
extern void IRAM_ATTR TimerHandler(); // ISR function
extern UDPLogger logger; // logging instance
// ----------------------------------------------------------------------------------
// Class
// ----------------------------------------------------------------------------------
TimeManager::TimeManager(){};
TimeManager::TimeManager(const char *tz,
const char *ntp_server,
uint32 ntp_update_period_s,
uint32 ntp_retry_delay_us,
bool (*is_wifi_connected)(void),
uint32 ntp_max_offline_time_s,
UDPLogger *logger)
{
_tz = tz;
_ntp_server = ntp_server;
_is_wifi_connected = is_wifi_connected;
_ntp_max_offline_time_s = ntp_max_offline_time_s;
_ntp_retry_delay_us = ntp_retry_delay_us;
_ntp_update_period_s = ntp_update_period_s;
_logger = logger;
}
void TimeManager::init()
{
// Set up NTP server once
_set_up_ntp();
if (_tm_state == TM_INITIAL_SYNC)
{
// force sntp reinit now
sntp_stop();
sntp_init();
}
settimeofday_cb([&]() { time_set_cb(); });
}
bool TimeManager::ntp_sync_successful(void) const
{
return (_time_now_ntp > 0);
return (_now > 1716913300); // UTC timestamp in the past (28.05.2024)
}
bool TimeManager::ntp_update_failed_prolonged(void)
bool TimeManager::ntp_sync_overdue(void)
{
bool retval = false;
if (_time_now_local >= (_time_now_ntp + (time_t)_ntp_max_offline_time_s))
if (!ntp_sync_successful())
{
_tm_state = TM_PROLONGED_SYNC_FAIL;
return false;
}
bool retval = false;
// after the ntp sync update delay has been reached six times, the sync is considered overdue
if (_now >= (_ntp_sync_timestamp_s + (time_t)(6 * (sntp_update_delay_MS_rfc_not_less_than_15000() / 1000))))
{
_tm_state = TM_SYNC_OVERDUE;
retval = true;
}
return retval;
}
bool TimeManager::ntp_sync_timeout(void)
{
if (!ntp_sync_successful())
{
return false;
}
bool retval = false;
// after the maxmimum offline time has been reached, the sync is considered timed out
if (_now >= (_ntp_sync_timestamp_s + (time_t)_ntp_max_offline_time_s))
{
_tm_state = TM_SYNC_TIMEOUT;
retval = true;
}
return retval;
@@ -46,117 +81,67 @@ bool TimeManager::ntp_update_failed_prolonged(void)
*
* @retval true if last update was successful
*/
NtpUpdateState TimeManager::ntp_time_update()
TimeUpdateState TimeManager::get_time()
{
NtpUpdateState retval = NTP_UPDATE_PENDING; // NTP time update
struct tm time_info; // local NTP time info
// Set up NTP server once
if (_tm_state == TM_INIT)
{
_set_up_ntp();
}
if (_tm_state == TM_SETUP_FAILED)
{
return NTP_UPDATE_SETUP_FAILED;
}
// Check if minimum update delay has elapsed. This is always active to prevent too many NTP server requests!
if ((_tm_state != TM_INITIAL_SYNC) && (system_get_time() - _ntp_sync_timestamp_us) <= _ntp_retry_delay_us)
{
return NTP_UPDATE_RETRY_DELAY;
}
// Check if it is time for a NTP sync.
if ((_tm_state != TM_INITIAL_SYNC) && (_tm_state != TM_RETRY_SYNC) && (((system_get_time() - _ntp_sync_timestamp_us) / 1000000) <= _ntp_update_period_s))
{
return NTP_UPDATE_TOO_EARLY;
}
_ntp_sync_timestamp_us = system_get_time(); // NTP update start time
TimeUpdateState retval = TIME_UPDATE_PENDING; // NTP time update
uint32 timestamp_us = system_get_time(); // NTP update start time
struct tm time_info; // local NTP time info
do
{
time(&_time_now_ntp); // get time from server and save it into _time_now_ntp
localtime_r(&_time_now_ntp, &time_info); // convert time
yield(); // since this loop could take up to NTP_MAX_UPDATE_TIME_US
_now = time(nullptr); // update time
(void)localtime_r(&_now, &time_info); // convert time
yield(); // since this loop could take up to NTP_MAX_UPDATE_TIME_US
} while (((system_get_time() - _ntp_sync_timestamp_us) <= NTP_MAX_UPDATE_TIME_US) && (time_info.tm_year < (NTP_MININUM_RX_YEAR - NTP_START_YEAR)));
} while (((system_get_time() - timestamp_us) <= NTP_MAX_UPDATE_TIME_US) && (time_info.tm_year < (NTP_MINIMUM_RX_YEAR - NTP_START_YEAR)));
retval = (time_info.tm_year <= (NTP_MININUM_RX_YEAR - NTP_START_YEAR)) ? NTP_UPDATE_FAILED : NTP_UPDATE_OK; // sanity check
retval = (time_info.tm_year <= (NTP_MINIMUM_RX_YEAR - NTP_START_YEAR)) ? TIME_UPDATE_FAILED : TIME_UPDATE_OK; // sanity check
if (retval == NTP_UPDATE_OK)
if (retval == TIME_UPDATE_OK)
{
_ntp_sync_timestamp_us = system_get_time(); // save NTP update timestamp
_time_info = time_info; // take over time_info to member variable
log_time(); // log current time
if ((_tm_state != TM_INITIAL_SYNC) && (abs(_time_now_ntp - _time_now_local) > 10)) // in the case that the local time drifted more than 10s in _ntp_update_period_s
{
_logger->log_string(String("Difference between local and NTP time was more than 10 seconds!\n"));
_logger->log_string("Local time was: " + String(_time_now_local) + ", NTP time is: " + String(_time_now_ntp) + "\n");
}
_time_now_local = _time_now_ntp; // sync local time with NTP time
if (_tm_state == TM_INITIAL_SYNC) // only set up the timer once after NTP update was successful
{
_set_up_timer_isr();
}
else // set state to normal
{
_tm_state = TM_NORMAL;
}
_time_info = time_info; // take over time_info to member variable
_tm_state = TM_NORMAL;
}
else
{
_tm_state = TM_RETRY_SYNC;
logger.log_string("NTP-Update was not successful. Retrying in " + String(_ntp_retry_delay_us / 1000) + "ms.");
logger.log_string("NTP-Update was not successful. Retrying in " + String(sntp_update_delay_MS_rfc_not_less_than_15000()) + "ms.");
}
return retval;
}
bool TimeManager::tm_isdst(void)
bool TimeManager::isdst(void) const
{
localtime_r(&_time_now_local, &_time_info); // convert time
return _time_info.tm_isdst > 0;
return (_time_info.tm_isdst > 0);
}
int TimeManager::tm_day(void)
int TimeManager::day(void) const
{
localtime_r(&_time_now_local, &_time_info); // convert time
return _time_info.tm_mday + 1; // add 1 to get actual day
return (_time_info.tm_mday + 1); // add 1 to get actual day
}
int TimeManager::tm_hour(void)
int TimeManager::hour(void) const
{
localtime_r(&_time_now_local, &_time_info); // convert time
return _time_info.tm_hour;
}
int TimeManager::tm_min(void)
int TimeManager::minute(void) const
{
localtime_r(&_time_now_local, &_time_info); // convert time
return _time_info.tm_min;
}
int TimeManager::tm_mon(void)
int TimeManager::month(void) const
{
localtime_r(&_time_now_local, &_time_info); // convert time
return _time_info.tm_mon + 1; // add 1 to get actual month
return (_time_info.tm_mon + 1); // add 1 to get actual month
}
int TimeManager::tm_year(void)
int TimeManager::year(void) const
{
localtime_r(&_time_now_local, &_time_info); // convert time
return _time_info.tm_year + NTP_START_YEAR; // add start year to get actual year
}
struct tm TimeManager::time_info(void)
struct tm TimeManager::time_info(void) const
{
localtime_r(&_time_now_local, &_time_info); // convert time
return _time_info;
}
@@ -207,21 +192,23 @@ void TimeManager::_set_up_ntp(void)
}
}
/**
* @brief Sets up the timer ISR
*
*/
void TimeManager::_set_up_timer_isr(void)
void TimeManager::time_set_cb(void)
{
// set up timer interrupt after NTP update is done
if (ITimer.attachInterruptInterval(PERIOD_CLOCK_UPDATE_US, TimerHandler))
if ((*_is_wifi_connected)())
{
_tm_state = TM_NORMAL;
logger.log_string(String("Timer ISR was attached successfully!"));
if (get_time() == TIME_UPDATE_OK)
{
_ntp_sync_timestamp_s = _now;
logger.log_string("NTP successfully synced at...");
log_time();
}
else
{
logger.log_string("NTP sync failed!");
}
}
else
{
_tm_state = TM_SETUP_FAILED;
logger.log_string("WARNING: Timer interrupt was not attached!");
logger.log_string("NTP sync failed, WiFi is not connected!");
}
}

View File

@@ -1,8 +1,6 @@
#include "udp_logger.h"
UDPLogger::UDPLogger()
{
}
UDPLogger::UDPLogger() {}
UDPLogger::UDPLogger(IPAddress interface_addr, IPAddress multicast_addr, int port, String name)
{
@@ -26,8 +24,10 @@ void UDPLogger::log_string(String message)
delay(10);
}
message = _name + ": " + message;
#ifdef SERIAL_DEBUG
Serial.println(message);
delay(10);
#endif /* SERIAL_DEBUG */
_udp.beginPacketMulticast(_multicastAddr, _port, _interfaceAddr);
message.toCharArray(_packetBuffer, 100);
_udp.print(_packetBuffer);

View File

@@ -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
*

View File

@@ -179,58 +179,79 @@ String time_to_string(uint8_t hours, uint8_t minutes)
// show hours
switch (hours)
{
case 0:
if (minutes >= 0 && minutes < 5)
case 0:
{
message += "MITTERNACHT ";
if (minutes >= 0 && minutes < 5)
{
message += "MITTERNACHT ";
}
else
{
message += "ZWOLF ";
}
break;
}
else
case 1:
{
message += "EIN";
message += (minutes > 4) ? "S " : " "; // add "S" if needed
break;
}
case 2:
{
message += "ZWEI ";
break;
}
case 3:
{
message += "DREI ";
break;
}
case 4:
{
message += "VIER ";
break;
}
case 5:
{
message += "FUNF ";
break;
}
case 6:
{
message += "SECHS ";
break;
}
case 7:
{
message += "SIEBEN ";
break;
}
case 8:
{
message += "ACHT ";
break;
}
case 9:
{
message += "NEUN ";
break;
}
case 10:
{
message += "ZEHN ";
break;
}
case 11:
{
message += "ELF ";
break;
}
case 12:
{
message += "ZWOLF ";
break;
}
break;
case 1:
message += "EIN";
// EIN(S)
if (minutes > 4)
{
message += "S";
}
message += " ";
break;
case 2:
message += "ZWEI ";
break;
case 3:
message += "DREI ";
break;
case 4:
message += "VIER ";
break;
case 5:
message += "FUNF ";
break;
case 6:
message += "SECHS ";
break;
case 7:
message += "SIEBEN ";
break;
case 8:
message += "ACHT ";
break;
case 9:
message += "NEUN ";
break;
case 10:
message += "ZEHN ";
break;
case 11:
message += "ELF ";
break;
case 12:
message += "ZWOLF ";
break;
}
if ((minutes < 5) && (hours != 0))
{

View File

@@ -23,19 +23,17 @@
#include <Arduino.h>
#include "wordclock_esp8266.h"
#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 <Adafruit_GFX.h> // https://github.com/adafruit/Adafruit-GFX-Library
#include <base64.hpp>
#include <EEPROM.h> // from ESP8266 Arduino Core (automatically installed when ESP8266 was installed via Boardmanager)
#include <ESP8266_ISR_Timer.h> // https://github.com/khoih-prog/ESP8266TimerInterrupt
#include <ESP8266TimerInterrupt.h> // https://github.com/khoih-prog/ESP8266TimerInterrupt
#include <EEPROM.h> // from ESP8266 Arduino Core (automatically installed when ESP8266 was installed via Boardmanager)
#include <ESP8266WebServer.h>
#include <ESP8266WiFi.h>
#include <FastLED.h>
#include <WiFiManager.h> // 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"
@@ -51,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
// ----------------------------------------------------------------------------------
@@ -71,20 +70,16 @@ static Pong pong = Pong(&led_matrix, &logger);
static Snake snake = Snake(&led_matrix, &logger);
static Tetris tetris = Tetris(&led_matrix, &logger);
// Time ManagerW
static TimeManager tm_mgr = TimeManager(MY_TZ, NTP_SERVER_URL,
NTP_UPDATE_PERIOD_S,
NTP_RETRY_DELAY_US,
NTP_MAX_OFFLINE_TIME_S,
&logger);
// Time Manager
static TimeManager tm_mgr;
// State variablesW
// 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 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 uint8_t current_state = (uint8_t)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)
@@ -104,19 +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};
// ----------------------------------------------------------------------------------
// STATIC VARIABLES
// ----------------------------------------------------------------------------------
ESP8266Timer ITimer; // ESP8266 Timer
// ----------------------------------------------------------------------------------
// ISR
// ----------------------------------------------------------------------------------
void IRAM_ATTR TimerHandler()
{
tm_mgr.increment_time_now_local();
}
// ----------------------------------------------------------------------------------
// SETUP
// ----------------------------------------------------------------------------------
@@ -136,6 +118,9 @@ void setup()
Serial.printf("Reset address: %u\n", reset_info->excvaddr);
Serial.println();
// Init FastLED
FastLED.addLeds<WS2812B, FASTLED_PIN, COLOR_ORDER>(leds, NUM_MATRIX);
// Init EEPROM
EEPROM.begin(EEPROM_SIZE);
@@ -153,7 +138,7 @@ void setup()
led_matrix.set_current_limit(CURRENT_LIMIT_LED);
// Turn on minutes LEDs (blue)
led_matrix.set_min_indicator(15, colors_24bit[6]);
led_matrix.set_min_indicator((uint8_t)0b1111, colors_24bit[6]);
led_matrix.draw_on_matrix_instant();
/* Use WiFiMaanger for handling initial Wifi setup */
@@ -172,7 +157,7 @@ void setup()
WiFi.persistent(true);
// Turn off minutes LEDs
led_matrix.set_min_indicator(15, 0);
led_matrix.set_min_indicator((uint8_t)0b1111, 0);
led_matrix.draw_on_matrix_instant();
// init ESP8266 File manager (LittleFS)
@@ -194,13 +179,16 @@ void setup()
cold_start_setup();
}
// get initial time
if (tm_mgr.ntp_time_update() == NTP_UPDATE_OK)
// set up time manager and get initial time
tm_mgr = TimeManager(MY_TZ, NTP_SERVER_URL, check_wifi_status, NTP_MAX_OFFLINE_TIME_S, &logger);
tm_mgr.init();
if (tm_mgr.get_time() == TIME_UPDATE_OK)
{
// show the current time for short time in words
String timeMessage = time_to_string(tm_mgr.tm_hour(), tm_mgr.tm_min());
String timeMessage = time_to_string(tm_mgr.hour(), tm_mgr.minute());
show_string_on_clock(timeMessage, main_color_clock);
draw_minute_indicator(tm_mgr.tm_min(), main_color_clock);
draw_minute_indicator(tm_mgr.minute(), main_color_clock);
led_matrix.draw_on_matrix_smooth(filter_factor);
}
else
@@ -209,11 +197,8 @@ void setup()
}
// init all animation modes
// init snake
random_snake(true, 8, colors_24bit[1], -1);
// init spiral
draw_spiral(true, spiral_direction, MATRIX_WIDTH - 6);
// init random tetris
random_tetris(true);
// Set range limits
@@ -252,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]) &&
@@ -260,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)
@@ -268,29 +251,26 @@ 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)
{
if (tm_mgr.ntp_time_update() == NTP_UPDATE_OK) // NTP time update
{
logger.log_string("NTP sync successful!");
}
(void)tm_mgr.get_time(); // NTP time update
if (tm_mgr.ntp_update_failed_prolonged() == true)
if (tm_mgr.ntp_sync_timeout())
{
logger.log_string("Trigger restart due to being offline for too long...");
delay(100);
ESP.restart();
}
last_time_update_us = system_get_time();
}
if ((current_time_us - last_nightmode_check_us) >= PERIOD_NIGHTMODE_CHECK_US)
@@ -368,22 +348,22 @@ void handle_current_state()
{
case ST_CLOCK: // state clock
{
if (tm_mgr.ntp_sync_successful() && 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.tm_hour(), (uint8_t)tm_mgr.tm_min()), main_color_clock);
draw_minute_indicator((uint8_t)tm_mgr.tm_min(), main_color_clock);
(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_successful() && tm_mgr.tm_state() == TM_RETRY_SYNC)
else if (tm_mgr.ntp_sync_overdue()) // if NTP sync is overdue
{
(void)show_string_on_clock(time_to_string((uint8_t)tm_mgr.tm_hour(), (uint8_t)tm_mgr.tm_min()), main_color_clock);
draw_minute_indicator((uint8_t)tm_mgr.tm_min(), colors_24bit[6]); // in blue to indicate a network problem
(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
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(15, colors_24bit[6]);
led_matrix.set_min_indicator((uint8_t)0b1111, colors_24bit[6]);
led_matrix.draw_on_matrix_instant();
}
break;
@@ -392,14 +372,14 @@ void handle_current_state()
{
if (tm_mgr.ntp_sync_successful())
{
show_digital_clock((uint8_t)tm_mgr.tm_hour(), (uint8_t)tm_mgr.tm_min(), main_color_clock);
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(15, colors_24bit[6]);
led_matrix.set_min_indicator((uint8_t)0b1111, colors_24bit[6]);
led_matrix.draw_on_matrix_instant();
}
break;
@@ -498,9 +478,9 @@ bool check_wifi_status()
*/
void check_night_mode()
{
// check if nightmode need to be activated
int hours = tm_mgr.tm_hour();
int minutes = tm_mgr.tm_min();
// Check if nightmode needs to be activated. This only toggles at the exact minute.
int hours = tm_mgr.hour();
int minutes = tm_mgr.minute();
if ((hours == night_mode_times_ps->start_hour) && (minutes == night_mode_times_ps->start_min))
{
@@ -554,21 +534,21 @@ void on_state_entry(uint8_t state)
}
/**
* @brief execute a state change to given newState
* @brief execute a state change to given new_state
*
* @param newState the new state to be changed to
* @param new_state the new state to be changed to
*/
void state_change(uint8_t newState)
void state_change(ClockState_en new_state)
{
if (flg_night_mode)
{
set_night_mode(false); // deactivate Nightmode
}
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]);
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]);
logger.log_string("FreeMemory=" + String(ESP.getFreeHeap()));
}
@@ -660,7 +640,7 @@ void handle_button()
}
else
{
state_change((current_state + 1) % (uint8_t)NUM_STATES);
state_change((ClockState_en)(((uint8_t)current_state + 1) % (uint8_t)NUM_STATES));
}
}
}
@@ -710,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
{
@@ -735,6 +718,7 @@ void handle_command()
{
String mode_str = webserver.arg(0);
logger.log_string("Mode change via Webserver to: " + mode_str);
// set current mode/state accordant sent mode
if (mode_str.equals("clock"))
{
@@ -816,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();
@@ -845,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();
@@ -870,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);
@@ -884,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
}
}
/**
@@ -894,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++)
{
@@ -901,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 = "{";
@@ -1049,9 +1041,9 @@ uint8_t update_brightness()
{
new_brightness = calculate_dynamic_brightness(brightness_ps->dyn_brightness_min,
brightness_ps->dyn_brightness_max,
tm_mgr.tm_hour(),
tm_mgr.tm_min(),
tm_mgr.tm_isdst());
tm_mgr.hour(),
tm_mgr.minute(),
tm_mgr.isdst());
}
else // use static brightness
{