Refactoring for stability, etc. Add new animation.

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

View File

@@ -2,6 +2,7 @@
#define ANIMATIONFUNCTIONS_H
#include <Arduino.h>
#include "wordclock_constants.h"
enum Direction
{
@@ -14,7 +15,737 @@ enum Direction
Direction next_direction(Direction dir, int d);
int random_snake(bool init, const uint8_t len, const uint32_t color, int numSteps);
int random_tetris(bool init);
int draw_heart_animation(void);
int draw_spiral(bool init, bool empty, uint8_t size);
void show_digital_clock(uint8_t hours, uint8_t minutes, uint32_t color);
#endif /* ANIMATIONFUNCTIONS_H */
#define HEART_ANIMATION_FRAMES 5
const uint32_t heart_frames_colormap_11x11[HEART_ANIMATION_FRAMES][MATRIX_WIDTH][MATRIX_HEIGHT] =
{
{
{
0x005b000a,
0x002d0304,
0x000f0002,
0x00090001,
0x00350306,
0x00530103,
0x00310407,
0x000a0104,
0x00090001,
0x00300306,
0x00570006,
},
{
0x00330206,
0x00090000,
0x00ce0404,
0x00d70300,
0x000f0008,
0x00350306,
0x000b0100,
0x00c70600,
0x00d40201,
0x00080202,
0x00390006,
},
{
0x000a0000,
0x00db0503,
0x00940906,
0x00950a03,
0x00e20100,
0x000e0008,
0x00d00000,
0x00960806,
0x00940605,
0x00d50600,
0x000d0103,
},
{
0x00da010c,
0x00940700,
0x00580000,
0x00570104,
0x00940a0a,
0x00d40100,
0x00940a08,
0x005a0006,
0x005b0005,
0x009d0305,
0x00da0300,
},
{
0x00d70707,
0x00910a06,
0x00560202,
0x00570207,
0x00580007,
0x00900a09,
0x00540109,
0x005a0004,
0x005b0007,
0x00910a04,
0x00d40201,
},
{
0x00da0207,
0x0092070c,
0x005b0002,
0x00330005,
0x00300405,
0x00580308,
0x002e0503,
0x002c0404,
0x005a0004,
0x00970609,
0x00d80202,
},
{
0x00040402,
0x00dc0600,
0x008f0a0b,
0x00590006,
0x00310504,
0x00320406,
0x00330507,
0x00620008,
0x00960709,
0x00dd0301,
0x000d0304,
},
{
0x00330205,
0x00090100,
0x00d8020a,
0x00930906,
0x00570209,
0x00330205,
0x00580007,
0x00980808,
0x00d60200,
0x000d0003,
0x00340205,
},
{
0x00560008,
0x00340306,
0x00100001,
0x00d40a00,
0x009e0507,
0x005c0007,
0x009e0508,
0x00e50000,
0x000e0001,
0x00310305,
0x00590004,
},
{
0x00a00606,
0x00580007,
0x00350304,
0x00100001,
0x00d40404,
0x00940902,
0x00d50407,
0x00000205,
0x00350304,
0x00560108,
0x00900906,
},
{
0x00d90104,
0x008d080b,
0x0060000b,
0x00340106,
0x00050100,
0x00e00106,
0x000f0000,
0x00370003,
0x00570104,
0x00950a07,
0x00e10007,
},
},
{
{
0x00310304,
0x000b0001,
0x00da0308,
0x00d90101,
0x000b0001,
0x00310304,
0x000b0001,
0x00d60101,
0x00d60101,
0x000b0001,
0x00310304,
},
{
0x000b0001,
0x00d90101,
0x00940808,
0x00940808,
0x00d60101,
0x000b0001,
0x00d60101,
0x00940808,
0x00940808,
0x00d60101,
0x000b0001,
},
{
0x00d90101,
0x00940808,
0x00590005,
0x00590005,
0x00940808,
0x00d90101,
0x00940808,
0x00560004,
0x00590005,
0x00940808,
0x00d60101,
},
{
0x00940808,
0x00590005,
0x00370803,
0x00310304,
0x00590005,
0x00940808,
0x00590005,
0x00310304,
0x00310304,
0x00590005,
0x00940808,
},
{
0x00940808,
0x00590005,
0x002c0303,
0x00310304,
0x00310304,
0x00560004,
0x00310304,
0x00310304,
0x00310304,
0x00590005,
0x00940808,
},
{
0x00940808,
0x00590005,
0x00310304,
0x000b0001,
0x000b0001,
0x00310304,
0x000b0001,
0x00130102,
0x00310304,
0x00590005,
0x00940808,
},
{
0x00d60101,
0x00940808,
0x00590005,
0x00310304,
0x000b0001,
0x000b0001,
0x000b0001,
0x00310304,
0x00590005,
0x00940808,
0x00d90101,
},
{
0x000b0001,
0x00d90802,
0x00940808,
0x00590005,
0x00310304,
0x000b0001,
0x00310304,
0x00560004,
0x00940808,
0x00d90101,
0x000b0001,
},
{
0x00310304,
0x000b0001,
0x00d90101,
0x00940808,
0x00590005,
0x00310304,
0x00590005,
0x00940808,
0x00d90802,
0x000b0001,
0x00310304,
},
{
0x00590005,
0x00310304,
0x000b0001,
0x00d90101,
0x00940808,
0x00560004,
0x00940808,
0x00d60101,
0x000b0001,
0x00310304,
0x00560004,
},
{
0x00940808,
0x00590005,
0x00310304,
0x000b0001,
0x00d60101,
0x00940808,
0x00d60101,
0x000b0001,
0x00310304,
0x00590005,
0x00940808,
},
},
{
{
0x000b0001,
0x00d90101,
0x00940808,
0x00940808,
0x00d90101,
0x000b0001,
0x00d90101,
0x00940808,
0x00940808,
0x00d90101,
0x000b0001,
},
{
0x00d90101,
0x00940808,
0x00590005,
0x00590005,
0x00940808,
0x00d90101,
0x00940808,
0x00560004,
0x00560004,
0x00940808,
0x00d90101,
},
{
0x00940808,
0x00590005,
0x002d0303,
0x00320508,
0x00590005,
0x00940808,
0x00590005,
0x002d0303,
0x00310304,
0x00560004,
0x00940808,
},
{
0x00560004,
0x00320508,
0x000b0001,
0x000b0001,
0x00310304,
0x00590005,
0x002f0308,
0x000b0001,
0x000b0001,
0x002d0303,
0x00590005,
},
{
0x00560004,
0x002d0303,
0x000b0001,
0x000b0001,
0x000b0001,
0x00320508,
0x000b0001,
0x000b0001,
0x000b0001,
0x002d0303,
0x00590005,
},
{
0x00590005,
0x00310304,
0x000b0001,
0x00d40809,
0x00d90101,
0x000b0001,
0x00d90101,
0x00d90101,
0x000b0001,
0x003b0203,
0x00590005,
},
{
0x00940808,
0x00590005,
0x002d0303,
0x000b0001,
0x00d90101,
0x00d40809,
0x00d90101,
0x000b0001,
0x002d0303,
0x00590005,
0x00940808,
},
{
0x00d40809,
0x00940808,
0x00590005,
0x00310304,
0x000b0001,
0x00d90101,
0x000b0001,
0x00320508,
0x00590005,
0x00940808,
0x00d90101,
},
{
0x000b0001,
0x00d60101,
0x00940808,
0x005a0209,
0x002d0303,
0x000b0001,
0x00310304,
0x00560004,
0x00940808,
0x00d90101,
0x000b0001,
},
{
0x00310304,
0x000b0001,
0x00d90101,
0x00940808,
0x00590005,
0x00310304,
0x00590005,
0x00940808,
0x00d60101,
0x000b0001,
0x00310304,
},
{
0x00560004,
0x00310304,
0x000b0001,
0x00d90101,
0x00940808,
0x00590005,
0x00940808,
0x00d60101,
0x000b0001,
0x00310304,
0x00590005,
},
},
{
{
0x00d80802,
0x00940808,
0x00590004,
0x00590004,
0x00940808,
0x00d90101,
0x00940808,
0x00590004,
0x00590004,
0x00940808,
0x00d90101,
},
{
0x00940808,
0x00560004,
0x00310304,
0x00310304,
0x00560004,
0x00940808,
0x00590004,
0x002a0309,
0x00310304,
0x00590004,
0x00940808,
},
{
0x00590004,
0x00310304,
0x000b0001,
0x000b0001,
0x00310304,
0x00590004,
0x00310304,
0x000b0001,
0x000b0001,
0x00310304,
0x00560004,
},
{
0x00350408,
0x000b0001,
0x00d80802,
0x00d60101,
0x000b0001,
0x00310304,
0x000b0001,
0x00d60101,
0x00d60101,
0x000b0001,
0x00310304,
},
{
0x002c0303,
0x00130102,
0x00d60101,
0x00d60101,
0x00d80802,
0x000b0001,
0x00d90101,
0x00d60101,
0x00d80802,
0x000b0001,
0x00310304,
},
{
0x00310304,
0x000b0001,
0x00d90101,
0x00940808,
0x00940808,
0x00db0209,
0x00940808,
0x00940808,
0x00d60101,
0x000b0001,
0x00310304,
},
{
0x00590004,
0x00310304,
0x000b0001,
0x00d80802,
0x00940808,
0x00940808,
0x00940808,
0x00d90101,
0x000b0001,
0x003b0303,
0x00560004,
},
{
0x00940808,
0x00590004,
0x00350408,
0x000b0001,
0x00d90101,
0x00940808,
0x00d90101,
0x000b0001,
0x00310304,
0x00590004,
0x00940808,
},
{
0x00d90101,
0x00940808,
0x00590004,
0x002e0804,
0x00130102,
0x00d60101,
0x000b0001,
0x00310304,
0x00590004,
0x00940808,
0x00d90101,
},
{
0x000b0001,
0x00e10102,
0x00940808,
0x00590004,
0x002c0303,
0x000b0001,
0x003b0303,
0x00590004,
0x00940808,
0x00d90101,
0x000b0001,
},
{
0x00310304,
0x000b0001,
0x00d80802,
0x00940808,
0x00560004,
0x00310304,
0x00590004,
0x00940808,
0x00d90101,
0x000b0001,
0x00310304,
},
},
{
{
0x00940808,
0x00590005,
0x00310304,
0x00310304,
0x00560004,
0x00940808,
0x00590005,
0x002c0303,
0x00310304,
0x00560004,
0x00940808,
},
{
0x00560004,
0x00310304,
0x000b0001,
0x000b0001,
0x00310304,
0x00590005,
0x00310304,
0x000b0001,
0x000b0001,
0x00310304,
0x00590005,
},
{
0x00310304,
0x000b0001,
0x00d90101,
0x00d90101,
0x000b0001,
0x002c0303,
0x000b0001,
0x00d90101,
0x00cf0804,
0x000b0001,
0x00310304,
},
{
0x000b0001,
0x00d60101,
0x00940808,
0x00940808,
0x00e10102,
0x000b0001,
0x00d90101,
0x00940808,
0x00940808,
0x00d90101,
0x000b0001,
},
{
0x000b0001,
0x00d90101,
0x00940808,
0x00940808,
0x00940808,
0x00d90101,
0x00940808,
0x00940808,
0x00940808,
0x00cf0804,
0x000b0001,
},
{
0x000b0001,
0x00da0308,
0x00940808,
0x00590005,
0x00590005,
0x00940808,
0x00590005,
0x00590005,
0x00940808,
0x00d90101,
0x000b0001,
},
{
0x00310304,
0x000b0001,
0x00d90101,
0x00940808,
0x00590005,
0x00590005,
0x00560004,
0x00940808,
0x00d90101,
0x000b0001,
0x00310304,
},
{
0x005a0109,
0x00310304,
0x000b0001,
0x00d90101,
0x00940808,
0x00560004,
0x00940808,
0x00cf0804,
0x000b0001,
0x00310304,
0x00590005,
},
{
0x00940808,
0x00590005,
0x00310304,
0x000b0001,
0x00e10102,
0x00940808,
0x00d60101,
0x000b0001,
0x00310304,
0x00590005,
0x00940808,
},
{
0x00d90101,
0x00940808,
0x00590005,
0x00310304,
0x000b0001,
0x00d90101,
0x00130102,
0x00310304,
0x00590005,
0x00940808,
0x00e10102,
},
{
0x000b0001,
0x00d90101,
0x00940808,
0x00590005,
0x00310304,
0x000b0001,
0x002c0303,
0x00560004,
0x00940808,
0x00d90101,
0x00130102,
},
}};
#endif /* ANIMATIONFUNCTIONS_H */

View File

@@ -44,10 +44,10 @@ private:
uint32_t _current_grid[MATRIX_HEIGHT][MATRIX_WIDTH] = {0};
// target representation of minutes indicator leds
uint32_t _target_indicators[4] = {0, 0, 0, 0};
uint32_t _target_minute_indicators[4] = {0, 0, 0, 0};
// current representation of minutes indicator leds
uint32_t _current_indicators[4] = {0, 0, 0, 0};
uint32_t _current_minute_indicators[4] = {0, 0, 0, 0};
void _draw_on_matrix(float factor);
uint16_t _calc_estimated_led_current(uint32_t color);

View File

@@ -65,7 +65,5 @@ typedef enum
#define MATRIX_WIDTH 11
#define MATRIX_HEIGHT 11
// State machine states count
#define NUM_STATES 6
#endif /* WORDCLOCK_CONSTANTS_H */

View File

@@ -18,15 +18,19 @@ typedef struct
int nightmode_end_min;
} NightModeTimes_st;
typedef enum
typedef enum
{
ST_CLOCK,
ST_DICLOCK,
ST_SPIRAL,
ST_TETRIS,
ST_SNAKE,
ST_PINGPONG
} clock_state_en;
ST_PINGPONG,
ST_HEARTS,
NUM_STATES
} ClockState_en;
#define CURRENT_TIME_MS (system_get_time() / 1000) // use ESP time interface
int EEPROM_read_address(int address);
String leading_zero2digit(int value);
@@ -38,7 +42,7 @@ void handle_current_state(void);
void handle_data_request(void);
void handle_led_direct(void);
void load_main_color(void);
void ntp_time_update(void);
void ntp_time_update(unsigned long *last_ntp_update);
void on_state_entry(uint8_t state);
void send_heartbeat(void);
void set_main_color(uint8_t red, uint8_t green, uint8_t blue);

View File

@@ -1,80 +1,83 @@
<!DOCTYPE HTML> <!-- For more information visit: https://fipsok.de -->
<html lang="de">
<head>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="style.css">
<title>Filesystem Manager</title>
<script>
document.addEventListener('DOMContentLoaded', () => {
list(JSON.parse(localStorage.getItem('sortBy')));
btn.addEventListener('click', () => {
if (!confirm(`Wirklich formatieren? Alle Daten gehen verloren.\nDu musst anschließend fs.html wieder laden.`)) event.preventDefault();
});
});
function list(to){
let myList = document.querySelector('main'), noted = '';
fetch(`?sort=${to}`).then( (response) => {
return response.json();
}).then((json) => {
myList.innerHTML = '<nav><input type="radio" id="/" name="group" checked="checked"><label for="/"> &#128193;</label><span id="cr">+&#128193;</nav></span><span id="si"></span>';
document.querySelector('form').setAttribute('action', '/upload?f=');
for (var i = 0; i < json.length - 1; i++) {
let dir = '', f = json[i].folder, n = json[i].name;
if (f != noted) {
noted = f;
dir = `<nav><input type="radio" id="${f}" name="group"><label for="${f}"></label> &#128193; ${f} <a href="?delete=/${f}">&#x1f5d1;&#xfe0f;</a></nav>`;
}
if (n != '') dir += `<li><a href="${f}/${n}">${n}</a><small> ${json[i].size}</small><a href="${f}/${n}"download="${n}"> Download</a> or<a href="?delete=${f}/${n}"> Delete</a>`;
myList.insertAdjacentHTML('beforeend', dir);
}
myList.insertAdjacentHTML('beforeend', `<li><b id="so">${to ? '&#9660;' : '&#9650;'} LittleFS</b> belegt ${json[i].usedBytes.replace(".00", "")} von ${json[i].totalBytes.replace(".00", "")}`);
var free = json[i].freeBytes;
cr.addEventListener('click', () => {
document.getElementById('no').classList.toggle('no');
});
so.addEventListener('click', () => {
list(to=++to%2);
localStorage.setItem('sortBy', JSON.stringify(to));
});
document.addEventListener('change', (e) => {
if (e.target.id == 'fs') {
for (var bytes = 0, i = 0; i < event.target.files.length; i++) bytes += event.target.files[i].size;
for (var output = `${bytes} Byte`, i = 0, circa = bytes / 1024; circa > 1; circa /= 1024) output = circa.toFixed(2) + [' KB', ' MB', ' GB'][i++];
if (bytes > free) {
si.innerHTML = `<li><b> ${output}</b><strong> Ungenügend Speicher frei</strong></li>`;
up.setAttribute('disabled', 'disabled');
}
else {
si.innerHTML = `<li><b>Dateigröße:</b> ${output}</li>`;
up.removeAttribute('disabled');
}
}
document.querySelectorAll(`input[type=radio]`).forEach(el => { if (el.checked) document.querySelector('form').setAttribute('action', '/upload?f=' + el.id)});
});
document.querySelectorAll('[href^="?delete=/"]').forEach(node => {
node.addEventListener('click', () => {
if (!confirm('Sicher!')) event.preventDefault();
document.addEventListener('DOMContentLoaded', () => {
list(JSON.parse(localStorage.getItem('sortBy')));
btn.addEventListener('click', () => {
if (!confirm(`Wirklich formatieren? Alle Daten gehen verloren.\nDu musst anschließend fs.html wieder laden.`)) event.preventDefault();
});
});
});
}
});
function list(to) {
let myList = document.querySelector('main'), noted = '';
fetch(`?sort=${to}`).then((response) => {
return response.json();
}).then((json) => {
myList.innerHTML = '<nav><input type="radio" id="/" name="group" checked="checked"><label for="/"> &#128193;</label><span id="cr">+&#128193;</nav></span><span id="si"></span>';
document.querySelector('form').setAttribute('action', '/upload?f=');
for (var i = 0; i < json.length - 1; i++) {
let dir = '', f = json[i].folder, n = json[i].name;
if (f != noted) {
noted = f;
dir = `<nav><input type="radio" id="${f}" name="group"><label for="${f}"></label> &#128193; ${f} <a href="?delete=/${f}">&#x1f5d1;&#xfe0f;</a></nav>`;
}
if (n != '') dir += `<li><a href="${f}/${n}">${n}</a><small> ${json[i].size}</small><a href="${f}/${n}"download="${n}"> Download</a> or<a href="?delete=${f}/${n}"> Delete</a>`;
myList.insertAdjacentHTML('beforeend', dir);
}
myList.insertAdjacentHTML('beforeend', `<li><b id="so">${to ? '&#9660;' : '&#9650;'} LittleFS</b> belegt ${json[i].usedBytes.replace(".00", "")} von ${json[i].totalBytes.replace(".00", "")}`);
var free = json[i].freeBytes;
cr.addEventListener('click', () => {
document.getElementById('no').classList.toggle('no');
});
so.addEventListener('click', () => {
list(to = ++to % 2);
localStorage.setItem('sortBy', JSON.stringify(to));
});
document.addEventListener('change', (e) => {
if (e.target.id == 'fs') {
for (var bytes = 0, i = 0; i < event.target.files.length; i++) bytes += event.target.files[i].size;
for (var output = `${bytes} Byte`, i = 0, circa = bytes / 1024; circa > 1; circa /= 1024) output = circa.toFixed(2) + [' KB', ' MB', ' GB'][i++];
if (bytes > free) {
si.innerHTML = `<li><b> ${output}</b><strong> Ungenügend Speicher frei</strong></li>`;
up.setAttribute('disabled', 'disabled');
}
else {
si.innerHTML = `<li><b>Dateigröße:</b> ${output}</li>`;
up.removeAttribute('disabled');
}
}
document.querySelectorAll(`input[type=radio]`).forEach(el => { if (el.checked) document.querySelector('form').setAttribute('action', '/upload?f=' + el.id) });
});
document.querySelectorAll('[href^="?delete=/"]').forEach(node => {
node.addEventListener('click', () => {
if (!confirm('Sicher!')) event.preventDefault();
});
});
});
}
</script>
</head>
<body>
</head>
<body>
<h2>ESP8266 Filesystem Manager</h2>
<form method="post" enctype="multipart/form-data">
<input id="fs" type="file" name="up[]" multiple>
<button id="up" disabled>Upload</button>
<input id="fs" type="file" name="up[]" multiple>
<button id="up" disabled>Upload</button>
</form>
<form id="no" class="no" method="POST">
<input name="new" placeholder="Ordner Name" pattern="[^\x22/%&\\:;]{1,31}" title="Zeichen &#8220; % & / : ; \ sind nicht erlaubt." required="">
<button>Create</button>
<input name="new" placeholder="Ordner Name" pattern="[^\x22/%&\\:;]{1,31}"
title="Zeichen &#8220; % & / : ; \ sind nicht erlaubt." required="">
<button>Create</button>
</form>
<main></main>
<form action="/format" method="POST">
<button id="btn">Format LittleFS</button>
<button id="btn">Format LittleFS</button>
</form>
</body>
</html>
</body>
</html>

View File

@@ -0,0 +1,71 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="50mm"
height="50mm"
version="1.1"
viewBox="0 0 50 50"
id="svg4"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<defs
id="defs4" />
<metadata
id="metadata1">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<g
transform="translate(-60.99,-59.667)"
display="none"
id="g1">
<rect
x="-30.238001"
y="-2.0789001"
width="265.14999"
height="146.28"
fill="#1b2131"
fill-rule="evenodd"
opacity="0.998"
id="rect1" />
</g>
<g
transform="translate(-158.51 -1.487)"
stroke="#fff"
id="g2">
<rect
x="159.51"
y="2.487"
width="48"
height="48"
ry="6.8036"
fill="none"
opacity=".998"
stroke="#fff"
stroke-dashoffset="37.795"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
id="rect2" />
</g>
<g
transform="translate(-60.944598,-60.498371)"
fill="none"
stroke="#ffffff"
stroke-linejoin="round"
stroke-width="2"
id="g4">
<path
d="m 98.493049,73.311072 c -1.567368,-1.782803 -3.642384,-2.757209 -5.857002,-2.757209 -2.214619,0 -4.29598,0.981624 -5.863346,2.764427 l -0.818584,0.9311 -0.831275,-0.945536 c -1.567366,-1.782803 -3.655074,-2.771645 -5.869692,-2.771645 -2.208273,0 -4.289634,0.981625 -5.850654,2.757209 -1.567366,1.782803 -2.43037,4.15025 -2.424025,6.669271 0,2.519022 0.86935,4.87925 2.436716,6.662054 l 11.91706,13.555077 c 0.164986,0.18766 0.387082,0.28871 0.602834,0.28871 0.21575,0 0.437847,-0.0938 0.602832,-0.28149 l 11.94244,-13.533427 c 1.567367,-1.782803 2.430367,-4.150249 2.430367,-6.66927 0.006,-2.519021 -0.85031,-4.886468 -2.417671,-6.669271 z M 97.274689,85.271009 85.935081,98.118738 74.620853,85.249355 c -1.24374,-1.414693 -1.929067,-3.291328 -1.929067,-5.290666 0,-1.999337 0.678982,-3.875972 1.922721,-5.283447 1.237395,-1.407477 2.887254,-2.187003 4.638643,-2.187003 1.757735,0 3.413939,0.779526 4.657679,2.19422 l 1.43411,1.631228 c 0.336317,0.382545 0.875694,0.382545 1.212012,0 l 1.421416,-1.616792 c 1.24374,-1.414695 2.899945,-2.194219 4.651334,-2.194219 1.751392,0 3.401248,0.779524 4.644988,2.187001 1.24374,1.414694 1.922718,3.291328 1.922718,5.290666 0.0064,1.999337 -0.678978,3.875972 -1.922718,5.290666 z"
id="path1"
style="fill:#ffffff;stroke-width:0.067677" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,3 @@
/* For more information visit:https://fipsok.de */
body {
font-family: sans-serif;
@@ -7,72 +6,89 @@ body {
flex-flow: column;
align-items: center;
}
h1,h2 {
h1,
h2 {
color: #e1e1e1;
text-shadow: 2px 2px 2px black;
}
li {
background-color: #feb1e2;
list-style-type: none;
margin-bottom: 10px;
padding: 2px 5px 1px 0;
box-shadow: 5px 5px 5px rgba(0,0,0,0.7);
box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.7);
}
li a:first-child, li b {
li a:first-child,
li b {
background-color: #8f05a5;
font-weight: bold;
color: white;
text-decoration:none;
text-decoration: none;
padding: 2px 5px;
text-shadow: 2px 2px 1px black;
cursor:pointer;
cursor: pointer;
}
li strong {
color: red;
}
input {
height:35px;
font-size:14px;
height: 35px;
font-size: 14px;
padding-left: .3em;
}
label + a {
label+a {
text-decoration: none;
}
h1 + main {
h1+main {
display: flex;
}
}
aside {
display: flex;
flex-direction: column;
padding: 0.2em;
}
button {
height:40px;
width:130px;
font-size:16px;
height: 40px;
width: 130px;
font-size: 16px;
margin-top: 1em;
box-shadow: 5px 5px 5px rgba(0,0,0,0.7);
box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.7);
}
div button {
background-color: #7bff97;
}
nav {
display: flex;
align-items: baseline;
justify-content: space-between;
}
#left {
align-items:flex-end;
align-items: flex-end;
text-shadow: 0.5px 0.5px 1px #757474;
}
#cr {
font-weight: bold;
cursor:pointer;
cursor: pointer;
font-size: 1.5em;
}
#up {
width: auto;
width: auto;
}
.note {
background-color: #fecdee;
padding: 0.5em;
@@ -81,31 +97,39 @@ nav {
max-width: 320px;
border-radius: 0.5em;
}
.no {
display: none;
}
form [title] {
background-color: skyblue;
font-size: 16px;
width: 125px;
}
form:nth-of-type(2) {
margin-bottom: 1em;
}
[value*=Format] {
margin-top: 1em;
box-shadow: 5px 5px 5px rgba(0,0,0,0.7);
box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.7);
}
[name="group"] {
display: none;
}
[name="group"] + label {
[name="group"]+label {
font-size: 1.5em;
margin-right: 5px;
}
[name="group"] + label::before {
[name="group"]+label::before {
content: "\002610";
}
[name="group"]:checked + label::before {
content: '\002611\0027A5';
}
[name="group"]:checked+label::before {
content: '\002611\0027A5';
}

View File

@@ -80,8 +80,8 @@ int NTPClientPlus::updateNTP()
// check if time off last ntp update is roughly in the same range: 100sec apart (validation check)
if (this->_lastSecsSince1900 == 0 || tempSecsSince1900 - this->_lastSecsSince1900 < 100000)
{
// Only update time then
this->_lastUpdate = millis() - (NTP_RECEIVE_WAIT_TIME_MS * (conn_tries + 1)); // Account for delay in reading the time
// Only update time then account for delay in reading the time
this->_lastUpdate = (system_get_time() / 1000) - (NTP_RECEIVE_WAIT_TIME_MS * (conn_tries + 1));
this->_secsSince1900 = tempSecsSince1900;
this->_currentEpoc = this->_secsSince1900 - UNIX_TIMESTAMP_1900;
@@ -141,7 +141,7 @@ unsigned long NTPClientPlus::getSecsSince1900() const
{
return this->_timeOffset + // User offset
this->_secsSince1900 + // seconds returned by the NTP server
((millis() - this->_lastUpdate) / 1000); // Time since last update
(((system_get_time() / 1000) - this->_lastUpdate) / 1000); // Time since last update
}
/**
@@ -685,8 +685,8 @@ bool NTPClientPlus::updateSWChange()
void wait(unsigned long time)
{
unsigned long start = millis();
while (millis() - start < time)
unsigned long start = (system_get_time() / 1000);
while (((system_get_time() / 1000) - start) < time)
{
yield();
};

View File

@@ -21,7 +21,7 @@ void UDPLogger::set_name(String name)
void UDPLogger::log_string(String message)
{
// wait 5 milliseconds if last send was less than 5 milliseconds before
if (millis() < (_lastSend + 5))
if ((system_get_time() / 1000) < (_lastSend + 5))
{
delay(5);
}
@@ -31,13 +31,13 @@ void UDPLogger::log_string(String message)
message.toCharArray(_packetBuffer, 100);
_udp.print(_packetBuffer);
_udp.endPacket();
_lastSend = millis();
_lastSend = (system_get_time() / 1000);
}
void UDPLogger::log_color_24bit(uint32_t color)
{
uint8_t result_red = color >> 16 & 0xff;
uint8_t result_green = color >> 8 & 0xff;
uint8_t result_blue = color & 0xff;
uint8_t result_red = color >> 16 & UINT8_MAX;
uint8_t result_green = color >> 8 & UINT8_MAX;
uint8_t result_blue = color & UINT8_MAX;
log_string(String(result_red) + ", " + String(result_green) + ", " + String(result_blue));
}

View File

@@ -60,10 +60,10 @@ void Pong::loopCycle()
*/
void Pong::ctrlUp(uint8_t playerid)
{
if (millis() > _lastButtonClick + DEBOUNCE_TIME)
if ((system_get_time() / 1000) > (_lastButtonClick + DEBOUNCE_TIME))
{
_playerMovement[playerid] = PADDLE_MOVE_DOWN; // need to swap direction as field is rotated 180deg
_lastButtonClick = millis();
_lastButtonClick = (system_get_time() / 1000);
}
}
@@ -74,10 +74,10 @@ void Pong::ctrlUp(uint8_t playerid)
*/
void Pong::ctrlDown(uint8_t playerid)
{
if (millis() > _lastButtonClick + DEBOUNCE_TIME)
if ((system_get_time() / 1000) > (_lastButtonClick + DEBOUNCE_TIME))
{
_playerMovement[playerid] = PADDLE_MOVE_UP; // need to swap direction as field is rotated 180deg
_lastButtonClick = millis();
_lastButtonClick = (system_get_time() / 1000);
}
}
@@ -88,10 +88,10 @@ void Pong::ctrlDown(uint8_t playerid)
*/
void Pong::ctrlNone(uint8_t playerid)
{
if (millis() > _lastButtonClick + DEBOUNCE_TIME)
if ((system_get_time() / 1000) > (_lastButtonClick + DEBOUNCE_TIME))
{
_playerMovement[playerid] = PADDLE_MOVE_NONE;
_lastButtonClick = millis();
_lastButtonClick = (system_get_time() / 1000);
}
}
@@ -102,9 +102,9 @@ void Pong::ctrlNone(uint8_t playerid)
*/
void Pong::initGame(uint8_t numBots)
{
(*_logger).log_string("Pong: init with " + String(numBots) + " Bots");
_logger->log_string("Pong: init with " + String(numBots) + " Bots");
resetLEDs();
_lastButtonClick = millis();
_lastButtonClick = (system_get_time() / 1000);
_numBots = numBots;
@@ -134,11 +134,11 @@ void Pong::initGame(uint8_t numBots)
void Pong::updateBall()
{
bool hitBall = false;
if ((millis() - _lastBallUpdate) < _ballDelay)
if (((system_get_time() / 1000) - _lastBallUpdate) < _ballDelay)
{
return;
}
_lastBallUpdate = millis();
_lastBallUpdate = (system_get_time() / 1000);
toggleLed(_ball.x, _ball.y, LED_TYPE_OFF);
// collision detection for player 1
@@ -199,7 +199,7 @@ void Pong::updateBall()
*/
void Pong::endGame()
{
(*_logger).log_string("Pong: Game ended");
_logger->log_string("Pong: Game ended");
_gameState = GAME_STATE_END;
toggleLed(_ball.x, _ball.y, LED_TYPE_BALL_RED);
}
@@ -210,11 +210,11 @@ void Pong::endGame()
*/
void Pong::updateGame()
{
if ((millis() - _lastDrawUpdate) < GAME_DELAY)
if (((system_get_time() / 1000) - _lastDrawUpdate) < GAME_DELAY)
{
return;
}
_lastDrawUpdate = millis();
_lastDrawUpdate = (system_get_time() / 1000);
// turn off paddle LEDs
for (uint8_t p = 0; p < PLAYER_AMOUNT; p++)
@@ -297,7 +297,7 @@ uint8_t Pong::getPlayerMovement(uint8_t playerId)
*/
void Pong::resetLEDs()
{
(*_ledmatrix).flush();
_ledmatrix->flush();
}
/**
@@ -328,5 +328,5 @@ void Pong::toggleLed(uint8_t x, uint8_t y, uint8_t type)
break;
}
(*_ledmatrix).grid_add_pixel(x, y, color);
_ledmatrix->grid_add_pixel(x, y, color);
}

View File

@@ -58,11 +58,11 @@ void Snake::loopCycle()
*/
void Snake::ctrlUp()
{
if (millis() > _lastButtonClick + DEBOUNCE_TIME && _gameState == GAME_STATE_RUNNING)
if ((system_get_time() / 1000) > _lastButtonClick + DEBOUNCE_TIME && _gameState == GAME_STATE_RUNNING)
{
(*_logger).log_string("Snake: UP");
_logger->log_string("Snake: UP");
_userDirection = DIRECTION_DOWN; // need to swap direction as field is rotated 180deg
_lastButtonClick = millis();
_lastButtonClick = (system_get_time() / 1000);
}
}
@@ -72,11 +72,11 @@ void Snake::ctrlUp()
*/
void Snake::ctrlDown()
{
if (millis() > _lastButtonClick + DEBOUNCE_TIME && _gameState == GAME_STATE_RUNNING)
if ((system_get_time() / 1000) > _lastButtonClick + DEBOUNCE_TIME && _gameState == GAME_STATE_RUNNING)
{
_logger->log_string("Snake: DOWN");
_userDirection = DIRECTION_UP; // need to swap direction as field is rotated 180deg
_lastButtonClick = millis();
_lastButtonClick = (system_get_time() / 1000);
}
}
@@ -86,11 +86,11 @@ void Snake::ctrlDown()
*/
void Snake::ctrlRight()
{
if (millis() > _lastButtonClick + DEBOUNCE_TIME && _gameState == GAME_STATE_RUNNING)
if ((system_get_time() / 1000) > _lastButtonClick + DEBOUNCE_TIME && _gameState == GAME_STATE_RUNNING)
{
_logger->log_string("Snake: RIGHT");
_userDirection = DIRECTION_LEFT; // need to swap direction as field is rotated 180deg
_lastButtonClick = millis();
_lastButtonClick = (system_get_time() / 1000);
}
}
@@ -100,11 +100,11 @@ void Snake::ctrlRight()
*/
void Snake::ctrlLeft()
{
if (millis() > _lastButtonClick + DEBOUNCE_TIME && _gameState == GAME_STATE_RUNNING)
if ((system_get_time() / 1000) > _lastButtonClick + DEBOUNCE_TIME && _gameState == GAME_STATE_RUNNING)
{
_logger->log_string("Snake: LEFT");
_userDirection = DIRECTION_RIGHT; // need to swap direction as field is rotated 180deg
_lastButtonClick = millis();
_lastButtonClick = (system_get_time() / 1000);
}
}
@@ -131,7 +131,7 @@ void Snake::initGame()
_food.y = -1;
_wormLength = MIN_TAIL_LENGTH;
_userDirection = DIRECTION_LEFT;
_lastButtonClick = millis();
_lastButtonClick = (system_get_time() / 1000);
for (int i = 0; i < MAX_TAIL_LENGTH; i++)
{
@@ -148,7 +148,7 @@ void Snake::initGame()
*/
void Snake::updateGame()
{
if ((millis() - _lastDrawUpdate) > GAME_DELAY)
if (((system_get_time() / 1000) - _lastDrawUpdate) > GAME_DELAY)
{
_logger->log_string("Snake: update game");
toggleLed(_tail[_wormLength - 1].x, _tail[_wormLength - 1].y, LED_TYPE_OFF);
@@ -197,7 +197,7 @@ void Snake::updateGame()
updateFood();
}
_lastDrawUpdate = millis();
_lastDrawUpdate = (system_get_time() / 1000);
}
}

View File

@@ -26,7 +26,7 @@ Tetris::Tetris(LEDMatrix *myledmatrix, UDPLogger *mylogger)
{
_logger = mylogger;
_ledmatrix = myledmatrix;
_gameStatet = GAME_STATE_READY;
_gameState = GAME_STATE_READY;
}
/**
@@ -35,7 +35,7 @@ Tetris::Tetris(LEDMatrix *myledmatrix, UDPLogger *mylogger)
*/
void Tetris::loopCycle()
{
switch (_gameStatet)
switch (_gameState)
{
case GAME_STATE_READY:
@@ -51,18 +51,18 @@ void Tetris::loopCycle()
// move faster down when allow drop
if (_allowdrop)
{
if (millis() > _droptime + 50)
if ((system_get_time() / 1000) > (_droptime + 50))
{
_droptime = millis();
_droptime = (system_get_time() / 1000);
shiftActiveBrick(DIR_DOWN);
printField();
}
}
// move down with regular speed
if ((millis() - _prevUpdateTime) > (_brickSpeed * _speedtetris / 100))
if (((system_get_time() / 1000) - _prevUpdateTime) > (_brickSpeed * _speedtetris / 100))
{
_prevUpdateTime = millis();
_prevUpdateTime = (system_get_time() / 1000);
shiftActiveBrick(DIR_DOWN);
printField();
}
@@ -74,7 +74,7 @@ void Tetris::loopCycle()
// and create new brick at top of field
checkFullLines();
newActiveBrick();
_prevUpdateTime = millis(); // Reset update time to avoid brick dropping two spaces
_prevUpdateTime = (system_get_time() / 1000); // Reset update time to avoid brick dropping two spaces
}
break;
case GAME_STATE_PAUSED:
@@ -85,17 +85,17 @@ void Tetris::loopCycle()
if (_tetrisGameOver == true)
{
_tetrisGameOver = false;
(*_logger).log_string("Tetris: end");
_logger->log_string("Tetris: end");
everythingRed();
_tetrisshowscore = millis();
_tetrisshowscore = (system_get_time() / 1000);
}
if (millis() > (_tetrisshowscore + RED_END_TIME))
if ((system_get_time() / 1000) > (_tetrisshowscore + RED_END_TIME))
{
resetLEDs();
_score = _nbRowsTotal;
showscore();
_gameStatet = GAME_STATE_READY;
_gameState = GAME_STATE_READY;
}
break;
}
@@ -107,10 +107,10 @@ void Tetris::loopCycle()
*/
void Tetris::ctrlStart()
{
if (millis() > _lastButtonClick + DEBOUNCE_TIME)
if ((system_get_time() / 1000) > _lastButtonClick + DEBOUNCE_TIME)
{
_lastButtonClick = millis();
_gameStatet = GAME_STATE_INIT;
_lastButtonClick = (system_get_time() / 1000);
_gameState = GAME_STATE_INIT;
}
}
@@ -120,20 +120,20 @@ void Tetris::ctrlStart()
*/
void Tetris::ctrlPlayPause()
{
if (millis() > _lastButtonClick + DEBOUNCE_TIME)
if ((system_get_time() / 1000) > _lastButtonClick + DEBOUNCE_TIME)
{
_lastButtonClick = millis();
if (_gameStatet == GAME_STATE_PAUSED)
_lastButtonClick = (system_get_time() / 1000);
if (_gameState == GAME_STATE_PAUSED)
{
(*_logger).log_string("Tetris: continue");
_logger->log_string("Tetris: continue");
_gameStatet = GAME_STATE_RUNNING;
_gameState = GAME_STATE_RUNNING;
}
else if (_gameStatet == GAME_STATE_RUNNING)
else if (_gameState == GAME_STATE_RUNNING)
{
(*_logger).log_string("Tetris: pause");
_logger->log_string("Tetris: pause");
_gameStatet = GAME_STATE_PAUSED;
_gameState = GAME_STATE_PAUSED;
}
}
}
@@ -144,9 +144,9 @@ void Tetris::ctrlPlayPause()
*/
void Tetris::ctrlRight()
{
if (millis() > _lastButtonClick + DEBOUNCE_TIME && _gameStatet == GAME_STATE_RUNNING)
if ((system_get_time() / 1000) > _lastButtonClick + DEBOUNCE_TIME && _gameState == GAME_STATE_RUNNING)
{
_lastButtonClick = millis();
_lastButtonClick = (system_get_time() / 1000);
shiftActiveBrick(DIR_RIGHT);
printField();
}
@@ -158,9 +158,9 @@ void Tetris::ctrlRight()
*/
void Tetris::ctrlLeft()
{
if (millis() > _lastButtonClick + DEBOUNCE_TIME && _gameStatet == GAME_STATE_RUNNING)
if ((system_get_time() / 1000) > _lastButtonClick + DEBOUNCE_TIME && _gameState == GAME_STATE_RUNNING)
{
_lastButtonClick = millis();
_lastButtonClick = (system_get_time() / 1000);
shiftActiveBrick(DIR_LEFT);
printField();
}
@@ -172,9 +172,9 @@ void Tetris::ctrlLeft()
*/
void Tetris::ctrlUp()
{
if (millis() > _lastButtonClick + DEBOUNCE_TIME && _gameStatet == GAME_STATE_RUNNING)
if ((system_get_time() / 1000) > _lastButtonClick + DEBOUNCE_TIME && _gameState == GAME_STATE_RUNNING)
{
_lastButtonClick = millis();
_lastButtonClick = (system_get_time() / 1000);
rotateActiveBrick();
printField();
}
@@ -187,10 +187,10 @@ void Tetris::ctrlUp()
void Tetris::ctrlDown()
{
// longer debounce time, to prevent immediate drop
if (millis() > _lastButtonClickr + DEBOUNCE_TIME * 5 && _gameStatet == GAME_STATE_RUNNING)
if ((system_get_time() / 1000) > _lastButtonClickr + DEBOUNCE_TIME * 5 && _gameState == GAME_STATE_RUNNING)
{
_allowdrop = true;
_lastButtonClickr = millis();
_lastButtonClickr = (system_get_time() / 1000);
}
}
@@ -221,7 +221,7 @@ void Tetris::resetLEDs()
*/
void Tetris::tetrisInit()
{
(*_logger).log_string("Tetris: init");
_logger->log_string("Tetris: init");
clearField();
_brickSpeed = INIT_SPEED;
@@ -230,9 +230,9 @@ void Tetris::tetrisInit()
_tetrisGameOver = false;
newActiveBrick();
_prevUpdateTime = millis();
_prevUpdateTime = (system_get_time() / 1000);
_gameStatet = GAME_STATE_RUNNING;
_gameState = GAME_STATE_RUNNING;
}
/**
@@ -318,7 +318,7 @@ void Tetris::newActiveBrick()
if (checkFieldCollision(&_activeBrick))
{
_tetrisGameOver = true;
_gameStatet = GAME_STATE_END;
_gameState = GAME_STATE_END;
}
}

View File

@@ -437,3 +437,20 @@ int random_tetris(bool init)
}
return 0;
}
int draw_heart_animation(void)
{
static uint8_t frame_idx = 0;
for (int col = 0; col < MATRIX_WIDTH; col++)
{
for (int row = 0; row < MATRIX_HEIGHT; row++)
{
led_matrix.grid_add_pixel(col, row, heart_frames_colormap_11x11[frame_idx][col][row]);
}
}
// Increase frame index per call until HEART_ANIMATION_FRAMES - 1 for array index.
frame_idx = (frame_idx >= (HEART_ANIMATION_FRAMES - 1)) ? 0 : frame_idx + 1;
return 0;
}

View File

@@ -50,9 +50,9 @@ uint32_t LEDMatrix::color_24bit(uint8_t r, uint8_t g, uint8_t b)
*/
uint16_t LEDMatrix::color_24_to_16bit(uint32_t color_24bit)
{
uint8_t r = color_24bit >> 16 & 0xff;
uint8_t g = color_24bit >> 8 & 0xff;
uint8_t b = color_24bit & 0xff;
uint8_t r = color_24bit >> 16 & UINT8_MAX;
uint8_t g = color_24bit >> 8 & UINT8_MAX;
uint8_t b = color_24bit & UINT8_MAX;
return ((uint16_t)(r & 0xF8) << 8) |
((uint16_t)(g & 0xFC) << 3) |
(b >> 3);
@@ -90,13 +90,13 @@ uint32_t LEDMatrix::wheel(uint8_t WheelPos)
*/
uint32_t LEDMatrix::interpolate_color_24bit(uint32_t color1, uint32_t color2, float factor)
{
uint8_t resultRed = color1 >> 16 & 0xff;
uint8_t resultGreen = color1 >> 8 & 0xff;
uint8_t resultBlue = color1 & 0xff;
resultRed = (uint8_t)(resultRed + (int16_t)(factor * ((int16_t)(color2 >> 16 & 0xff) - (int16_t)resultRed)));
resultGreen = (uint8_t)(resultGreen + (int16_t)(factor * ((int16_t)(color2 >> 8 & 0xff) - (int16_t)resultGreen)));
resultBlue = (uint8_t)(resultBlue + (int16_t)(factor * ((int16_t)(color2 & 0xff) - (int16_t)resultBlue)));
return color_24bit(resultRed, resultGreen, resultBlue);
uint8_t result_R = color1 >> 16 & UINT8_MAX;
uint8_t result_G = color1 >> 8 & UINT8_MAX;
uint8_t result_B = color1 & UINT8_MAX;
result_R = (uint8_t)(result_R + (int16_t)(factor * ((int16_t)(color2 >> 16 & UINT8_MAX) - (int16_t)result_R)));
result_G = (uint8_t)(result_G + (int16_t)(factor * ((int16_t)(color2 >> 8 & UINT8_MAX) - (int16_t)result_G)));
result_B = (uint8_t)(result_B + (int16_t)(factor * ((int16_t)(color2 & UINT8_MAX) - (int16_t)result_B)));
return color_24bit(result_R, result_G, result_B);
}
/**
@@ -128,19 +128,19 @@ void LEDMatrix::set_min_indicator(uint8_t pattern, uint32_t color)
// 0 -> 0000
if (pattern & 1)
{
_target_indicators[0] = color;
_target_minute_indicators[0] = color;
}
if (pattern >> 1 & 1)
{
_target_indicators[1] = color;
_target_minute_indicators[1] = color;
}
if (pattern >> 2 & 1)
{
_target_indicators[2] = color;
_target_minute_indicators[2] = color;
}
if (pattern >> 3 & 1)
{
_target_indicators[3] = color;
_target_minute_indicators[3] = color;
}
}
@@ -158,10 +158,6 @@ void LEDMatrix::grid_add_pixel(uint8_t x, uint8_t y, uint32_t color)
{
_target_grid[y][x] = color;
}
else
{
// logger->log_string("Index out of Range: " + String(x) + ", " + String(y));
}
}
/**
@@ -179,10 +175,10 @@ void LEDMatrix::flush(void)
}
}
// set every minutes indicator led to 0
_target_indicators[0] = 0;
_target_indicators[1] = 0;
_target_indicators[2] = 0;
_target_indicators[3] = 0;
_target_minute_indicators[0] = 0;
_target_minute_indicators[1] = 0;
_target_minute_indicators[2] = 0;
_target_minute_indicators[3] = 0;
}
/**
@@ -215,31 +211,31 @@ void LEDMatrix::_draw_on_matrix(float factor)
uint32_t filtered_color = 0;
// loop over all leds in matrix
for (int s = 0; s < MATRIX_WIDTH; s++)
for (uint8_t col = 0; col < MATRIX_WIDTH; col++)
{
for (int z = 0; z < MATRIX_HEIGHT; z++)
for (uint8_t row = 0; row < MATRIX_HEIGHT; row++)
{
// inplement momentum as smooth transistion function
uint32_t filtered_color = interpolate_color_24bit(_current_grid[z][s], _target_grid[z][s], factor);
_neomatrix->drawPixel(s, z, color_24_to_16bit(filtered_color));
_current_grid[z][s] = filtered_color;
// implement momentum as smooth transistion function
filtered_color = interpolate_color_24bit(_current_grid[row][col], _target_grid[row][col], factor);
_neomatrix->drawPixel(col, row, color_24_to_16bit(filtered_color));
_current_grid[row][col] = filtered_color;
total_current += _calc_estimated_led_current(filtered_color);
}
}
// loop over all minute indicator leds
for (int i = 0; i < 4; i++)
for (uint8_t i = 0; i < 4; i++)
{
filtered_color = interpolate_color_24bit(_current_indicators[i], _target_indicators[i], factor);
filtered_color = interpolate_color_24bit(_current_minute_indicators[i], _target_minute_indicators[i], factor);
_neomatrix->drawPixel(MATRIX_WIDTH - (1 + i), MATRIX_HEIGHT, color_24_to_16bit(filtered_color));
_current_indicators[i] = filtered_color;
_current_minute_indicators[i] = filtered_color;
total_current += _calc_estimated_led_current(filtered_color);
}
// Check if totalCurrent reaches CURRENTLIMIT -> if yes reduce brightness
// Check if totalCurrent reaches _current_limit -> if yes reduce brightness
if (total_current > _current_limit)
{
uint8_t new_brightness = _brightness * float(_current_limit) / float(total_current);
uint8_t new_brightness = (uint8)(_brightness * (float(_current_limit) / float(total_current)));
_neomatrix->setBrightness(new_brightness);
}
_neomatrix->show();
@@ -319,9 +315,9 @@ void LEDMatrix::set_brightness(uint8_t brightness)
uint16_t LEDMatrix::_calc_estimated_led_current(uint32_t color)
{
// extract rgb values
uint8_t red = color >> 16 & 0xff;
uint8_t green = color >> 8 & 0xff;
uint8_t blue = color & 0xff;
uint8_t red = color >> 16 & UINT8_MAX;
uint8_t green = color >> 8 & UINT8_MAX;
uint8_t blue = color & UINT8_MAX;
// Linear estimation: 20mA for full brightness per LED
// (calculation avoids float numbers)

View File

@@ -16,38 +16,36 @@ const String clock_chars_as_string = "ESRISTNFUNFVIERTELZEHNZWANZIGHVORPIKACHUNA
void draw_minute_indicator(uint8_t minutes, uint32_t color)
{
// separate LEDs for minutes in an additional row
switch (minutes % 5)
{
switch (minutes % 5)
{
case 1:
{
led_matrix.set_min_indicator(0b1000, color);
break;
}
case 1:
{
led_matrix.set_min_indicator(0b1000, color);
break;
}
case 2:
{
led_matrix.set_min_indicator(0b1100, color);
break;
}
case 2:
{
led_matrix.set_min_indicator(0b1100, color);
break;
}
case 3:
{
led_matrix.set_min_indicator(0b1110, color);
break;
}
case 3:
{
led_matrix.set_min_indicator(0b1110, color);
break;
}
case 4:
{
led_matrix.set_min_indicator(0b1111, color);
break;
}
case 0:
default:
{
break;
}
}
case 4:
{
led_matrix.set_min_indicator(0b1111, color);
break;
}
case 0:
default:
{
break;
}
}
}

View File

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