boot, clock, http, log, mqtt, system, wifi, Node, demo, NodeTest
This commit is contained in:
commit
a6fb6af1a9
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
/.pio/
|
||||||
|
/.idea/
|
||||||
|
/src/patrix/library.json
|
||||||
|
/*.tar*
|
||||||
29
library.json
Normal file
29
library.json
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"name": "Patrix",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "Patrix library",
|
||||||
|
"keywords": "patrix",
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Patrick Haßel",
|
||||||
|
"email": "development@patrick-hassel.de",
|
||||||
|
"url": "https://patrick-hassel.de/",
|
||||||
|
"maintainer": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"homepage": "https://patrick-hassel.de/",
|
||||||
|
"dependencies": {
|
||||||
|
"knolleary/pubsubclient": "2.8",
|
||||||
|
"bblanchon/ArduinoJson": "7.3.0",
|
||||||
|
"me-no-dev/ESPAsyncWebServer": "1.2.4"
|
||||||
|
},
|
||||||
|
"export": {
|
||||||
|
"include": "src/patrix"
|
||||||
|
},
|
||||||
|
"frameworks": "arduino",
|
||||||
|
"platforms": [
|
||||||
|
"espressif8266",
|
||||||
|
"espressif32"
|
||||||
|
]
|
||||||
|
}
|
||||||
9
platformio.ini
Normal file
9
platformio.ini
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
[env:Patrix]
|
||||||
|
platform = espressif32
|
||||||
|
board = esp32dev
|
||||||
|
framework = arduino
|
||||||
|
lib_deps =
|
||||||
|
knolleary/pubsubclient@2.8
|
||||||
|
bblanchon/ArduinoJson@7.3.0
|
||||||
|
me-no-dev/ESPAsyncWebServer@1.2.4
|
||||||
|
build_flags = -DWIFI_SSID=\"HappyNet\" -DWIFI_PKEY=\"1Grausame!Sackratte7\" -DWIFI_HOST=\"PatrixTest\"
|
||||||
21
src/demo/NodeTest.h
Normal file
21
src/demo/NodeTest.h
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
#ifndef NODE_TEST_H
|
||||||
|
#define NODE_TEST_H
|
||||||
|
|
||||||
|
#include <public/node/Node.h>
|
||||||
|
#include <patrix/node/Node.h>
|
||||||
|
|
||||||
|
class NodeTest final : public Node {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
explicit NodeTest() : Node(true, true, true) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup() override {
|
||||||
|
display.printf("Test");
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
7
src/demo/main.cpp
Normal file
7
src/demo/main.cpp
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#include "NodeTest.h"
|
||||||
|
|
||||||
|
auto node = NodeTest();
|
||||||
|
|
||||||
|
Node &patrixGetNode() {
|
||||||
|
return node;
|
||||||
|
}
|
||||||
47
src/library.json
Normal file
47
src/library.json
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
{
|
||||||
|
"name": "Patrix",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Patrick Haßel",
|
||||||
|
"email": "development@patrick-hassel.de",
|
||||||
|
"maintainer": true,
|
||||||
|
"url": "https://patrick-hassel.de/"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Patrix library",
|
||||||
|
"homepage": "https://patrick-hassel.de/",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": [
|
||||||
|
{
|
||||||
|
"owner": "knolleary",
|
||||||
|
"name": "pubsubclient",
|
||||||
|
"version": "2.8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"owner": "bblanchon",
|
||||||
|
"name": "ArduinoJson",
|
||||||
|
"version": "7.3.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"owner": "me-no-dev",
|
||||||
|
"name": "ESPAsyncWebServer",
|
||||||
|
"version": "1.2.4"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"export": {
|
||||||
|
"exclude": [
|
||||||
|
"src/demo/**"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"patrix"
|
||||||
|
],
|
||||||
|
"platforms": [
|
||||||
|
"espressif8266",
|
||||||
|
"espressif32"
|
||||||
|
],
|
||||||
|
"frameworks": [
|
||||||
|
"arduino"
|
||||||
|
]
|
||||||
|
}
|
||||||
16
src/patrix/Patrix.cpp
Normal file
16
src/patrix/Patrix.cpp
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#include <patrix/Patrix.h>
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
logSetup();
|
||||||
|
bootDelay();
|
||||||
|
patrixNode.setup();
|
||||||
|
httpSetup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
wifiLoop();
|
||||||
|
clockLoop();
|
||||||
|
mqttLoop();
|
||||||
|
patrixNode.loop();
|
||||||
|
httpLoop();
|
||||||
|
}
|
||||||
17
src/patrix/Patrix.h
Normal file
17
src/patrix/Patrix.h
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#ifndef PATRIX_H
|
||||||
|
#define PATRIX_H
|
||||||
|
|
||||||
|
#include <ArduinoJson.h>
|
||||||
|
#include <ESPAsyncWebServer.h>
|
||||||
|
#include <PubSubClient.h>
|
||||||
|
|
||||||
|
#include <patrix/core/boot.h>
|
||||||
|
#include <patrix/core/clock.h>
|
||||||
|
#include <patrix/core/http.h>
|
||||||
|
#include <patrix/core/log.h>
|
||||||
|
#include <patrix/core/mqtt.h>
|
||||||
|
#include <patrix/core/system.h>
|
||||||
|
#include <patrix/core/wifi.h>
|
||||||
|
#include <patrix/node/Node.h>
|
||||||
|
|
||||||
|
#endif
|
||||||
95
src/patrix/core/boot.cpp
Normal file
95
src/patrix/core/boot.cpp
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
#include <patrix/core/boot.h>
|
||||||
|
#include <patrix/core/clock.h>
|
||||||
|
#include <patrix/core/log.h>
|
||||||
|
#include <patrix/core/wifi.h>
|
||||||
|
#include <patrix/node/Node.h>
|
||||||
|
|
||||||
|
#include "esp32/rom/rtc.h"
|
||||||
|
|
||||||
|
#define BOOT_DELAY_SECONDS 3
|
||||||
|
|
||||||
|
auto failureReset = false;
|
||||||
|
|
||||||
|
const char *getResetReason(const RESET_REASON reason) {
|
||||||
|
switch (reason) {
|
||||||
|
case NO_MEAN: return "NO_MEAN";
|
||||||
|
case POWERON_RESET: return "POWERON_RESET";
|
||||||
|
case SW_RESET: return "SW_RESET";
|
||||||
|
case OWDT_RESET: return "OWDT_RESET";
|
||||||
|
case DEEPSLEEP_RESET: return "DEEPSLEEP_RESET";
|
||||||
|
case SDIO_RESET: return "SDIO_RESET";
|
||||||
|
case TG0WDT_SYS_RESET: return "TG0WDT_SYS_RESET";
|
||||||
|
case TG1WDT_SYS_RESET: return "TG1WDT_SYS_RESET";
|
||||||
|
case RTCWDT_SYS_RESET: return "RTCWDT_SYS_RESET";
|
||||||
|
case INTRUSION_RESET: return "INTRUSION_RESET";
|
||||||
|
case TGWDT_CPU_RESET: return "TGWDT_CPU_RESET";
|
||||||
|
case SW_CPU_RESET: return "SW_CPU_RESET";
|
||||||
|
case RTCWDT_CPU_RESET: return "RTCWDT_CPU_RESET";
|
||||||
|
case EXT_CPU_RESET: return "EXT_CPU_RESET";
|
||||||
|
case RTCWDT_BROWN_OUT_RESET: return "RTCWDT_BROWN_OUT_RESET";
|
||||||
|
case RTCWDT_RTC_RESET: return "RTCWDT_RTC_RESET";
|
||||||
|
default: return "[???]";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void bootReasonLoad() {
|
||||||
|
const auto r0 = rtc_get_reset_reason(0);
|
||||||
|
const auto r1 = rtc_get_reset_reason(1);
|
||||||
|
failureReset = (r0 != POWERON_RESET && r0 != DEEPSLEEP_RESET) || (r1 != POWERON_RESET && r1 != DEEPSLEEP_RESET);
|
||||||
|
if (failureReset) {
|
||||||
|
warn("Forcing OTA delay because of failure-reset: r0=%s, r1=%s", getResetReason(r0), getResetReason(r1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void boot_waitForWifi() {
|
||||||
|
info("Waiting for WiFi...");
|
||||||
|
while (!isWiFiConnected()) {
|
||||||
|
wifiLoop();
|
||||||
|
yield();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void boot_waitForOTA() {
|
||||||
|
info("Waiting %d seconds for OTA update...", BOOT_DELAY_SECONDS);
|
||||||
|
const auto start = millis();
|
||||||
|
while (millis() - start < BOOT_DELAY_SECONDS * 1000) {
|
||||||
|
wifiLoop();
|
||||||
|
yield();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void boot_waitForClock() {
|
||||||
|
info("Waiting for system time...");
|
||||||
|
while (!isClockSet()) {
|
||||||
|
wifiLoop();
|
||||||
|
clockLoop();
|
||||||
|
yield();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void bootDelay() {
|
||||||
|
bootReasonLoad();
|
||||||
|
|
||||||
|
if (!failureReset && !patrixNode.waitForWiFi && !patrixNode.waitForOTA && !patrixNode.waitForClock) {
|
||||||
|
info("NOT delaying boot.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
info("Boot delay active...");
|
||||||
|
|
||||||
|
boot_waitForWifi();
|
||||||
|
|
||||||
|
if (failureReset || patrixNode.waitForOTA) {
|
||||||
|
boot_waitForOTA();
|
||||||
|
} else {
|
||||||
|
info("NOT waiting for OTA.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (patrixNode.waitForClock) {
|
||||||
|
boot_waitForClock();
|
||||||
|
} else {
|
||||||
|
info("NOT waiting for system time.");
|
||||||
|
}
|
||||||
|
|
||||||
|
info("Boot delay complete.");
|
||||||
|
}
|
||||||
6
src/patrix/core/boot.h
Normal file
6
src/patrix/core/boot.h
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#ifndef PATRIX_BOOT_H
|
||||||
|
#define PATRIX_BOOT_H
|
||||||
|
|
||||||
|
void bootDelay();
|
||||||
|
|
||||||
|
#endif
|
||||||
94
src/patrix/core/clock.cpp
Normal file
94
src/patrix/core/clock.cpp
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
#include <WiFi.h>
|
||||||
|
#include <patrix/core/clock.h>
|
||||||
|
#include <patrix/core/log.h>
|
||||||
|
#include <patrix/core/wifi.h>
|
||||||
|
|
||||||
|
#define CLOCK_GMT_OFFSET_SECONDS 3600
|
||||||
|
#define CLOCK_DST_OFFSET_SECONDS 3600
|
||||||
|
#define CLOCK_NTP_SERVER2_URL "de.pool.ntp.org"
|
||||||
|
#define CLOCK_EPOCH_SECONDS_MIN 1735686000
|
||||||
|
|
||||||
|
time_t clockOffset = 0;
|
||||||
|
|
||||||
|
time_t startupTime = 0;
|
||||||
|
|
||||||
|
auto ntpSet = false;
|
||||||
|
|
||||||
|
void clockLoop() {
|
||||||
|
if (isClockSet()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ntpSet && isWiFiConnected()) {
|
||||||
|
configTime(CLOCK_GMT_OFFSET_SECONDS, CLOCK_DST_OFFSET_SECONDS, CLOCK_NTP_SERVER2_URL, WiFi.gatewayIP().toString().c_str());
|
||||||
|
ntpSet = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto now = time(nullptr);
|
||||||
|
if (isCorrectTime(now)) {
|
||||||
|
startupTime = now - clockOffset;
|
||||||
|
info("clock set after %ld seconds! So startup was at %s", clockOffset, getStartupStr());
|
||||||
|
} else {
|
||||||
|
clockOffset = now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isClockSet() {
|
||||||
|
return startupTime != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
char *getClockStr(const time_t epoch) {
|
||||||
|
auto now = epoch;
|
||||||
|
if (now == 0) {
|
||||||
|
now = time(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
tm t{};
|
||||||
|
localtime_r(&now, &t);
|
||||||
|
|
||||||
|
static char buffer[20];
|
||||||
|
snprintf(buffer, sizeof buffer, "%04d-%02d-%02d %02d:%02d:%02d", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *getStartupStr() {
|
||||||
|
return getClockStr(startupTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
time_t getUptimeSeconds() {
|
||||||
|
return time(nullptr) - startupTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *getUptimeStr() {
|
||||||
|
const auto totalSeconds = getUptimeSeconds();
|
||||||
|
const auto totalMinutes = totalSeconds / 60;
|
||||||
|
const auto totalHours = totalMinutes / 60;
|
||||||
|
|
||||||
|
const auto partSeconds = totalSeconds % 60;
|
||||||
|
const auto partMinutes = totalMinutes % 60;
|
||||||
|
const auto partHours = totalHours % 24;
|
||||||
|
const auto partDays = totalHours / 24;
|
||||||
|
|
||||||
|
static char buffer[20];
|
||||||
|
snprintf(buffer, sizeof buffer, "%ldd %2ldh %2ldm %2lds", partDays, partHours, partMinutes, partSeconds);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isCorrectTime(const time_t now) {
|
||||||
|
return now >= CLOCK_EPOCH_SECONDS_MIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
time_t clockCorrect(const time_t t) {
|
||||||
|
if (!isClockSet() || isCorrectTime(t)) {
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
return t + clockOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clockCorrect(time_t *t) {
|
||||||
|
if (!isClockSet() || isCorrectTime(*t)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
*t += clockOffset;
|
||||||
|
}
|
||||||
24
src/patrix/core/clock.h
Normal file
24
src/patrix/core/clock.h
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
#ifndef PATRIX_CLOCK_H
|
||||||
|
#define PATRIX_CLOCK_H
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
void clockLoop();
|
||||||
|
|
||||||
|
bool isClockSet();
|
||||||
|
|
||||||
|
char *getClockStr(time_t epoch = 0);
|
||||||
|
|
||||||
|
char *getStartupStr();
|
||||||
|
|
||||||
|
time_t getUptimeSeconds();
|
||||||
|
|
||||||
|
char *getUptimeStr();
|
||||||
|
|
||||||
|
bool isCorrectTime(time_t now);
|
||||||
|
|
||||||
|
time_t clockCorrect(time_t t);
|
||||||
|
|
||||||
|
void clockCorrect(time_t *t);
|
||||||
|
|
||||||
|
#endif
|
||||||
58
src/patrix/core/http.cpp
Normal file
58
src/patrix/core/http.cpp
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
#include <ESPAsyncWebServer.h>
|
||||||
|
#include <patrix/core/log.h>
|
||||||
|
#include <patrix/core/system.h>
|
||||||
|
#include <patrix/node/Node.h>
|
||||||
|
|
||||||
|
AsyncWebServer server(80);
|
||||||
|
|
||||||
|
AsyncWebSocket ws("/ws");
|
||||||
|
|
||||||
|
void httpReboot(AsyncWebServerRequest *request) {
|
||||||
|
info("Rebooting...");
|
||||||
|
request->redirect("/");
|
||||||
|
request->client()->close();
|
||||||
|
yield();
|
||||||
|
delay(500);
|
||||||
|
|
||||||
|
restart();
|
||||||
|
}
|
||||||
|
|
||||||
|
void httpSetup() {
|
||||||
|
ws.onEvent([](AsyncWebSocket *socket, AsyncWebSocketClient *client, AwsEventType type, void *arg, unsigned char *message, unsigned length) {
|
||||||
|
const char *t;
|
||||||
|
switch (type) {
|
||||||
|
case WS_EVT_CONNECT:
|
||||||
|
t = "CONNECT";
|
||||||
|
patrixNode.websocketEvent(socket, client, type, arg, message, length);
|
||||||
|
break;
|
||||||
|
case WS_EVT_DISCONNECT:
|
||||||
|
t = "DISCONNECT";
|
||||||
|
break;
|
||||||
|
case WS_EVT_PONG:
|
||||||
|
t = "PONG";
|
||||||
|
break;
|
||||||
|
case WS_EVT_ERROR:
|
||||||
|
t = "ERROR";
|
||||||
|
break;
|
||||||
|
case WS_EVT_DATA:
|
||||||
|
t = "DATA";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
t = "[???]";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
debug("%s: %s (%d bytes)", client->remoteIP().toString().c_str(), t, length);
|
||||||
|
});
|
||||||
|
server.addHandler(&ws);
|
||||||
|
|
||||||
|
server.on("/reboot", HTTP_GET, httpReboot);
|
||||||
|
server.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
void httpLoop() {
|
||||||
|
ws.cleanupClients();
|
||||||
|
}
|
||||||
|
|
||||||
|
void websocketSendAll(const String& payload) {
|
||||||
|
ws.textAll(payload);
|
||||||
|
}
|
||||||
12
src/patrix/core/http.h
Normal file
12
src/patrix/core/http.h
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#ifndef PATRIX_HTTP_H
|
||||||
|
#define PATRIX_HTTP_H
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
void httpSetup();
|
||||||
|
|
||||||
|
void httpLoop();
|
||||||
|
|
||||||
|
void websocketSendAll(const String& payload);
|
||||||
|
|
||||||
|
#endif
|
||||||
75
src/patrix/core/log.cpp
Normal file
75
src/patrix/core/log.cpp
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
#include <patrix/core/clock.h>
|
||||||
|
#include <patrix/core/log.h>
|
||||||
|
|
||||||
|
void doLog(LogLevel level, const char *format, va_list args);
|
||||||
|
|
||||||
|
auto logLevel = DEBUG;
|
||||||
|
|
||||||
|
void logSetup() {
|
||||||
|
delay(500);
|
||||||
|
Serial.begin(115200);
|
||||||
|
info("Startup");
|
||||||
|
}
|
||||||
|
|
||||||
|
void log(const LogLevel level, const char *format, ...) {
|
||||||
|
va_list args;
|
||||||
|
va_start(args, format);
|
||||||
|
doLog(level, format, args);
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
void error(const char *format, ...) {
|
||||||
|
va_list args;
|
||||||
|
va_start(args, format);
|
||||||
|
doLog(ERROR, format, args);
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
void warn(const char *format, ...) {
|
||||||
|
va_list args;
|
||||||
|
va_start(args, format);
|
||||||
|
doLog(WARN, format, args);
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
void info(const char *format, ...) {
|
||||||
|
va_list args;
|
||||||
|
va_start(args, format);
|
||||||
|
doLog(INFO, format, args);
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
void debug(const char *format, ...) {
|
||||||
|
va_list args;
|
||||||
|
va_start(args, format);
|
||||||
|
doLog(DEBUG, format, args);
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
void doLog(const LogLevel level, const char *format, const va_list args) {
|
||||||
|
if (level > logLevel) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Serial.print(getClockStr());
|
||||||
|
switch (level) {
|
||||||
|
case ERROR:
|
||||||
|
Serial.print(" [ERROR] ");
|
||||||
|
break;
|
||||||
|
case WARN:
|
||||||
|
Serial.print(" [WARN ] ");
|
||||||
|
break;
|
||||||
|
case INFO:
|
||||||
|
Serial.print(" [INFO ] ");
|
||||||
|
break;
|
||||||
|
case DEBUG:
|
||||||
|
Serial.print(" [DEBUG] ");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
char message[256];
|
||||||
|
vsnprintf(message, sizeof message, format, args);
|
||||||
|
Serial.print(message);
|
||||||
|
Serial.print("\n");
|
||||||
|
|
||||||
|
yield();
|
||||||
|
}
|
||||||
23
src/patrix/core/log.h
Normal file
23
src/patrix/core/log.h
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#ifndef PATRIX_LOG_H
|
||||||
|
#define PATRIX_LOG_H
|
||||||
|
|
||||||
|
enum LogLevel {
|
||||||
|
ERROR = 0,
|
||||||
|
WARN = 1,
|
||||||
|
INFO = 2,
|
||||||
|
DEBUG = 3
|
||||||
|
};
|
||||||
|
|
||||||
|
void logSetup();
|
||||||
|
|
||||||
|
void error(const char *format, ...);
|
||||||
|
|
||||||
|
void warn(const char *format, ...);
|
||||||
|
|
||||||
|
void info(const char *format, ...);
|
||||||
|
|
||||||
|
void debug(const char *format, ...);
|
||||||
|
|
||||||
|
void log(LogLevel level, const char *format, ...);
|
||||||
|
|
||||||
|
#endif
|
||||||
49
src/patrix/core/mqtt.cpp
Normal file
49
src/patrix/core/mqtt.cpp
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
#include <PubSubClient.h>
|
||||||
|
#include <WiFi.h>
|
||||||
|
#include <patrix/core/log.h>
|
||||||
|
#include <patrix/core/mqtt.h>
|
||||||
|
#include <patrix/core/wifi.h>
|
||||||
|
|
||||||
|
#define MQTT_RETRY_DELAY_MILLIS 3000
|
||||||
|
|
||||||
|
WiFiClient wifiClient;
|
||||||
|
|
||||||
|
PubSubClient mqtt(wifiClient);
|
||||||
|
|
||||||
|
auto mqttHost = "10.0.0.50";
|
||||||
|
|
||||||
|
auto mqttPort = 1883;
|
||||||
|
|
||||||
|
auto mqttLastConnectMillis = 0UL;
|
||||||
|
|
||||||
|
char mqttWillTopic[128];
|
||||||
|
|
||||||
|
void mqttLoop() {
|
||||||
|
if (mqtt.loop()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!isWiFiConnected()) {
|
||||||
|
mqttLastConnectMillis = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (mqttLastConnectMillis == 0 || millis() - mqttLastConnectMillis > MQTT_RETRY_DELAY_MILLIS) {
|
||||||
|
snprintf(mqttWillTopic, sizeof(mqttWillTopic), "%s/online", WiFiClass::getHostname());
|
||||||
|
mqtt.setServer(mqttHost, mqttPort);
|
||||||
|
mqtt.setKeepAlive(10);
|
||||||
|
mqtt.setBufferSize(512);
|
||||||
|
mqttLastConnectMillis = millis();
|
||||||
|
info("mqtt connecting: host=%s, port=%d", mqttHost, mqttPort);
|
||||||
|
if (mqtt.connect(WiFiClass::getHostname(), mqttWillTopic, 1, true, "false")) {
|
||||||
|
info("mqtt connected");
|
||||||
|
yield();
|
||||||
|
mqtt.publish(mqttWillTopic, "true", true);
|
||||||
|
yield();
|
||||||
|
} else {
|
||||||
|
error("mqtt failed to connect");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool mqttPublish(const String& topic, const String& payload, const Retain retain) {
|
||||||
|
return mqtt.publish(topic.c_str(), payload.c_str(), retain == RETAIN);
|
||||||
|
}
|
||||||
14
src/patrix/core/mqtt.h
Normal file
14
src/patrix/core/mqtt.h
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
#ifndef PATRIX_MQTT_H
|
||||||
|
#define PATRIX_MQTT_H
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
enum Retain {
|
||||||
|
NO_RETAIN, RETAIN
|
||||||
|
};
|
||||||
|
|
||||||
|
void mqttLoop();
|
||||||
|
|
||||||
|
bool mqttPublish(const String& topic, const String& payload, Retain retain);
|
||||||
|
|
||||||
|
#endif
|
||||||
8
src/patrix/core/system.cpp
Normal file
8
src/patrix/core/system.cpp
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#include <Esp.h>
|
||||||
|
#include <patrix/core/system.h>
|
||||||
|
#include <patrix/core/wifi.h>
|
||||||
|
|
||||||
|
void restart() {
|
||||||
|
wifiOff();
|
||||||
|
ESP.restart();
|
||||||
|
}
|
||||||
6
src/patrix/core/system.h
Normal file
6
src/patrix/core/system.h
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#ifndef PATRIX_SYSTEM_H
|
||||||
|
#define PATRIX_SYSTEM_H
|
||||||
|
|
||||||
|
void restart();
|
||||||
|
|
||||||
|
#endif
|
||||||
92
src/patrix/core/wifi.cpp
Normal file
92
src/patrix/core/wifi.cpp
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
#include <ArduinoOTA.h>
|
||||||
|
#include <patrix/core/log.h>
|
||||||
|
#include <patrix/core/wifi.h>
|
||||||
|
|
||||||
|
#define WIFI_TIMEOUT_MILLIS 10000
|
||||||
|
|
||||||
|
auto wifiEnabled = true;
|
||||||
|
|
||||||
|
auto wifiConnected = false;
|
||||||
|
|
||||||
|
auto wifiTryMillis = 0UL;
|
||||||
|
|
||||||
|
auto wifiSsid = WIFI_SSID;
|
||||||
|
|
||||||
|
auto wifiPkey = WIFI_PKEY;
|
||||||
|
|
||||||
|
auto wifiHost = WIFI_HOST;
|
||||||
|
|
||||||
|
void wifiSetupOTA() {
|
||||||
|
ArduinoOTA.onStart([] {
|
||||||
|
info("beginning ota update...");
|
||||||
|
Serial.print("OTA-UPDATE: 0%");
|
||||||
|
});
|
||||||
|
ArduinoOTA.onProgress([](const unsigned done, const unsigned total) {
|
||||||
|
Serial.printf("\rOTA-UPDATE: %3.0f%%", 100.0 * done / total);
|
||||||
|
});
|
||||||
|
ArduinoOTA.onEnd([] {
|
||||||
|
Serial.print("\rOTA-UPDATE: COMPLETE\n");
|
||||||
|
info("OTA update complete");
|
||||||
|
});
|
||||||
|
ArduinoOTA.onError([](const ota_error_t errorCode) {
|
||||||
|
auto name = "[???]";
|
||||||
|
switch (errorCode) {
|
||||||
|
case OTA_AUTH_ERROR:
|
||||||
|
name = "AUTH";
|
||||||
|
break;
|
||||||
|
case OTA_BEGIN_ERROR:
|
||||||
|
name = "BEGIN";
|
||||||
|
break;
|
||||||
|
case OTA_CONNECT_ERROR:
|
||||||
|
name = "CONNECT";
|
||||||
|
break;
|
||||||
|
case OTA_RECEIVE_ERROR:
|
||||||
|
name = "RECEIVE";
|
||||||
|
break;
|
||||||
|
case OTA_END_ERROR:
|
||||||
|
name = "END";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Serial.printf("\nOTA-UPDATE: ERROR #%d=%s\n", errorCode, name);
|
||||||
|
error("OTA update failed: #%d=%s", errorCode, name);
|
||||||
|
});
|
||||||
|
ArduinoOTA.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
void wifiOff() {
|
||||||
|
info("wifi disabled");
|
||||||
|
wifiEnabled = false;
|
||||||
|
WiFi.disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
void wifiLoop() {
|
||||||
|
const auto currentState = WiFi.localIP() != 0;
|
||||||
|
if (wifiConnected != currentState) {
|
||||||
|
wifiConnected = currentState;
|
||||||
|
if (wifiConnected) {
|
||||||
|
info("wifi connected as %s", WiFi.localIP().toString().c_str());
|
||||||
|
wifiSetupOTA();
|
||||||
|
} else {
|
||||||
|
warn("wifi disconnected");
|
||||||
|
ArduinoOTA.end();
|
||||||
|
WiFi.disconnect();
|
||||||
|
}
|
||||||
|
} else if (!wifiConnected) {
|
||||||
|
if (!wifiEnabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (wifiTryMillis == 0 || millis() - wifiTryMillis >= WIFI_TIMEOUT_MILLIS) {
|
||||||
|
wifiTryMillis = millis();
|
||||||
|
WiFiClass::hostname(wifiHost);
|
||||||
|
WiFi.setAutoReconnect(true);
|
||||||
|
info(R"(connecting to SSID "%s" with hostname "%s")", wifiSsid, wifiHost);
|
||||||
|
WiFi.begin(wifiSsid, wifiPkey);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ArduinoOTA.handle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isWiFiConnected() {
|
||||||
|
return wifiConnected;
|
||||||
|
}
|
||||||
10
src/patrix/core/wifi.h
Normal file
10
src/patrix/core/wifi.h
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#ifndef PATRIX_WIFI_H
|
||||||
|
#define PATRIX_WIFI_H
|
||||||
|
|
||||||
|
void wifiOff();
|
||||||
|
|
||||||
|
void wifiLoop();
|
||||||
|
|
||||||
|
bool isWiFiConnected();
|
||||||
|
|
||||||
|
#endif
|
||||||
4
src/patrix/node/Node.cpp
Normal file
4
src/patrix/node/Node.cpp
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
#include <patrix/node/Node.h>
|
||||||
|
|
||||||
|
// ReSharper disable once CppUseAuto
|
||||||
|
Node patrixNode = patrixGetNode();
|
||||||
37
src/patrix/node/Node.h
Normal file
37
src/patrix/node/Node.h
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
#ifndef NODE_H
|
||||||
|
#define NODE_H
|
||||||
|
|
||||||
|
#include <ESPAsyncWebServer.h>
|
||||||
|
|
||||||
|
class Node {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
const bool waitForWiFi;
|
||||||
|
|
||||||
|
const bool waitForOTA;
|
||||||
|
|
||||||
|
const bool waitForClock;
|
||||||
|
|
||||||
|
explicit Node(const bool waitForWiFi, const bool waitForOTA, const bool waitForClock)
|
||||||
|
: waitForWiFi(waitForWiFi),
|
||||||
|
waitForOTA(waitForOTA),
|
||||||
|
waitForClock(waitForClock) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~Node() = default;
|
||||||
|
|
||||||
|
virtual void setup() {}
|
||||||
|
|
||||||
|
virtual void loop() {}
|
||||||
|
|
||||||
|
virtual void websocketEvent(AsyncWebSocket *socket, AsyncWebSocketClient *client, AwsEventType type, void *arg, unsigned char *message, unsigned length) {}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
extern Node patrixNode;
|
||||||
|
|
||||||
|
Node& patrixGetNode();
|
||||||
|
|
||||||
|
#endif
|
||||||
Loading…
Reference in New Issue
Block a user