mqtt and system tabs

This commit is contained in:
Svante Kaiser 2023-12-11 17:52:41 +03:00
parent 0c74411808
commit 3aabf4f8c1
21 changed files with 572 additions and 136 deletions

View File

@ -20,7 +20,6 @@ board_build.filesystem = littlefs
extra_scripts =
post:scripts/build_interface.py
lib_deps =
adafruit/Adafruit NeoPixel@^1.11.0
ottowinter/AsyncMqttClient-esphome@^0.8.6
ottowinter/ESPAsyncWebServer-esphome@^3.1.0
bblanchon/ArduinoJson@^6.21.3

View File

@ -1,10 +1,13 @@
#include "app/routes.h"
#include "infra/eth.h"
#include "infra/httpServer.h"
#include "infra/mqtt.h"
#include "infra/relay.h"
#include "infra/fs.h"
#include "config/config.h"
#include "utils/print.h"
#include "utils/settings.h"
#include "utils/utils.h"
void handleDoorOpen(AsyncWebServerRequest *request) {
relayTurnOn();
@ -31,7 +34,7 @@ void getFeatures(AsyncWebServerRequest *request) {
}
void intercomStatus(AsyncWebServerRequest* request) {
AsyncJsonResponse* response = new AsyncJsonResponse(false, MAX_AP_STATUS_SIZE);
AsyncJsonResponse* response = new AsyncJsonResponse(false, MAX_INTERCOM_STATUS_SIZE);
JsonObject root = response->getRoot();
root["status"] = 0;
@ -46,25 +49,8 @@ void intercomStatus(AsyncWebServerRequest* request) {
void networkStatus(AsyncWebServerRequest* request) {
AsyncJsonResponse* response = new AsyncJsonResponse(false, MAX_NETWORK_STATUS_SIZE);
JsonObject root = response->getRoot();
wl_status_t status = WL_CONNECTED;
root["status"] = (uint8_t)status;
if (status == WL_CONNECTED) {
root["local_ip"] = ETH.localIP().toString();
IPv6Address localIPv6 = ETH.localIPv6();
if (!(localIPv6 == IPv6Address()))
root["local_ip_v6"] = ETH.localIPv6().toString();
root["mac_address"] = ETH.macAddress();
root["full_duplex"] = ETH.fullDuplex();
root["link_speed"] = ETH.linkSpeed();
root["link_up"] = ETH.linkUp();
root["network_id"] = ETH.networkID();
root["subnet_mask"] = ETH.subnetMask().toString();
root["gateway_ip"] = ETH.gatewayIP().toString();
IPAddress dnsIP = ETH.dnsIP();
if (dnsIP)
root["dns_ip"] = dnsIP.toString();
}
getEthStatus(root);
response->setLength();
request->send(response);
@ -79,20 +65,14 @@ void scanNetworks(AsyncWebServerRequest* request) {
}
static void networkSettingsRead(AsyncWebServerRequest* request) {
AsyncJsonResponse* response = new AsyncJsonResponse(false, MAX_NETWORK_STATUS_SIZE);
AsyncJsonResponse* response = new AsyncJsonResponse(false, MAX_NETWORK_SETTINGS_SIZE);
DynamicJsonDocument doc(1024);
bool success = readJsonVariantFromFile(NETWORK_SETTINGS_PATH, doc);
JsonVariant root = doc.as<JsonVariant>();
if (!success) {
root["static_ip_config"] = false;
root["local_ip"] = ETH.localIP().toString();
root["gateway_ip"] = ETH.gatewayIP().toString();
root["subnet_mask"] = ETH.subnetMask().toString();
root["dns_ip"] = ETH.dnsIP().toString();
root["dns_ip_2"] = ETH.dnsIP(1).toString();
}
if (!success)
getDefaultEthConf(root);
response->setLength();
@ -101,24 +81,21 @@ static void networkSettingsRead(AsyncWebServerRequest* request) {
request->send(200, "application/json", jsonString.c_str());
}
static void networkSettingsUpdate(AsyncWebServerRequest* request, JsonVariant &json) {
if (!json.is<JsonVariant>()) {
static void networkSettingsUpdate(AsyncWebServerRequest *request,
uint8_t *data, size_t len, size_t index, size_t total) {
DynamicJsonDocument jsonDoc(MAX_NETWORK_SETTINGS_SIZE);
String jsonStr = requestDataToStr(data, len);
DeserializationError error = deserializeJson(jsonDoc, jsonStr);
if (!jsonDoc.is<JsonVariant>()) {
request->send(400);
return;
}
JsonVariant root = json.as<JsonVariant>();
JsonVariant root = jsonDoc.as<JsonVariant>();
bool success = writeJsonVariantToFile(NETWORK_SETTINGS_PATH, root);
setEthConfig(
root["static_ip_config"].as<bool>(),
root["local_ip"].as<String>(),
root["gateway_ip"].as<String>(),
root["subnet_mask"].as<String>(),
root["dns_ip"].as<String>()
);
if (success) {
String jsonString;
serializeJson(root, jsonString);
@ -126,6 +103,106 @@ static void networkSettingsUpdate(AsyncWebServerRequest* request, JsonVariant &j
}
else
request->send(500, "text/plain", "Network settings not updated");
configureEth(
root["static_ip_config"].as<bool>(),
root["local_ip"].as<String>(),
root["gateway_ip"].as<String>(),
root["subnet_mask"].as<String>(),
root["dns_ip"].as<String>()
);
}
static void mqttStatus(AsyncWebServerRequest* request) {
AsyncJsonResponse* response = new AsyncJsonResponse(false, MAX_MQTT_STATUS_SIZE);
JsonObject root = response->getRoot();
root["enabled"] = getMqttEnabled();
root["connected"] = getMqttClient().connected();
root["client_id"] = getMqttClient().getClientId();
root["disconnect_reason"] = (uint8_t)getMqttDisconnectReason();
response->setLength();
request->send(response);
}
static void mqttSettingsRead(AsyncWebServerRequest* request) {
AsyncJsonResponse* response = new AsyncJsonResponse(false, MAX_MQTT_SETTINGS_SIZE);
DynamicJsonDocument doc(1024);
bool success = readJsonVariantFromFile(MQTT_SETTINGS_PATH, doc);
JsonVariant root = doc.as<JsonVariant>();
if (!success)
getDefaultMqttConf(root);
response->setLength();
String jsonString;
serializeJson(root, jsonString);
request->send(200, "application/json", jsonString.c_str());
}
static void mqttSettingsUpdate(AsyncWebServerRequest *request,
uint8_t *data, size_t len, size_t index, size_t total) {
DynamicJsonDocument jsonDoc(MAX_MQTT_SETTINGS_SIZE);
String jsonStr = requestDataToStr(data, len);
DeserializationError error = deserializeJson(jsonDoc, jsonStr);
if (!jsonDoc.is<JsonVariant>() || error) {
request->send(400);
return;
}
JsonVariant root = jsonDoc.as<JsonVariant>();
bool fileLoaded = writeJsonVariantToFile(MQTT_SETTINGS_PATH, root);
bool enabled = root["enabled"].as<bool>();
bool mqttConfigured = configureMqtt(
root["enabled"].as<bool>(),
root["host"].as<String>(),
root["port"].as<uint16_t>(),
root["username"].as<String>(),
root["password"].as<String>(),
root["client_id"].as<String>(),
root["keep_alive"].as<uint16_t>(),
root["clean_session"].as<bool>(),
root["max_topic_length"].as<uint16_t>()
);
if (!fileLoaded) {
request->send(500, "text/plain", "MQTT settings not updated");
} else if (!mqttConfigured && enabled) {
request->send(404, "text/plain", "MQTT settings not configured");
} else {
String jsonString;
serializeJson(root, jsonString);
request->send(200, "application/json", jsonString.c_str());
}
}
void systemStatus(AsyncWebServerRequest* request) {
AsyncJsonResponse* response = new AsyncJsonResponse(false, MAX_ESP_STATUS_SIZE);
JsonObject root = response->getRoot();
root["esp_platform"] = "esp32";
root["max_alloc_heap"] = ESP.getMaxAllocHeap();
root["psram_size"] = ESP.getPsramSize();
root["free_psram"] = ESP.getFreePsram();
root["cpu_freq_mhz"] = ESP.getCpuFreqMHz();
root["free_heap"] = ESP.getFreeHeap();
root["sketch_size"] = ESP.getSketchSize();
root["free_sketch_space"] = ESP.getFreeSketchSpace();
root["sdk_version"] = ESP.getSdkVersion();
root["flash_chip_size"] = ESP.getFlashChipSize();
root["flash_chip_speed"] = ESP.getFlashChipSpeed();
root["fs_total"] = LittleFS.totalBytes();
root["fs_used"] = LittleFS.usedBytes();
response->setLength();
request->send(response);
}
void handleDoorStatus(AsyncWebServerRequest *request) {
@ -133,14 +210,25 @@ void handleDoorStatus(AsyncWebServerRequest *request) {
request->send(200, "text/plain", "OK");
}
void handleBodyRequest(AsyncWebServer& server, const char* uri, WebRequestMethodComposite method, \
void handleBodyRequest(AsyncWebServer& server, const char* uri, WebRequestMethodComposite method,
ArJsonRequestHandlerFunction onRequest) {
AsyncCallbackJsonWebHandler* handler = \
new AsyncCallbackJsonWebHandler(uri, networkSettingsUpdate);
new AsyncCallbackJsonWebHandler(uri, onRequest);
handler->setMethod(method);
server.addHandler(handler);
}
void restartNow(AsyncWebServerRequest *request) {
request->send(200, "text/plain", "OK");
ESP.restart();
}
void factoryReset(AsyncWebServerRequest *request) {
deleteFilesInDir(FS_CONFIG_DIRECTORY);
restartNow(request);
}
void initRoutes() {
AsyncWebServer& server = getServer();
server.on("/api/v1/door/open", handleDoorOpen);
@ -150,7 +238,15 @@ void initRoutes() {
server.on("/api/v1/networkStatus", networkStatus);
server.on("/api/v1/networkSettings", HTTP_GET, networkSettingsRead);
handleBodyRequest(server, "/api/v1/networkSettings", HTTP_POST, networkSettingsUpdate);
server.on("/api/v1/networkSettings", HTTP_POST, [](AsyncWebServerRequest *request) {}, NULL, networkSettingsUpdate);
server.on("/api/v1/intercomStatus", intercomStatus);
server.on("/api/v1/mqttStatus", mqttStatus);
server.on("/api/v1/mqttSettings", HTTP_GET, mqttSettingsRead);
server.on("/api/v1/mqttSettings", HTTP_POST, [](AsyncWebServerRequest *request) {}, NULL, mqttSettingsUpdate);
server.on("/api/v1/systemStatus", systemStatus);
server.on("/api/v1/restart", restartNow);
server.on("/api/v1/factoryReset", factoryReset);
}

View File

@ -1,4 +1,5 @@
#include <ArduinoJson.h>
#include <AsyncJson.h>
#include <FS.h>
void initRoutes();

View File

@ -1,11 +1,18 @@
#pragma once
#define DEFAULT_STATIC_LOCAL_IP IPAddress(192, 168, 1, 150)
#define DEFAULT_STATIC_GATEWAY IPAddress(192, 168, 1, 1)
#define DEFAULT_STATIC_SUBNET IPAddress(255, 255, 255, 0)
#define FACTORY_STATIC_LOCAL_IP IPAddress(192, 168, 1, 150)
#define FACTORY_STATIC_GATEWAY IPAddress(192, 168, 1, 1)
#define FACTORY_STATIC_SUBNET IPAddress(255, 255, 255, 0)
#define MQTT_HOST IPAddress(192, 168, 1, 173)
#define MQTT_PORT 1883
#define FACTORY_MQTT_ENABLED false
#define FACTORY_MQTT_HOST ""
#define FACTORY_MQTT_PORT 1883
#define FACTORY_MQTT_USERNAME ""
#define FACTORY_MQTT_PASSWORD ""
#define FACTORY_MQTT_CLIENT_ID "#{platform}-#{unique_id}"
#define FACTORY_MQTT_KEEP_ALIVE 16
#define FACTORY_MQTT_CLEAN_SESSION true
#define FACTORY_MQTT_MAX_TOPIC_LENGTH 128
#define SERIAL_NUMBER "4823"
@ -21,13 +28,18 @@
#define DOOR_SENS_PIN 114
#define DATA_PIN 12
#define DATA_PERIOD 240 // microseconds
#define DATA_PERIOD 120 // microseconds
#define PRINT_RAW_SIGNAL_FLAG 0
#define PRINT_RAW_SIGNAL_FLAG 1
#define MAX_FEATURES_SIZE 256
#define MAX_AP_STATUS_SIZE 1024
#define MAX_NETWORK_STATUS_SIZE 1024
#define MAX_NETWORK_SETTINGS_SIZE 1024
#define MAX_INTERCOM_STATUS_SIZE 1024
#define MAX_MQTT_STATUS_SIZE 1024
#define MAX_MQTT_SETTINGS_SIZE 1024
#define MAX_ESP_STATUS_SIZE 1024
#define FS_CONFIG_DIRECTORY "/config"
#define NETWORK_SETTINGS_PATH "/config/networkSettings.json"
#define MQTT_SETTINGS_PATH "/config/mqttSettings.json"

View File

@ -38,7 +38,7 @@ void receiveData(int data) {
previousData = data;
}
void changeState(State state, bool resetCountersFlag=true) {
void changeState(State state, bool resetCountersFlag) {
currentState = state;
switch (state) {
@ -140,3 +140,7 @@ void updateStateMachine(int data) {
}
}
}
void initStateMachine() {
changeState(CONNECTED);
}

View File

@ -19,5 +19,7 @@ enum State {
void resetCounters();
void updateStateMachine(int data);
void initStateMachine();
void changeState(State state, bool resetCountersFlag=true);
#endif // STATE_MACHINE_H

View File

@ -1,12 +1,11 @@
#include <ArduinoJson.h>
#include "infra/eth.h"
#include "infra/fs.h"
#include "infra/mqtt.h"
#include "config/config.h"
#include "utils/print.h"
extern bool eth_connected;
TimerHandle_t ethReconnectTimer;
wl_status_t status;
void WiFiEvent(WiFiEvent_t event)
{
@ -19,6 +18,7 @@ void WiFiEvent(WiFiEvent_t event)
break;
case ARDUINO_EVENT_ETH_CONNECTED:
Serial.println("ETH Connected");
status = WL_CONNECTED;
break;
case ARDUINO_EVENT_ETH_GOT_IP:
Serial.print("ETH MAC: ");
@ -31,17 +31,14 @@ void WiFiEvent(WiFiEvent_t event)
Serial.print(", ");
Serial.print(ETH.linkSpeed());
Serial.println("Mbps");
eth_connected = true;
delay(2000);
connectToMqtt();
break;
case ARDUINO_EVENT_ETH_DISCONNECTED:
Serial.println("ETH Disconnected");
eth_connected = false;
status = WL_DISCONNECTED;
break;
case ARDUINO_EVENT_ETH_STOP:
Serial.println("ETH Stopped");
eth_connected = false;
break;
default:
break;
@ -67,15 +64,12 @@ void WiFiEvent(WiFiEvent_t event)
Serial.print(", ");
Serial.print(ETH.linkSpeed());
Serial.println("Mbps");
eth_connected = true;
break;
case SYSTEM_EVENT_ETH_DISCONNECTED:
Serial.println("ETH Disconnected");
eth_connected = false;
break;
case SYSTEM_EVENT_ETH_STOP:
Serial.println("ETH Stopped");
eth_connected = false;
break;
default:
break;
@ -83,75 +77,7 @@ void WiFiEvent(WiFiEvent_t event)
#endif
}
bool loadEthConfig() {
DynamicJsonDocument doc(1024);
bool success = readJsonVariantFromFile(NETWORK_SETTINGS_PATH, doc);
if (!success)
return false;
JsonVariant root = doc.as<JsonVariant>();
setEthConfig(
root["static_ip_config"].as<bool>(),
root["local_ip"].as<String>(),
root["gateway_ip"].as<String>(),
root["subnet_mask"].as<String>(),
root["dns_ip"].as<String>()
);
return true;
}
void initEth() {
pinMode(NRST, OUTPUT);
digitalWrite(NRST, 0);
delay(200);
digitalWrite(NRST, 1);
delay(200);
digitalWrite(NRST, 0);
delay(200);
digitalWrite(NRST, 1);
delay(200);
ETH.begin(ETH_ADDR,
ETH_POWER_PIN,
ETH_MDC_PIN,
ETH_MDIO_PIN,
ETH_TYPE,
ETH_CLK_MODE);
if (!loadEthConfig() && DEFAULT_STATIC_LOCAL_IP) {
ETH.config(
DEFAULT_STATIC_LOCAL_IP,
DEFAULT_STATIC_GATEWAY,
DEFAULT_STATIC_SUBNET
);
}
}
void testClient(const char * host, uint16_t port)
{
Serial.print("\nconnecting to ");
Serial.println(host);
WiFiClient client;
if (!client.connect(host, port)) {
Serial.println("connection failed");
return;
}
client.printf("GET / HTTP/1.1\r\nHost: %s\r\n\r\n", host);
while (client.connected() && !client.available());
while (client.available()) {
Serial.write(client.read());
}
Serial.println("closing connection\n");
client.stop();
}
void setEthConfig(bool isStatic, String localIp, String gateway,
void configureEth(bool isStatic, String localIp, String gateway,
String subnet, String dns1, String dns2) {
IPAddress newLocalIp = IPAddress();
IPAddress newGateway = IPAddress();
@ -174,6 +100,120 @@ String subnet, String dns1, String dns2) {
newDns2
);
else {
//ESP.restart();
ETH.config((uint32_t)0, (uint32_t)0, (uint32_t)0);
}
}
void configureEth(JsonVariant& root) {
configureEth(
root["static_ip_config"].as<bool>(),
root["local_ip"].as<String>(),
root["gateway_ip"].as<String>(),
root["subnet_mask"].as<String>(),
root["dns_ip"].as<String>()
);
}
void getDefaultEthConf(JsonVariant& root) {
root["static_ip_config"] = true;
root["local_ip"] = ETH.localIP().toString();
root["gateway_ip"] = ETH.gatewayIP().toString();
root["subnet_mask"] = ETH.subnetMask().toString();
root["dns_ip"] = ETH.dnsIP().toString();
root["dns_ip_2"] = ETH.dnsIP(1).toString();
}
void getEthStatus(JsonObject& root) {
root["status"] = (uint8_t)status;
if (status == WL_CONNECTED) {
root["local_ip"] = ETH.localIP().toString();
IPv6Address localIPv6 = ETH.localIPv6();
if (!(localIPv6 == IPv6Address()))
root["local_ip_v6"] = ETH.localIPv6().toString();
root["mac_address"] = ETH.macAddress();
root["full_duplex"] = ETH.fullDuplex();
root["link_speed"] = ETH.linkSpeed();
root["link_up"] = ETH.linkUp();
root["network_id"] = ETH.networkID();
root["subnet_mask"] = ETH.subnetMask().toString();
root["gateway_ip"] = ETH.gatewayIP().toString();
IPAddress dnsIP = ETH.dnsIP();
if (dnsIP)
root["dns_ip"] = dnsIP.toString();
}
}
bool loadEthConfig() {
DynamicJsonDocument doc(1024);
bool success = readJsonVariantFromFile(NETWORK_SETTINGS_PATH, doc);
if (!success)
return false;
JsonVariant root = doc.as<JsonVariant>();
configureEth(root);
return true;
}
void initEth() {
pinMode(NRST, OUTPUT);
digitalWrite(NRST, 0);
delay(200);
digitalWrite(NRST, 1);
delay(200);
digitalWrite(NRST, 0);
delay(200);
digitalWrite(NRST, 1);
delay(200);
ETH.begin(ETH_ADDR,
ETH_POWER_PIN,
ETH_MDC_PIN,
ETH_MDIO_PIN,
ETH_TYPE,
ETH_CLK_MODE);
if (!loadEthConfig() && FACTORY_STATIC_LOCAL_IP) {
ETH.config(
FACTORY_STATIC_LOCAL_IP,
FACTORY_STATIC_GATEWAY,
FACTORY_STATIC_SUBNET
);
}
}
void testClient(const char * host, uint16_t port) {
Serial.print("\nconnecting to ");
Serial.println(host);
WiFiClient client;
if (!client.connect(host, port)) {
Serial.println("connection failed");
return;
}
client.printf("GET / HTTP/1.1\r\nHost: %s\r\n\r\n", host);
while (client.connected() && !client.available());
while (client.available()) {
Serial.write(client.read());
}
Serial.println("closing connection\n");
client.stop();
}
bool checkHost(const char* host, uint16_t port) {
WiFiClient client;
if (client.connect(host, port)) {
client.stop();
return true;
} else {
return false;
}
}

View File

@ -1,6 +1,7 @@
#pragma once
#include <ETH.h>
#include <ArduinoJson.h>
/*
* ETH_CLOCK_GPIO0_IN - default: external clock from crystal oscillator
@ -30,7 +31,9 @@
void WiFiEvent(WiFiEvent_t event);
void initEth();
void testClient(const char * host, uint16_t port);
void setEthConfig(
bool checkHost(const char* host, uint16_t port);
void getEthStatus(JsonObject& root);
void configureEth(
bool isStatic,
String localIp,
String gateway,
@ -38,3 +41,5 @@ void setEthConfig(
String dns1="",
String dns2=""
);
void configureEth(JsonVariant& root);
void getDefaultEthConf(JsonVariant& root);

View File

@ -24,7 +24,7 @@ bool readJsonVariantFromFile(const char* file_path, DynamicJsonDocument& jsonDoc
}
String fileContent = file.readString();
println("Reading config: ", fileContent);
println("Reading config: ", file_path, ", content:", fileContent);
DeserializationError error = deserializeJson(jsonDoc, fileContent);
@ -62,10 +62,32 @@ bool writeJsonVariantToFile(const char* file_path, JsonVariant& jsonVariant) {
String jsonString;
serializeJson(jsonVariant, jsonString);
println("Writing config: ", jsonString);
println("Writing config: ", file_path, ", content:", jsonString);
file.print(jsonString);
file.close();
return true;
}
void deleteFilesInDir(const char* path) {
File dir = LittleFS.open(path);
if (!dir.isDirectory()) {
Serial.println("Not a directory");
return;
}
File file = dir.openNextFile();
while (file) {
String fullPath = String(path) + '/' + String(file.name());
if (file.isDirectory()) {
deleteFilesInDir(fullPath.c_str());
} else {
file.close();
LittleFS.remove(fullPath);
}
file = dir.openNextFile();
}
dir.close();
}

View File

@ -5,3 +5,4 @@
void initFileSystem();
bool readJsonVariantFromFile(const char* filename, DynamicJsonDocument& jsonDoc);
bool writeJsonVariantToFile(const char* filename, JsonVariant& jsonObj);
void deleteFilesInDir(const char* path);

0
src/infra/intercom.cpp Normal file
View File

0
src/infra/intercom.h Normal file
View File

View File

@ -1,16 +1,23 @@
#include <Arduino.h>
#include <ETH.h>
#include "config/config.h"
#include "infra/mqtt.h"
#include "infra/eth.h"
#include "infra/fs.h"
#include "utils/settings.h"
AsyncMqttClient mqttClient;
TimerHandle_t mqttReconnectTimer;
AsyncMqttClientDisconnectReason mqttDisconnectReason;
void connectToMqtt() {
Serial.println("Connecting to MQTT...");
mqttClient.connect();
}
DynamicJsonDocument mqttConf(1024);
bool mqttConnected = false;
bool mqttEnabled = true;
// Pointers to hold retained copies of the mqtt client connection strings.
// This is required as AsyncMqttClient holds refrences to the supplied connection strings.
char* retainedHost;
char* retainedClientId;
char* retainedUsername;
char* retainedPassword;
void publishToMQTT(const char* topic, const char* message) {
mqttClient.publish(topic, 2, true, message);
@ -38,6 +45,8 @@ void onMqttConnect(bool sessionPresent) {
Serial.print("Session present: ");
Serial.println(sessionPresent);
mqttConnected = true;
publishToMQTT(MAC_ADDRESS_MQTT_TOPIC, ETH.macAddress().c_str());
publishToMQTT(IP_ADDRESS_MQTT_TOPIC, ETH.localIP().toString().c_str());
publishToMQTT(SERIAL_NUMBER_MQTT_TOPIC, SERIAL_NUMBER);
@ -49,9 +58,8 @@ void onMqttConnect(bool sessionPresent) {
void onMqttDisconnect(AsyncMqttClientDisconnectReason reason) {
Serial.println("Disconnected from MQTT.");
if (WiFi.isConnected()) {
xTimerStart(mqttReconnectTimer, 0);
}
mqttConnected = false;
mqttDisconnectReason = reason;
}
void onMqttSubscribe(uint16_t packetId, uint8_t qos) {
@ -74,14 +82,157 @@ void onMqttPublish(uint16_t packetId) {
Serial.println(packetId);
}
void initMQTT() {
mqttReconnectTimer = xTimerCreate("mqttTimer", pdMS_TO_TICKS(2000), pdFALSE, (void*)0, reinterpret_cast<TimerCallbackFunction_t>(connectToMqtt));
AsyncMqttClient& getMqttClient() {
return mqttClient;
}
AsyncMqttClientDisconnectReason& getMqttDisconnectReason() {
return mqttDisconnectReason;
}
bool getMqttConnected() {
return mqttConnected;
}
bool getMqttEnabled() {
return mqttEnabled;
}
/**
* Retains a copy of the cstr provided in the pointer provided using dynamic allocation.
*
* Frees the pointer before allocation and leaves it as nullptr if cstr == nullptr.
*/
static char* retainCstr(const char* cstr, char** ptr) {
// free up previously retained value if exists
free(*ptr);
*ptr = nullptr;
// dynamically allocate and copy cstr (if non null)
if (cstr != nullptr) {
*ptr = (char*)malloc(strlen(cstr) + 1);
strcpy(*ptr, cstr);
}
// return reference to pointer for convenience
return *ptr;
}
void setMqttServer(String host, uint16_t port) {
Serial.print("MQTT server: ");
Serial.print(host);
Serial.print(":");
Serial.println(port);
mqttClient.setServer(retainCstr(host.c_str(), &retainedHost), port);
}
bool checkMqttHost(String host, uint16_t port) {
if (!checkHost(host.c_str(), port)) {
Serial.print("MQTT host is not responding: ");
Serial.print(host);
Serial.print(":");
Serial.println(port);
return false;
}
return true;
}
bool configureMqtt(bool enabled, String host, uint16_t port, String username, String password,
String clientId, uint16_t keepAlive, bool cleanSession, uint16_t maxTopicLength) {
if (!mqttEnabled) {
mqttEnabled = enabled;
return checkMqttHost(host, port);
}
mqttEnabled = enabled;
mqttClient.disconnect();
if (!checkMqttHost(host, port))
return false;
if (enabled) {
Serial.println(F("Reconnecting to MQTT..."));
setMqttServer(host, port);
if (username.length() > 0) {
mqttClient.setCredentials(
retainCstr(username.c_str(), &retainedUsername),
retainCstr(password.length() > 0 ? password.c_str() : nullptr, &retainedPassword));
} else {
mqttClient.setCredentials(retainCstr(nullptr, &retainedUsername), retainCstr(nullptr, &retainedPassword));
}
mqttClient.setClientId(retainCstr(clientId.c_str(), &retainedClientId));
mqttClient.setKeepAlive(keepAlive);
mqttClient.setCleanSession(cleanSession);
mqttClient.setMaxTopicLength(maxTopicLength);
mqttClient.connect();
return true;
}
return false;
}
bool configureMqtt(JsonVariant& root) {
configureMqtt(
root["enabled"].as<bool>(),
root["host"].as<String>(),
root["port"].as<uint16_t>(),
root["username"].as<String>(),
root["password"].as<String>(),
root["client_id"].as<String>(),
root["keep_alive"].as<uint16_t>(),
root["clean_session"].as<bool>(),
root["max_topic_length"].as<uint16_t>()
);
}
void getDefaultMqttConf(JsonVariant& root) {
root["enabled"] = FACTORY_MQTT_ENABLED;
root["host"] = FACTORY_MQTT_HOST;
root["port"] = FACTORY_MQTT_PORT;
root["username"] = FACTORY_MQTT_USERNAME;
root["password"] = FACTORY_MQTT_PASSWORD;
root["client_id"] = formatSetting(FACTORY_MQTT_CLIENT_ID);
root["keep_alive"] = FACTORY_MQTT_KEEP_ALIVE;
root["clean_session"] = FACTORY_MQTT_CLEAN_SESSION;
root["max_topic_length"] = FACTORY_MQTT_MAX_TOPIC_LENGTH;
}
bool loadMqttConfig() {
bool success = readJsonVariantFromFile(MQTT_SETTINGS_PATH, mqttConf);
if (!success)
return false;
}
void reconnectMQTTIfNeeded() {
if (!mqttClient.connected()) {
if (mqttConf.isNull())
loadMqttConfig();
JsonVariant root = mqttConf.as<JsonVariant>();
if (!mqttConf.is<JsonVariant>())
return;
bool mqttConfigured = configureMqtt(
root["enabled"].as<bool>(),
root["host"].as<String>(),
root["port"].as<uint16_t>(),
root["username"].as<String>(),
root["password"].as<String>(),
root["client_id"].as<String>(),
root["keep_alive"].as<uint16_t>(),
root["clean_session"].as<bool>(),
root["max_topic_length"].as<uint16_t>()
);
}
}
void initMQTT() {
mqttClient.onConnect(onMqttConnect);
mqttClient.onDisconnect(onMqttDisconnect);
mqttClient.onSubscribe(onMqttSubscribe);
mqttClient.onUnsubscribe(onMqttUnsubscribe);
//mqttClient.onMessage(onMqttMessage);
//mqttClient.onPublish(onMqttPublish);
mqttClient.setServer(MQTT_HOST, MQTT_PORT);
}

View File

@ -1,17 +1,37 @@
#pragma once
#include <Arduino.h>
#include <ArduinoJson.h>
#include <ETH.h>
#include <HTTPClient.h>
#include <AsyncMqttClient.h>
void connectToMqtt();
void onMqttConnect(bool sessionPresent);
void onMqttDisconnect(AsyncMqttClientDisconnectReason reason);
void onMqttSubscribe(uint16_t packetId, uint8_t qos);
void onMqttUnsubscribe(uint16_t packetId);
void onMqttPublish(uint16_t packetId);
void sendDataToMQTT(int flatNumber);
void publishToMQTT(const char* topic, const char* message);
void publishToMQTT(const char* topic, int message);
void publishToMQTT(const char* topic, float message);
void publishToMQTT(const char* topic, bool message);
void initMQTT();
AsyncMqttClient& getMqttClient();
AsyncMqttClientDisconnectReason& getMqttDisconnectReason();
bool getMqttConnected();
bool getMqttEnabled();
bool configureMqtt(
bool enabled,
String host,
uint16_t port,
String username,
String password,
String clientId,
uint16_t keepAlive,
bool cleanSession,
uint16_t maxTopicLength
);
void getDefaultMqttConf(JsonVariant& root);
bool configureMqtt(JsonVariant& root);
void reconnectMQTTIfNeeded();

0
src/infra/system.cpp Normal file
View File

0
src/infra/system.h Normal file
View File

View File

@ -22,10 +22,10 @@
uint32_t lastMillis;
uint64_t lastMicros;
bool eth_connected = false;
int data = 0;
bool flag = false;
void setup() {
Serial.begin(115200);
@ -44,6 +44,7 @@ void setup() {
relayTurnOn();
delay(2000);
//initStateMachine();
initFileSystem();
initEth();
initMQTT();
@ -59,11 +60,21 @@ void loop() {
if (PRINT_RAW_SIGNAL_FLAG)
printf("{}", data);
lastMillis = micros() + DATA_PERIOD;
lastMicros = micros() + DATA_PERIOD;
}
if (eth_connected) {
if (lastMillis < millis()) {;
if (true) {
if (lastMillis < millis()) {
if (getMqttEnabled() && !getMqttConnected()) {
reconnectMQTTIfNeeded();
}
/*if (flag) {
changeState(RECEIVING_DATA);
flag = false;
} else {
changeState(CONNECTED);
flag = true;
}*/
lastMillis = millis() + 3000;
}
}

51
src/utils/settings.cpp Normal file
View File

@ -0,0 +1,51 @@
#include "utils/settings.h"
#ifdef ESP32
const String PLATFORM = "esp32";
#elif defined(ESP8266)
const String PLATFORM = "esp8266";
#endif
/**
* Returns a new string after replacing each instance of the pattern with a value generated by calling the provided
* callback.
*/
String replaceEach(String value, String pattern, String (*generateReplacement)()) {
while (true) {
int index = value.indexOf(pattern);
if (index == -1) {
break;
}
value = value.substring(0, index) + generateReplacement() + value.substring(index + pattern.length());
}
return value;
}
/**
* Generates a random number, encoded as a hex string.
*/
String getRandom() {
return String(random(2147483647), HEX);
}
/**
* Uses the station's MAC address to create a unique id for each device.
*/
String getUniqueId() {
uint8_t mac[6];
#ifdef ESP32
esp_read_mac(mac, ESP_MAC_WIFI_STA);
#elif defined(ESP8266)
wifi_get_macaddr(STATION_IF, mac);
#endif
char macStr[13] = {0};
sprintf(macStr, "%02x%02x%02x%02x%02x%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
return String(macStr);
}
String formatSetting(String value) {
value = replaceEach(value, "#{random}", getRandom);
value.replace("#{unique_id}", getUniqueId());
value.replace("#{platform}", PLATFORM);
return value;
}

5
src/utils/settings.h Normal file
View File

@ -0,0 +1,5 @@
#pragma once
#include <Arduino.h>
String formatSetting(String value);

11
src/utils/utils.cpp Normal file
View File

@ -0,0 +1,11 @@
#include "utils/utils.h"
String requestDataToStr(uint8_t *data, size_t len) {
String str = "";
for (size_t i = 0; i < len; i++) {
str += (char)data[i];
}
return str;
}

5
src/utils/utils.h Normal file
View File

@ -0,0 +1,5 @@
#pragma once
#include <Arduino.h>
String requestDataToStr(uint8_t *data, size_t len);