Compare commits
10 Commits
0cf2e5ca90
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 1629f24fcc | |||
| adb3677d71 | |||
| d6f06525c9 | |||
| 4d810783fc | |||
| feb26d7269 | |||
| 3dfd6c82fe | |||
| 244455909d | |||
| f53f557a6a | |||
| e1df24633a | |||
| 4543cf1e09 |
13
.gitignore
vendored
13
.gitignore
vendored
@@ -65,4 +65,15 @@ log.txt
|
||||
modules.order
|
||||
Module.symvers
|
||||
Mkfile.old
|
||||
dkms.conf
|
||||
dkms.conf
|
||||
|
||||
# Python
|
||||
venv
|
||||
.venv
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
|
||||
# Local backup
|
||||
*.*_
|
||||
*.*bak*
|
||||
|
||||
99
README.md
99
README.md
@@ -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
|
||||

|
||||
|
||||
## Screenshots of webserver UI
|
||||

|
||||
|
||||
## 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
26
include/diagnosis.h
Normal 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
|
||||
@@ -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 */
|
||||
@@ -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};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
#define HTTP_PORT (80) // Standard HTTP port
|
||||
|
||||
// ESP8266 Pins
|
||||
#define NEOPIXEL_PIN (14) // pin to which the NeoPixels are attached
|
||||
#define FASTLED_PIN (14) // pin to which the LEDs are attached
|
||||
#define BUTTON_PIN (5) // pin to which the button is attached
|
||||
|
||||
// Time limits
|
||||
@@ -36,8 +36,8 @@
|
||||
#define PERIOD_ANIMATION_US (200 * 1000) // 200ms
|
||||
#define PERIOD_CLOCK_UPDATE_US (1 * 1000 * 1000) // Must be 1s! Do not change!
|
||||
#define PERIOD_HEARTBEAT_US (1 * 1000 * 1000) // 1s
|
||||
#define PERIOD_MATRIX_UPDATE_US (100 * 1000) // 100ms
|
||||
#define PERIOD_NIGHTMODE_CHECK_US (20 * 1000 * 1000) // 20s
|
||||
#define PERIOD_MATRIX_UPDATE_US (33 * 1000) // 33ms
|
||||
#define PERIOD_NIGHTMODE_CHECK_US (30 * 1000 * 1000) // 30s
|
||||
#define PERIOD_TIME_UPDATE_US (1 * 1000 * 1000) // 1000ms
|
||||
#define PERIOD_PONG_US (10 * 1000) // 10ms
|
||||
#define PERIOD_SNAKE_US (50 * 1000) // 50ms
|
||||
@@ -63,10 +63,12 @@
|
||||
|
||||
// Number of colors in colors array
|
||||
#define NUM_COLORS (7)
|
||||
#define COLOR_ORDER GRB // WS2812B color order
|
||||
|
||||
// LED matrix size
|
||||
#define MATRIX_WIDTH (11)
|
||||
#define MATRIX_HEIGHT (11)
|
||||
#define NUM_MATRIX (MATRIX_WIDTH * (MATRIX_HEIGHT + 1))
|
||||
|
||||
// NTP macros
|
||||
#define BUILD_YEAR (__DATE__ + 7) // Will expand to current year at compile time as string.
|
||||
|
||||
@@ -12,15 +12,16 @@
|
||||
default_envs = nodemcuv2
|
||||
|
||||
[env]
|
||||
platform = espressif8266
|
||||
platform = espressif8266@2.6.3
|
||||
board = nodemcuv2
|
||||
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
|
||||
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
13
scripts/http_diagnosis.py
Normal 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)
|
||||
@@ -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
BIN
scripts/requirements.txt
Normal file
Binary file not shown.
93
src/connectivity/diagnosis.cpp
Normal file
93
src/connectivity/diagnosis.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
*
|
||||
|
||||
@@ -23,17 +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 <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"
|
||||
@@ -49,10 +49,11 @@
|
||||
// GLOBAL VARIABLES
|
||||
// ----------------------------------------------------------------------------------
|
||||
UDPLogger logger; // Global UDP logger instance
|
||||
Adafruit_NeoMatrix matrix = Adafruit_NeoMatrix(MATRIX_WIDTH, MATRIX_HEIGHT + 1, NEOPIXEL_PIN,
|
||||
NEO_MATRIX_TOP + NEO_MATRIX_LEFT + NEO_MATRIX_ROWS + NEO_MATRIX_ZIGZAG,
|
||||
NEO_GRB + NEO_KHZ800); // NeoMatrix
|
||||
LEDMatrix led_matrix = LEDMatrix(&matrix, DEFAULT_BRIGHTNESS, &logger); // NeoMatrix wrapper
|
||||
|
||||
CRGB leds[NUM_MATRIX]; // LED array for FastLED
|
||||
FastLED_NeoMatrix matrix = FastLED_NeoMatrix(leds, MATRIX_WIDTH, (MATRIX_HEIGHT + 1),
|
||||
NEO_MATRIX_TOP + NEO_MATRIX_LEFT + NEO_MATRIX_ROWS + NEO_MATRIX_ZIGZAG);
|
||||
LEDMatrix led_matrix = LEDMatrix(&matrix, DEFAULT_BRIGHTNESS, &logger); // FastLED_NeoMatrix wrapper
|
||||
ESP8266WebServer webserver(HTTP_PORT); // Webserver
|
||||
|
||||
// ----------------------------------------------------------------------------------
|
||||
@@ -78,7 +79,7 @@ static bool flg_reset_wifi_creds = false; // Used to reset stored
|
||||
static float filter_factor = DEFAULT_SMOOTHING_FACTOR; // Stores smoothing factor for led transition, value of 1 represents no smoothing.
|
||||
static uint32_t main_color_clock = colors_24bit[2]; // Color of the clock and digital clock
|
||||
static uint8_t current_brightness = DEFAULT_BRIGHTNESS; // Current brightness of LEDs
|
||||
static ClockState_en current_state = ST_CLOCK; // Stores current state
|
||||
static ClockState_en current_state = ST_CLOCK; // Stores current state
|
||||
|
||||
// Other variables
|
||||
static uint32 last_led_direct_us = 0; // Time of last direct LED command (=> fall back to normal mode after timeout)
|
||||
@@ -98,7 +99,6 @@ static const uint32_t period_timings[NUM_STATES] = {PERIOD_TIME_UPDATE_US, PERIO
|
||||
PERIOD_ANIMATION_US, PERIOD_TETRIS_US, PERIOD_SNAKE_US,
|
||||
PERIOD_PONG_US, PERIOD_ANIMATION_US};
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------------
|
||||
// SETUP
|
||||
// ----------------------------------------------------------------------------------
|
||||
@@ -118,6 +118,9 @@ void setup()
|
||||
Serial.printf("Reset address: %u\n", reset_info->excvaddr);
|
||||
Serial.println();
|
||||
|
||||
// Init FastLED
|
||||
FastLED.addLeds<WS2812B, FASTLED_PIN, COLOR_ORDER>(leds, NUM_MATRIX);
|
||||
|
||||
// Init EEPROM
|
||||
EEPROM.begin(EEPROM_SIZE);
|
||||
|
||||
@@ -234,7 +237,6 @@ void loop()
|
||||
{
|
||||
send_heartbeat(); // send heartbeat update
|
||||
last_heartbeat_us = system_get_time();
|
||||
delay(10);
|
||||
}
|
||||
|
||||
if (!flg_night_mode && ((current_time_us - last_animation_step_us) > period_timings[current_state]) &&
|
||||
@@ -242,7 +244,6 @@ void loop()
|
||||
{
|
||||
handle_current_state(); // handle current state
|
||||
last_animation_step_us = system_get_time();
|
||||
delay(10);
|
||||
}
|
||||
|
||||
if ((current_time_us - last_brightness_update_us) >= PERIOD_BRIGHTNESS_UPDATE_US)
|
||||
@@ -250,14 +251,12 @@ void loop()
|
||||
current_brightness = update_brightness(); // update brightness
|
||||
logger.log_string("Brightness: " + String(((uint16_t)current_brightness * 100) / UINT8_MAX) + "%");
|
||||
last_brightness_update_us = system_get_time();
|
||||
delay(10);
|
||||
}
|
||||
|
||||
if ((current_time_us - last_matrix_update_us) >= PERIOD_MATRIX_UPDATE_US)
|
||||
{
|
||||
update_matrix(); // update matrix
|
||||
last_matrix_update_us = system_get_time();
|
||||
delay(10);
|
||||
}
|
||||
|
||||
if ((current_time_us - last_time_update_us) >= PERIOD_TIME_UPDATE_US)
|
||||
@@ -271,7 +270,7 @@ void loop()
|
||||
ESP.restart();
|
||||
}
|
||||
|
||||
last_time_update_us = system_get_time();
|
||||
last_time_update_us = system_get_time();
|
||||
}
|
||||
|
||||
if ((current_time_us - last_nightmode_check_us) >= PERIOD_NIGHTMODE_CHECK_US)
|
||||
@@ -347,87 +346,87 @@ void handle_current_state()
|
||||
{
|
||||
switch (current_state)
|
||||
{
|
||||
case ST_CLOCK: // state clock
|
||||
case ST_CLOCK: // state clock
|
||||
{
|
||||
if (tm_mgr.tm_state() == TM_NORMAL)
|
||||
{
|
||||
if (tm_mgr.tm_state() == TM_NORMAL)
|
||||
{
|
||||
(void)show_string_on_clock(time_to_string((uint8_t)tm_mgr.hour(), (uint8_t)tm_mgr.minute()), main_color_clock);
|
||||
draw_minute_indicator((uint8_t)tm_mgr.minute(), main_color_clock);
|
||||
}
|
||||
else if (tm_mgr.ntp_sync_overdue()) // if NTP sync is overdue
|
||||
{
|
||||
(void)show_string_on_clock(time_to_string((uint8_t)tm_mgr.hour(), (uint8_t)tm_mgr.minute()), main_color_clock);
|
||||
draw_minute_indicator((uint8_t)tm_mgr.minute(), colors_24bit[6]); // in blue to indicate a network problem
|
||||
}
|
||||
else // if no NTP sync has been done, only show 4 blue minute indicators
|
||||
{
|
||||
// clear matrix
|
||||
led_matrix.flush();
|
||||
// Turn on minutes LEDs (blue)
|
||||
led_matrix.set_min_indicator((uint8_t)0b1111, colors_24bit[6]);
|
||||
led_matrix.draw_on_matrix_instant();
|
||||
}
|
||||
break;
|
||||
(void)show_string_on_clock(time_to_string((uint8_t)tm_mgr.hour(), (uint8_t)tm_mgr.minute()), main_color_clock);
|
||||
draw_minute_indicator((uint8_t)tm_mgr.minute(), main_color_clock);
|
||||
}
|
||||
case ST_DICLOCK: // state diclock
|
||||
else if (tm_mgr.ntp_sync_overdue()) // if NTP sync is overdue
|
||||
{
|
||||
if (tm_mgr.ntp_sync_successful())
|
||||
{
|
||||
show_digital_clock((uint8_t)tm_mgr.hour(), (uint8_t)tm_mgr.minute(), main_color_clock);
|
||||
}
|
||||
else
|
||||
{
|
||||
// clear matrix
|
||||
led_matrix.flush();
|
||||
// Turn on minutes LEDs (blue)
|
||||
led_matrix.set_min_indicator((uint8_t)0b1111, colors_24bit[6]);
|
||||
led_matrix.draw_on_matrix_instant();
|
||||
}
|
||||
break;
|
||||
(void)show_string_on_clock(time_to_string((uint8_t)tm_mgr.hour(), (uint8_t)tm_mgr.minute()), main_color_clock);
|
||||
draw_minute_indicator((uint8_t)tm_mgr.minute(), colors_24bit[6]); // in blue to indicate a network problem
|
||||
}
|
||||
case ST_SPIRAL: // state spiral
|
||||
else // if no NTP sync has been done, only show 4 blue minute indicators
|
||||
{
|
||||
int res = draw_spiral(false, spiral_direction, MATRIX_WIDTH - 2);
|
||||
if ((bool)res && spiral_direction == 0)
|
||||
{
|
||||
// change spiral direction to closing (draw empty LEDs)
|
||||
spiral_direction = true;
|
||||
// init spiral with new spiral direction
|
||||
draw_spiral(true, spiral_direction, MATRIX_WIDTH - 1);
|
||||
}
|
||||
else if (res && spiral_direction == 1)
|
||||
{
|
||||
// reset spiral direction to normal drawing LEDs
|
||||
spiral_direction = false;
|
||||
// init spiral with new spiral direction
|
||||
draw_spiral(true, spiral_direction, MATRIX_WIDTH - 1);
|
||||
}
|
||||
break;
|
||||
// clear matrix
|
||||
led_matrix.flush();
|
||||
// Turn on minutes LEDs (blue)
|
||||
led_matrix.set_min_indicator((uint8_t)0b1111, colors_24bit[6]);
|
||||
led_matrix.draw_on_matrix_instant();
|
||||
}
|
||||
case ST_TETRIS: // state tetris
|
||||
break;
|
||||
}
|
||||
case ST_DICLOCK: // state diclock
|
||||
{
|
||||
if (tm_mgr.ntp_sync_successful())
|
||||
{
|
||||
tetris.loopCycle();
|
||||
break;
|
||||
show_digital_clock((uint8_t)tm_mgr.hour(), (uint8_t)tm_mgr.minute(), main_color_clock);
|
||||
}
|
||||
case ST_SNAKE: // state snake
|
||||
else
|
||||
{
|
||||
snake.loopCycle();
|
||||
break;
|
||||
// clear matrix
|
||||
led_matrix.flush();
|
||||
// Turn on minutes LEDs (blue)
|
||||
led_matrix.set_min_indicator((uint8_t)0b1111, colors_24bit[6]);
|
||||
led_matrix.draw_on_matrix_instant();
|
||||
}
|
||||
case ST_PINGPONG: // state ping pong
|
||||
break;
|
||||
}
|
||||
case ST_SPIRAL: // state spiral
|
||||
{
|
||||
int res = draw_spiral(false, spiral_direction, MATRIX_WIDTH - 2);
|
||||
if ((bool)res && spiral_direction == 0)
|
||||
{
|
||||
pong.loopCycle();
|
||||
break;
|
||||
// change spiral direction to closing (draw empty LEDs)
|
||||
spiral_direction = true;
|
||||
// init spiral with new spiral direction
|
||||
draw_spiral(true, spiral_direction, MATRIX_WIDTH - 1);
|
||||
}
|
||||
case ST_HEARTS:
|
||||
else if (res && spiral_direction == 1)
|
||||
{
|
||||
draw_heart_animation();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
break;
|
||||
// reset spiral direction to normal drawing LEDs
|
||||
spiral_direction = false;
|
||||
// init spiral with new spiral direction
|
||||
draw_spiral(true, spiral_direction, MATRIX_WIDTH - 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ST_TETRIS: // state tetris
|
||||
{
|
||||
tetris.loopCycle();
|
||||
break;
|
||||
}
|
||||
case ST_SNAKE: // state snake
|
||||
{
|
||||
snake.loopCycle();
|
||||
break;
|
||||
}
|
||||
case ST_PINGPONG: // state ping pong
|
||||
{
|
||||
pong.loopCycle();
|
||||
break;
|
||||
}
|
||||
case ST_HEARTS:
|
||||
{
|
||||
draw_heart_animation();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -503,34 +502,34 @@ void on_state_entry(uint8_t state)
|
||||
filter_factor = DEFAULT_SMOOTHING_FACTOR;
|
||||
switch (state)
|
||||
{
|
||||
case ST_SPIRAL:
|
||||
{
|
||||
spiral_direction = 0; // Init spiral with normal drawing mode
|
||||
draw_spiral(true, spiral_direction, MATRIX_WIDTH - 1);
|
||||
break;
|
||||
}
|
||||
case ST_TETRIS:
|
||||
{
|
||||
filter_factor = 1.0f; // no smoothing
|
||||
tetris.ctrlStart();
|
||||
break;
|
||||
}
|
||||
case ST_SNAKE:
|
||||
{
|
||||
filter_factor = 1.0f; // no smoothing
|
||||
snake.initGame();
|
||||
break;
|
||||
}
|
||||
case ST_PINGPONG:
|
||||
{
|
||||
filter_factor = 1.0f; // no smoothing
|
||||
pong.initGame(1);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
break;
|
||||
}
|
||||
case ST_SPIRAL:
|
||||
{
|
||||
spiral_direction = 0; // Init spiral with normal drawing mode
|
||||
draw_spiral(true, spiral_direction, MATRIX_WIDTH - 1);
|
||||
break;
|
||||
}
|
||||
case ST_TETRIS:
|
||||
{
|
||||
filter_factor = 1.0f; // no smoothing
|
||||
tetris.ctrlStart();
|
||||
break;
|
||||
}
|
||||
case ST_SNAKE:
|
||||
{
|
||||
filter_factor = 1.0f; // no smoothing
|
||||
snake.initGame();
|
||||
break;
|
||||
}
|
||||
case ST_PINGPONG:
|
||||
{
|
||||
filter_factor = 1.0f; // no smoothing
|
||||
pong.initGame(1);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -546,7 +545,7 @@ void state_change(ClockState_en new_state)
|
||||
set_night_mode(false); // deactivate Nightmode
|
||||
}
|
||||
|
||||
led_matrix.flush(); // first clear matrix
|
||||
led_matrix.flush(); // first clear matrix
|
||||
current_state = new_state; // set new state
|
||||
on_state_entry((uint8_t)current_state);
|
||||
logger.log_string("State change to: " + state_names[(uint8_t)current_state]);
|
||||
@@ -691,13 +690,16 @@ void draw_main_color()
|
||||
*/
|
||||
void handle_command()
|
||||
{
|
||||
bool send204 = true; // flag to send 204 response
|
||||
// receive command and handle accordingly
|
||||
#ifdef SERIAL_DEBUG
|
||||
for (uint8_t i = 0; i < webserver.args(); i++)
|
||||
{
|
||||
Serial.print(webserver.argName(i));
|
||||
Serial.print(F(": "));
|
||||
Serial.println(webserver.arg(i));
|
||||
}
|
||||
#endif /* SERIAL_DEBUG */
|
||||
|
||||
if (webserver.argName(0).equals("led")) // the parameter which was sent to this server is led color
|
||||
{
|
||||
@@ -798,7 +800,6 @@ void handle_command()
|
||||
else if (webserver.argName(0).equals("tetris"))
|
||||
{
|
||||
String cmd_str = webserver.arg(0);
|
||||
// logger.log_string("Tetris cmd via Webserver to: " + cmd_str);
|
||||
if (cmd_str.equals("up"))
|
||||
{
|
||||
tetris.ctrlUp();
|
||||
@@ -827,7 +828,6 @@ void handle_command()
|
||||
else if (webserver.argName(0).equals("snake"))
|
||||
{
|
||||
String cmd_str = webserver.arg(0);
|
||||
// logger.log_string("Snake cmd via Webserver to: " + cmd_str);
|
||||
if (cmd_str.equals("up"))
|
||||
{
|
||||
snake.ctrlUp();
|
||||
@@ -852,7 +852,6 @@ void handle_command()
|
||||
else if (webserver.argName(0).equals("pong"))
|
||||
{
|
||||
String cmd_str = webserver.arg(0);
|
||||
// logger.log_string("Pong cmd via Webserver to: " + cmd_str);
|
||||
if (cmd_str.equals("up"))
|
||||
{
|
||||
pong.ctrlUp(1);
|
||||
@@ -866,8 +865,18 @@ void handle_command()
|
||||
pong.initGame(1);
|
||||
}
|
||||
}
|
||||
else if (webserver.argName(0).equals("diag"))
|
||||
{
|
||||
String cmd_str = webserver.arg(0);
|
||||
Diagnosis diag(&logger, &led_matrix);
|
||||
webserver.send(200, "text/plain", diag.handle_command(cmd_str));
|
||||
send204 = false;
|
||||
}
|
||||
|
||||
webserver.send(204, "text/plain", "No Content"); // this page doesn't send back content --> 204
|
||||
if (send204)
|
||||
{
|
||||
webserver.send(204, "text/plain", "No Content"); // this page doesn't send back content --> 204
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -876,6 +885,7 @@ void handle_command()
|
||||
*/
|
||||
void handle_data_request()
|
||||
{
|
||||
#ifdef SERIAL_DEBUG
|
||||
// receive data request and handle accordingly
|
||||
for (uint8_t i = 0; i < webserver.args(); i++)
|
||||
{
|
||||
@@ -883,7 +893,7 @@ void handle_data_request()
|
||||
Serial.print(F(": "));
|
||||
Serial.println(webserver.arg(i));
|
||||
}
|
||||
|
||||
#endif /* SERIAL_DEBUG */
|
||||
if (webserver.argName(0).equals("key"))
|
||||
{
|
||||
String message = "{";
|
||||
|
||||
Reference in New Issue
Block a user