Compare commits

...

7 Commits

14 changed files with 309 additions and 166 deletions

11
.gitignore vendored
View File

@@ -66,3 +66,14 @@ modules.order
Module.symvers
Mkfile.old
dkms.conf
# Python
venv
.venv
# Logs
*.log
# Local backup
*.*_
*.*bak*

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;
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};

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

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

View File

@@ -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
fastled/FastLED@3.7.0
marcmerlin/FastLED NeoMatrix@^1.2
tzapu/WiFiManager@^0.16.0
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,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

@@ -24,16 +24,16 @@
#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 <base64.hpp>
#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
// ----------------------------------------------------------------------------------
@@ -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)
@@ -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,9 +865,19 @@ 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;
}
if (send204)
{
webserver.send(204, "text/plain", "No Content"); // this page doesn't send back content --> 204
}
}
/**
* @brief Handler for GET requests
@@ -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 = "{";