[UNTESTED] Patrix, log, console, wifi, mqtt

This commit is contained in:
Patrick Haßel 2024-04-09 16:23:53 +02:00
commit e0e80dedad
13 changed files with 460 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
.pio
.idea

21
lib/patrix/Patrix.cpp Normal file
View File

@ -0,0 +1,21 @@
#include <Arduino.h>
#include <Patrix.h>
#include <wifi.h>
#include <console.h>
#include "mqtt.h"
#include "log.h"
void setup() {
delay(500);
Serial.begin(115200);
info("MAIN", "\n\n\nStartup...");
mqttSetup();
wifiSetup();
patrixSetup();
}
void loop() {
consoleLoop();
mqttLoop();
patrixLoop();
}

8
lib/patrix/Patrix.h Normal file
View File

@ -0,0 +1,8 @@
#ifndef SENSOR3_PATRIX_H
#define SENSOR3_PATRIX_H
void patrixSetup();
void patrixLoop();
#endif

74
lib/patrix/console.cpp Normal file
View File

@ -0,0 +1,74 @@
#include <console.h>
#include <HardwareSerial.h>
#include <log.h>
#include <Esp.h>
#include <WiFi.h>
#include "mqtt.h"
#include "wifi.h"
char consoleBuffer[64] = "";
char *consoleBufferW = consoleBuffer;
void consoleLoop() {
uint8_t i = 0;
while (Serial.available() > 0 && i++ < 100) {
const char symbol = static_cast<char>(Serial.read());
if (symbol == '\n' || symbol == '\r') {
if (*consoleBuffer != 0) {
Serial.print('\n');
info("CONSOLE", "> %s", consoleBuffer);
consoleHandle(consoleBuffer);
consoleBufferW = consoleBuffer;
*consoleBufferW = 0;
}
} else if (symbol == '\b') {
if (consoleBufferW > consoleBuffer) {
consoleBufferW--;
*consoleBufferW = 0;
Serial.print("\b \b");
}
} else {
if (consoleBufferW < consoleBuffer + sizeof consoleBuffer - 1) {
*consoleBufferW = symbol;
consoleBufferW++;
*consoleBufferW = 0;
Serial.print("\r");
Serial.print(consoleBuffer);
} else {
Serial.print("\nCONSOLE BUFFER OVERFLOW\n");
Serial.print(consoleBuffer);
}
}
}
}
void consoleHandle(const char *cmd) {
if (strcmp(cmd, "help") == 0) {
info("CONSOLE", "help : print this help");
info("CONSOLE", "net : print network info");
info("CONSOLE", "reboot : reboot system");
info("CONSOLE", "wifi : reconnect wifi");
info("CONSOLE", "mqtt : reconnect mqtt");
} else if (strcmp(cmd, "net") == 0) {
info("CONSOLE", "MAC: %17s", WiFi.macAddress().c_str());
info("CONSOLE", "IP: %17s", WiFi.localIP().toString().c_str());
info("CONSOLE", "Gateway: %17s", WiFi.gatewayIP().toString().c_str());
info("CONSOLE", "SSID: %17s", WiFi.SSID().c_str());
info("CONSOLE", "BSSID: %17s", WiFi.BSSIDstr().c_str());
info("CONSOLE", "RSSI: %17d", WiFi.RSSI());
} else if (strcmp(cmd, "reboot") == 0) {
info("CONSOLE", "Rebooting...");
delay(500);
yield();
ESP.restart();
} else if (strcmp(cmd, "wifi") == 0) {
info("CONSOLE", "Reconnecting WiFi...");
wifiConnect();
} else if (strcmp(cmd, "mqtt") == 0) {
info("CONSOLE", "Reconnecting MQTT...");
mqttDisconnect();
} else {
info("CONSOLE", "Unknown command: %s", cmd);
}
}

8
lib/patrix/console.h Normal file
View File

@ -0,0 +1,8 @@
#ifndef SENSOR3_CONSOLE_H
#define SENSOR3_CONSOLE_H
void consoleLoop();
void consoleHandle(const char *cmd);
#endif

41
lib/patrix/log.cpp Normal file
View File

@ -0,0 +1,41 @@
#include <HardwareSerial.h>
#include "log.h"
#include "mqtt.h"
#define SEPARATOR " | "
void getDateTime(char *buffer, size_t size) {
time_t now;
time(&now);
tm time{};
localtime_r(&now, &time);
strftime(buffer, size, "%Y-%m-%d %H:%M:%S %z", &time);
}
void log(const char *level, const char *module, const char *format, ...) {
char datetime[26];
getDateTime(datetime, sizeof datetime);
va_list vl;
va_start(vl, format);
char message[500];
vsnprintf(message, sizeof message, format, vl);
va_end(vl);
const size_t len = Serial.print(datetime) + Serial.print(SEPARATOR) + Serial.print(module) + Serial.print(SEPARATOR) + Serial.print(level) + Serial.print(SEPARATOR) + Serial.print(message) + Serial.println();
mqttLog(len, SEPARATOR, datetime, module, level, message);
}
void info(const char *module, const char *format, ...) {
va_list vl;
va_start(vl, format);
log("INFO", module, format, vl);
va_end(vl);
}
void error(const char *module, const char *format, ...) {
va_list vl;
va_start(vl, format);
log("ERROR", module, format, vl);
va_end(vl);
}

8
lib/patrix/log.h Normal file
View File

@ -0,0 +1,8 @@
#ifndef SENSOR3_LOG_H
#define SENSOR3_LOG_H
void info(const char *module, const char *format, ...);
void error(const char *module, const char *format, ...);
#endif

79
lib/patrix/mqtt.cpp Normal file
View File

@ -0,0 +1,79 @@
#include "mqtt.h"
#include <WiFiClient.h>
#include <WiFi.h>
#include "log.h"
#include "PubSubClient.h"
#include "console.h"
#define CONNECT_TIMEOUT_MILLISECONDS 5000
unsigned long mqttLastConnectTry = 0;
WiFiClient espClient;
PubSubClient mqtt(espClient);
char logTopic[32] = "log/UNSET";
char cmdTopic[64] = "cmd/UNSET";
void mqttSetup() {
snprintf(logTopic, sizeof logTopic, "%s/%s", "log", HOSTNAME);
snprintf(cmdTopic, sizeof cmdTopic, "%s/%s", "cmd", HOSTNAME);
}
void mqttLog(const size_t len, const char *separator, const char *datetime, const char *module, const char *level, const char *message) {
if (mqtt.beginPublish(logTopic, len, false)) {
mqtt.print(datetime);
mqtt.print(separator);
mqtt.print(module);
mqtt.print(separator);
mqtt.print(level);
mqtt.print(separator);
mqtt.print(message);
mqtt.endPublish();
}
}
void mqttDisconnect() {
mqtt.disconnect();
}
void mqttLoop() {
if (WiFi.localIP() != 0 && !mqtt.connected() && (mqttLastConnectTry == 0 || millis() - mqttLastConnectTry > CONNECT_TIMEOUT_MILLISECONDS)) {
mqttLastConnectTry = millis();
mqtt.setServer("10.0.0.50", 1883);
if (!mqtt.connect(HOSTNAME, logTopic, 0, false, "disconnected")) {
Serial.printf("failed to connect\n");
return;
}
info("MQTT", "connected");
mqtt.setBufferSize(512);
mqtt.subscribe(cmdTopic);
mqtt.setCallback([](const char *topic, const uint8_t *bytes, const unsigned int length) {
char content[64];
if (length > sizeof content - 1) {
error("MQTT", "RECEIVE BUFFER OVERFLOW");
return;
}
memcpy(content, bytes, length);
content[length] = 0;
if (strcmp(topic, cmdTopic) == 0) {
info("MQTT", "CMD > %s", content);
consoleHandle(content);
}
});
}
mqtt.loop();
}
bool mqttPublish(const char *topic, const JsonDocument &doc) {
if (!mqtt.connected()) {
return false;
}
char buffer[512];
const size_t size = serializeJson(doc, buffer);
return mqtt.publish(topic, buffer, size);
}

16
lib/patrix/mqtt.h Normal file
View File

@ -0,0 +1,16 @@
#ifndef SENSOR3_MQTT_H
#define SENSOR3_MQTT_H
#include <ArduinoJson.h>
void mqttSetup();
void mqttLoop();
void mqttDisconnect();
void mqttLog(size_t len, const char *separator, const char *datetime, const char *module, const char *level, const char *message);
bool mqttPublish(const char *topic, const JsonDocument &doc);
#endif

159
lib/patrix/wifi.cpp Normal file
View File

@ -0,0 +1,159 @@
#include "wifi.h"
#include "log.h"
#include <WiFi.h>
#include <ArduinoOTA.h>
#define NTP_SERVER "pool.ntp.org"
#define TIMEZONE_OFFSET 3600
#define DST_OFFSET 3600
#define MIN_EPOCH_SECONDS 1712675973
uint8_t otaLastLogStep = 0;
bool wifiConnected = false;
bool timeSet = false;
time_t preTimeOffset = 0;
unsigned long wifiLastConnectTry = 0;
void otaSetup();
void bootDelay();
void wifiSetup() {
WiFi.softAPdisconnect();
WiFi.setAutoReconnect(false);
wifiConnect();
otaSetup();
configTime(TIMEZONE_OFFSET, DST_OFFSET, NTP_SERVER, WiFi.gatewayIP().toString().c_str());
bootDelay();
}
void wifiConnect() {
WiFi.disconnect();
yield();
wifiLastConnectTry = millis();
info("WIFI", "Connecting: %s", WIFI_SSID);
WiFi.begin(WIFI_SSID, WIFI_PKEY);
yield();
}
void otaSetup() {
ArduinoOTA.onStart([] {
info("OTA", "Start!");
otaLastLogStep = 0;
});
ArduinoOTA.onProgress([](unsigned int received, unsigned int total) {
uint8_t currentStep = 20 * received / total;
if (otaLastLogStep != currentStep) {
info("OTA", "Progress: %3d%%", currentStep * 5);
otaLastLogStep = currentStep;
}
});
ArduinoOTA.onEnd([] {
info("OTA", "Complete!");
});
ArduinoOTA.onError([](ota_error_t e) {
const char *name;
switch (e) {
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;
default:
name = "[???]";
break;
}
error("OTA", name);
});
ArduinoOTA.begin();
}
void bootDelay() {
#ifdef BOOT_DELAY
info("BOOT DELAY", "Waiting for WiFi...");
while ((uint32_t) WiFi.localIP() == 0) {
delay(100);
yield();
wifiLoop();
}
info("BOOT DELAY", "WiFi connected!");
unsigned long begin = millis();
while (millis() - begin < 10000) {
delay(100);
yield();
wifiLoop();
}
info("BOOT DELAY", "Completed.");
#endif
}
void wifiLoop() {
const time_t epochSeconds = getTime();
if (!timeSet) {
timeSet = epochSeconds >= MIN_EPOCH_SECONDS;
if (!timeSet) {
preTimeOffset = epochSeconds;
} else {
info("NTP", "Time set!");
}
}
const bool hasIp = static_cast<uint32_t>(WiFi.localIP()) != 0;
if (wifiConnected) {
if (!hasIp) {
wifiConnected = false;
info("WIFI", "Disconnected!");
wifiConnect();
}
} else {
if (hasIp) {
wifiConnected = true;
info("WIFI", "Connected as: %s", WiFi.localIP().toString().c_str());
} else if (millis() - wifiLastConnectTry > 10000) {
info("WIFI", "Timeout!");
wifiConnect();
}
}
}
bool isTimeSet() {
return timeSet;
}
time_t getTime() {
time_t epochSeconds;
time(&epochSeconds);
return epochSeconds;
}
time_t correctTime(const time_t value) {
if (!timeSet) {
return value;
}
if (value < MIN_EPOCH_SECONDS) {
return getTime() - preTimeOffset + value;
}
return value;
}
bool isOlderThan(time_t time, time_t seconds) {
return getTime() > correctTime(time) + seconds;
}

20
lib/patrix/wifi.h Normal file
View File

@ -0,0 +1,20 @@
#ifndef GAERBOX_WIFI_H
#define GAERBOX_WIFI_H
#include <ctime>
void wifiSetup();
void wifiLoop();
void wifiConnect();
bool isTimeSet();
time_t getTime();
time_t correctTime(time_t value);
bool isOlderThan(time_t time, time_t seconds);
#endif

15
platformio.ini Normal file
View File

@ -0,0 +1,15 @@
[env:Fermenter]
platform = espressif32
board = esp32dev
framework = arduino
upload_port = /dev/ttyUSB0
upload_speed = 921600
;upload_protocol = espota
;upload_port =
;upload_flags = --auth=OtaAuthPatrixFermenter
monitor_port = /dev/ttyUSB0
monitor_speed = 115200
monitor_filters = esp32_exception_decoder
build_flags = -D HOSTNAME=\"Fermenter\" -D WIFI_SSID=\"HappyNet\" -D WIFI_PKEY=\"1Grausame!Sackratte7\" -D OTA_PASSWORD=\"OtaAuthPatrixFermenter\" -D BOOT_DELAY=true
lib_deps = knolleary/PubSubClient
bblanchon/ArduinoJson

9
src/Fermenter.cpp Normal file
View File

@ -0,0 +1,9 @@
#include <Patrix.h>
void patrixSetup() {
}
void patrixLoop() {
}