Compare commits
15 Commits
448e1bad15
...
DEPLOYED
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4a1e944ea2 | ||
|
|
a1a66ebf8e | ||
|
|
b19ed89158 | ||
|
|
25251785fa | ||
| 0e842294be | |||
| 57957740d9 | |||
|
|
25aa2d6cb6 | ||
|
|
eaa643bf3c | ||
|
|
abe0cb0839 | ||
|
|
fc2316b0f2 | ||
| fa1b288f4d | |||
|
|
1110648978 | ||
|
|
581eca124e | ||
|
|
1d1eb6fbfa | ||
|
|
ad90702ab6 |
BIN
docs/ESP32_clock_adjust.xlsx
Normal file
BIN
docs/ESP32_clock_adjust.xlsx
Normal file
Binary file not shown.
@@ -82,7 +82,7 @@
|
|||||||
"cmd": "setCronJob",
|
"cmd": "setCronJob",
|
||||||
"params": {
|
"params": {
|
||||||
"name": "nomedeljob",
|
"name": "nomedeljob",
|
||||||
"timeStr": "* * * 10,45 5 *",
|
"cronExpr": "* * * 10,45 5 *",
|
||||||
"action": "qua ci va un dizionario come se arrivasse da mqtt, cosi li interpreto alla stessa maniera"
|
"action": "qua ci va un dizionario come se arrivasse da mqtt, cosi li interpreto alla stessa maniera"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
54
esp32-s3-waveshare8.json
Normal file
54
esp32-s3-waveshare8.json
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
{
|
||||||
|
"build": {
|
||||||
|
"arduino": {
|
||||||
|
"ldscript": "esp32s3_out.ld",
|
||||||
|
"partitions": "app3M_fat9M_16MB.csv"
|
||||||
|
},
|
||||||
|
"core": "esp32",
|
||||||
|
"extra_flags": [
|
||||||
|
"-DARDUINO_ESP32S3_DEV",
|
||||||
|
"-DARDUINO_USB_MODE=1",
|
||||||
|
"-DARDUINO_RUNNING_CORE=1",
|
||||||
|
"-DARDUINO_EVENT_RUNNING_CORE=1",
|
||||||
|
"-DARDUINO_USB_CDC_ON_BOOT=1"
|
||||||
|
],
|
||||||
|
"partitions": "app3M_fat9M_16MB.csv",
|
||||||
|
"f_cpu": "240000000L",
|
||||||
|
"f_flash": "80000000L",
|
||||||
|
"flash_mode": "qio",
|
||||||
|
"hwids": [
|
||||||
|
[
|
||||||
|
"0x303A",
|
||||||
|
"0x1001"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"mcu": "esp32s3",
|
||||||
|
"variant": "esp32s3"
|
||||||
|
},
|
||||||
|
"connectivity": [
|
||||||
|
"bluetooth",
|
||||||
|
"wifi",
|
||||||
|
"ethernet"
|
||||||
|
],
|
||||||
|
"debug": {
|
||||||
|
"default_tool": "esp-builtin",
|
||||||
|
"onboard_tools": [
|
||||||
|
"esp-builtin"
|
||||||
|
],
|
||||||
|
"openocd_target": "esp32s3.cfg"
|
||||||
|
},
|
||||||
|
"frameworks": [
|
||||||
|
"arduino",
|
||||||
|
"espidf"
|
||||||
|
],
|
||||||
|
"name": "Espressif ESP32-S3-Waveshare_8RO-8DI",
|
||||||
|
"upload": {
|
||||||
|
"flash_size": "16MB",
|
||||||
|
"maximum_ram_size": 327680,
|
||||||
|
"maximum_size": 16777216,
|
||||||
|
"require_upload_port": true,
|
||||||
|
"speed": 921600
|
||||||
|
},
|
||||||
|
"url": "https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/hw-reference/esp32s3/user-guide-devkitm-1.html",
|
||||||
|
"vendor": "Espressif"
|
||||||
|
}
|
||||||
@@ -5,39 +5,7 @@
|
|||||||
#include <DebugLog.h>
|
#include <DebugLog.h>
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <ArduinoJson.h>
|
#include <ArduinoJson.h>
|
||||||
#include <FFat.h>
|
#include <fsmount.h>
|
||||||
|
|
||||||
#include <mutex>
|
|
||||||
|
|
||||||
class FSmount
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
FSmount()
|
|
||||||
{
|
|
||||||
if (!FFat.begin(false))
|
|
||||||
{
|
|
||||||
LOG_ERROR("Unable to mount filesystem without formatting");
|
|
||||||
if (!FFat.begin(true))
|
|
||||||
{
|
|
||||||
LOG_ERROR("Formatted and mounted filesystem");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG_INFO("Local Filesystem Mounted Correctly");
|
|
||||||
const auto totalBytes = FFat.totalBytes();
|
|
||||||
const auto freeBytes = FFat.freeBytes();
|
|
||||||
const auto usedBytes = FFat.usedBytes();
|
|
||||||
const auto mountPoint = FFat.mountpoint();
|
|
||||||
LOG_INFO("Local filesystem, total", totalBytes / 1024, "KB - used", usedBytes / 1024, "KB - free", freeBytes / 1024, "KB");
|
|
||||||
LOG_INFO("Local filesystem, mountpoint", mountPoint);
|
|
||||||
}
|
|
||||||
|
|
||||||
~FSmount()
|
|
||||||
{
|
|
||||||
FFat.end(); // unmout filesystem to avoid corruption
|
|
||||||
LOG_INFO("Local Filesystem Unmounted Correctly");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class Config
|
class Config
|
||||||
{
|
{
|
||||||
@@ -88,7 +56,7 @@ public:
|
|||||||
file.close(); // close config file before unmounting filesystem
|
file.close(); // close config file before unmounting filesystem
|
||||||
};
|
};
|
||||||
|
|
||||||
ArduinoJson::JsonDocument& getConfig()
|
ArduinoJson::JsonDocument &getConfig()
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(m_mutex);
|
std::lock_guard<std::mutex> lock(m_mutex);
|
||||||
serialize();
|
serialize();
|
||||||
@@ -184,6 +152,7 @@ private:
|
|||||||
ntp["timezone"] = m_ntpTimezone;
|
ntp["timezone"] = m_ntpTimezone;
|
||||||
ntp["updateInterval"] = m_ntpUpdateInterval;
|
ntp["updateInterval"] = m_ntpUpdateInterval;
|
||||||
ntp["retries"] = m_ntpRetries;
|
ntp["retries"] = m_ntpRetries;
|
||||||
|
ntp["ntpRtcOffsetRegister"] = m_ntpRtcOffsetRegister;
|
||||||
};
|
};
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -252,9 +221,10 @@ private:
|
|||||||
{
|
{
|
||||||
auto ntp = m_configJson["ntp"];
|
auto ntp = m_configJson["ntp"];
|
||||||
m_ntpPool = ntp["pool"].as<std::string>();
|
m_ntpPool = ntp["pool"].as<std::string>();
|
||||||
m_ntpTimezone = ntp["timezone"].as<uint16_t>();
|
m_ntpTimezone = ntp["timezone"].as<int8_t>();
|
||||||
m_ntpUpdateInterval = ntp["updateInterval"].as<uint16_t>();
|
m_ntpUpdateInterval = ntp["updateInterval"].as<uint16_t>();
|
||||||
m_ntpRetries = ntp["retries"].as<uint8_t>();
|
m_ntpRetries = ntp["retries"].as<uint8_t>();
|
||||||
|
m_ntpRtcOffsetRegister = ntp["ntpRtcOffsetRegister"].as<uint8_t>();
|
||||||
};
|
};
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -264,7 +234,7 @@ private:
|
|||||||
m_mqttLoopTime = mqtt["loopTime"].as<uint16_t>();
|
m_mqttLoopTime = mqtt["loopTime"].as<uint16_t>();
|
||||||
m_mqttKeepalive = mqtt["keepalive"].as<uint8_t>();
|
m_mqttKeepalive = mqtt["keepalive"].as<uint8_t>();
|
||||||
m_mqttRetries = mqtt["retries"].as<uint8_t>();
|
m_mqttRetries = mqtt["retries"].as<uint8_t>();
|
||||||
auto subscribe = mqtt["subsribe"].as<ArduinoJson::JsonObject>();
|
auto subscribe = mqtt["subscribe"].as<ArduinoJson::JsonObject>();
|
||||||
for (auto v : subscribe)
|
for (auto v : subscribe)
|
||||||
{
|
{
|
||||||
m_mqttSubscribe[v.key().c_str()] = v.value().as<std::string>();
|
m_mqttSubscribe[v.key().c_str()] = v.value().as<std::string>();
|
||||||
@@ -304,9 +274,10 @@ public:
|
|||||||
|
|
||||||
// NTP
|
// NTP
|
||||||
std::string m_ntpPool = "pool.ntp.org";
|
std::string m_ntpPool = "pool.ntp.org";
|
||||||
uint16_t m_ntpTimezone = 3600; // GTM +1
|
int8_t m_ntpTimezone = +1; // GMT +1
|
||||||
uint16_t m_ntpUpdateInterval = 3600; // every hour
|
uint16_t m_ntpUpdateInterval = 3600; // every hour
|
||||||
uint8_t m_ntpRetries = 5;
|
uint8_t m_ntpRetries = 5;
|
||||||
|
uint8_t m_ntpRtcOffsetRegister = 0xE7; // -25 pulses in fast mode
|
||||||
|
|
||||||
// MQTT
|
// MQTT
|
||||||
std::string m_mqttHost = "10.0.2.249";
|
std::string m_mqttHost = "10.0.2.249";
|
||||||
@@ -319,6 +290,7 @@ public:
|
|||||||
std::map<const std::string, std::string> m_mqttSubscribe = {
|
std::map<const std::string, std::string> m_mqttSubscribe = {
|
||||||
{"commands", "etcontroller/hw/commands"}};
|
{"commands", "etcontroller/hw/commands"}};
|
||||||
std::map<const std::string, std::string> m_mqttPublish = {
|
std::map<const std::string, std::string> m_mqttPublish = {
|
||||||
|
{"cronjobs", "etcontroller/hw/cronjobs"},
|
||||||
{"answers", "etcontroller/hw/answers"},
|
{"answers", "etcontroller/hw/answers"},
|
||||||
{"heatpump", "etcontroller/hw/heatpump"},
|
{"heatpump", "etcontroller/hw/heatpump"},
|
||||||
{"temperatures", "etcontroller/hw/temperatures"},
|
{"temperatures", "etcontroller/hw/temperatures"},
|
||||||
|
|||||||
38
include/fsmount.h
Normal file
38
include/fsmount.h
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define DEBUGLOG_DEFAULT_LOG_LEVEL_DEBUG
|
||||||
|
|
||||||
|
#include <DebugLog.h>
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <FFat.h>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
class FSmount
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FSmount()
|
||||||
|
{
|
||||||
|
if (!FFat.begin(false))
|
||||||
|
{
|
||||||
|
LOG_ERROR("Unable to mount filesystem without formatting");
|
||||||
|
if (!FFat.begin(true))
|
||||||
|
{
|
||||||
|
LOG_ERROR("Formatted and mounted filesystem");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_INFO("Local Filesystem Mounted Correctly");
|
||||||
|
const auto totalBytes = FFat.totalBytes();
|
||||||
|
const auto freeBytes = FFat.freeBytes();
|
||||||
|
const auto usedBytes = FFat.usedBytes();
|
||||||
|
const auto mountPoint = FFat.mountpoint();
|
||||||
|
LOG_INFO("Local filesystem, total", totalBytes / 1024, "KB - used", usedBytes / 1024, "KB - free", freeBytes / 1024, "KB");
|
||||||
|
LOG_INFO("Local filesystem, mountpoint", mountPoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
~FSmount()
|
||||||
|
{
|
||||||
|
FFat.end(); // unmout filesystem to avoid corruption
|
||||||
|
LOG_INFO("Local Filesystem Unmounted Correctly");
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -3,12 +3,12 @@
|
|||||||
namespace drivers
|
namespace drivers
|
||||||
{
|
{
|
||||||
|
|
||||||
Ethernet::Ethernet(const std::string hostname) : m_hostname(hostname), m_connected(false), m_localIP(IPAddress()), m_udp(NetworkUDP()), m_timeClient(m_udp)
|
Ethernet::Ethernet(const std::string &hostname, const std::string &ntpPool, const int8_t tz, const uint16_t updateInterval) : m_hostname(hostname), m_ntpPool(ntpPool), m_connected(false), m_localIP(IPAddress()), m_udp(NetworkUDP()), m_timeClient(m_udp)
|
||||||
{
|
{
|
||||||
SPI.begin(ETH_SPI_SCK, ETH_SPI_MISO, ETH_SPI_MOSI);
|
SPI.begin(ETH_SPI_SCK, ETH_SPI_MISO, ETH_SPI_MOSI);
|
||||||
ETH.begin(ETH_PHY_TYPE, ETH_PHY_ADDR, ETH_PHY_CS, ETH_PHY_IRQ, ETH_PHY_RST, SPI);
|
ETH.begin(ETH_PHY_TYPE, ETH_PHY_ADDR, ETH_PHY_CS, ETH_PHY_IRQ, ETH_PHY_RST, SPI);
|
||||||
|
|
||||||
m_timeClient = std::move(NTPClient(m_udp, "pool.ntp.org", 0, 3600)); // NTP server, time offset in seconds, update interval
|
m_timeClient = std::move(NTPClient(m_udp, m_ntpPool.c_str(), tz * 3600, updateInterval)); // NTP server, time offset in seconds, update interval
|
||||||
m_timeClient.begin();
|
m_timeClient.begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,6 +30,17 @@ namespace drivers
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const bool Ethernet::setNtpTimeOffset(const int8_t tz)
|
||||||
|
{
|
||||||
|
if (m_connected)
|
||||||
|
{
|
||||||
|
m_timeClient.setTimeOffset(tz * 3600);
|
||||||
|
LOG_DEBUG("Time zone UTC ", tz);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
const bool Ethernet::isConnected()
|
const bool Ethernet::isConnected()
|
||||||
{
|
{
|
||||||
return m_connected;
|
return m_connected;
|
||||||
|
|||||||
@@ -30,15 +30,17 @@ namespace drivers
|
|||||||
{
|
{
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Ethernet(const std::string hostname);
|
Ethernet(const std::string &hostname, const std::string &ntpPool, const int8_t tz, const uint16_t updateInterval);
|
||||||
~Ethernet();
|
~Ethernet();
|
||||||
|
|
||||||
void onEvent(arduino_event_id_t event, arduino_event_info_t info);
|
void onEvent(arduino_event_id_t event, arduino_event_info_t info);
|
||||||
const bool isConnected();
|
const bool isConnected();
|
||||||
const bool getNtpTime(time_t &time);
|
const bool getNtpTime(time_t &time);
|
||||||
|
const bool setNtpTimeOffset(const int8_t tz);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const std::string m_hostname;
|
const std::string m_hostname;
|
||||||
|
const std::string m_ntpPool;
|
||||||
bool m_connected;
|
bool m_connected;
|
||||||
NetworkUDP m_udp;
|
NetworkUDP m_udp;
|
||||||
IPAddress m_localIP;
|
IPAddress m_localIP;
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ namespace drivers
|
|||||||
{
|
{
|
||||||
LOG_INFO("Inizializing RGB Led");
|
LOG_INFO("Inizializing RGB Led");
|
||||||
pinMode(c_ledPin, OUTPUT);
|
pinMode(c_ledPin, OUTPUT);
|
||||||
m_lp.pin = c_ledPin;
|
m_blinkTask = NULL;
|
||||||
m_lp.blinkTask = NULL;
|
m_flashTimer = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
Led::~Led()
|
Led::~Led()
|
||||||
@@ -22,54 +22,89 @@ namespace drivers
|
|||||||
|
|
||||||
void Led::setColor(const color_t color)
|
void Led::setColor(const color_t color)
|
||||||
{
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(m_ledMutex);
|
||||||
blinkStop();
|
blinkStop();
|
||||||
|
m_colorDefault = color;
|
||||||
rgbLedWrite(c_ledPin, color.g, color.r, color.b);
|
rgbLedWrite(c_ledPin, color.g, color.r, color.b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Led::flashHandle(TimerHandle_t th)
|
||||||
|
{
|
||||||
|
Led *led = (Led *)pvTimerGetTimerID(th);
|
||||||
|
rgbLedWrite(led->c_ledPin, led->m_colorDefault.g, led->m_colorDefault.r, led->m_colorDefault.b); // reset color to saved color
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Led::flashColor(const uint16_t tOn, const color_t color)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(m_ledMutex);
|
||||||
|
rgbLedWrite(c_ledPin, color.g, color.r, color.b); // set color to flash
|
||||||
|
if (m_flashTimer == NULL)
|
||||||
|
{
|
||||||
|
m_flashTimer = xTimerCreate("flasher", pdMS_TO_TICKS(tOn), pdFALSE, NULL, flashHandle);
|
||||||
|
xTimerStart(m_flashTimer, 0);
|
||||||
|
LOG_INFO("Led Flash timer created");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
xTimerStop(m_flashTimer, 0);
|
||||||
|
if (!xTimerChangePeriod(m_flashTimer, pdMS_TO_TICKS(tOn), pdMS_TO_TICKS(1)) || !xTimerReset(m_flashTimer, pdMS_TO_TICKS(1)))
|
||||||
|
{
|
||||||
|
LOG_ERROR("Led Flash timer failed reset");
|
||||||
|
xTimerDelete(m_flashTimer, 0);
|
||||||
|
m_flashTimer = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Led::blinkColor(const uint16_t tOn, const uint16_t tOff, const color_t color)
|
void Led::blinkColor(const uint16_t tOn, const uint16_t tOff, const color_t color)
|
||||||
{
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(m_ledMutex);
|
||||||
blinkStop();
|
blinkStop();
|
||||||
m_lp.color1 = color;
|
m_color1 = color;
|
||||||
m_lp.color2 = {0, 0, 0};
|
m_color2 = {0, 0, 0};
|
||||||
m_lp.tOn = tOn;
|
m_tOn = tOn;
|
||||||
m_lp.tOff = tOff;
|
m_tOff = tOff;
|
||||||
xTaskCreate(blinkTask, "blinker", TASK_STACK, static_cast<void *>(&m_lp), TASK_PRIORITY, &m_lp.blinkTask);
|
xTaskCreate(blinkTask, "blinker", TASK_STACK, this, TASK_PRIORITY, &m_blinkTask);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Led::blinkAlternate(const uint16_t tOn, const uint16_t tOff, const color_t color1, const color_t color2)
|
void Led::blinkAlternate(const uint16_t tOn, const uint16_t tOff, const color_t color1, const color_t color2)
|
||||||
{
|
{
|
||||||
{
|
std::lock_guard<std::mutex> lock(m_ledMutex);
|
||||||
blinkStop();
|
blinkStop();
|
||||||
m_lp.color1 = color1;
|
m_color1 = color1;
|
||||||
m_lp.color2 = color2;
|
m_color2 = color2;
|
||||||
m_lp.tOn = tOn;
|
m_tOn = tOn;
|
||||||
m_lp.tOff = tOff;
|
m_tOff = tOff;
|
||||||
xTaskCreate(blinkTask, "blinker", TASK_STACK, static_cast<void *>(&m_lp), TASK_PRIORITY, &m_lp.blinkTask);
|
xTaskCreate(blinkTask, "blinker", TASK_STACK, this, TASK_PRIORITY, &m_blinkTask);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Led::blinkStop()
|
void Led::blinkStop()
|
||||||
{
|
{
|
||||||
if (m_lp.blinkTask != NULL)
|
if (m_blinkTask != NULL)
|
||||||
vTaskDelete(m_lp.blinkTask);
|
vTaskDelete(m_blinkTask);
|
||||||
m_lp.blinkTask = NULL;
|
m_blinkTask = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Led::blinkTask(void *params)
|
void Led::blinkTask(void *params)
|
||||||
{
|
{
|
||||||
|
Led *led = static_cast<Led *>(params);
|
||||||
LOG_DEBUG("Blinker Task Created");
|
LOG_DEBUG("Blinker Task Created");
|
||||||
led_params_t *lPar = static_cast<led_params_t *>(params);
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
rgbLedWrite(lPar->pin, lPar->color1.g, lPar->color1.r, lPar->color1.b);
|
{
|
||||||
delay(lPar->tOn);
|
std::lock_guard<std::mutex> lock(led->m_ledMutex);
|
||||||
rgbLedWrite(lPar->pin, lPar->color2.g, lPar->color2.r, lPar->color2.b); // off
|
rgbLedWrite(led->c_ledPin, led->m_color1.g, led->m_color1.r, led->m_color1.b);
|
||||||
if (lPar->tOff == 0)
|
}
|
||||||
|
delay(led->m_tOn);
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(led->m_ledMutex);
|
||||||
|
rgbLedWrite(led->c_ledPin, led->m_color2.g, led->m_color2.r, led->m_color2.b); // off
|
||||||
|
}
|
||||||
|
if (led->m_tOff == 0)
|
||||||
break;
|
break;
|
||||||
delay(lPar->tOff);
|
delay(led->m_tOff);
|
||||||
}
|
}
|
||||||
LOG_DEBUG("Blinker Task Ended");
|
LOG_DEBUG("Blinker Task Ended");
|
||||||
lPar->blinkTask = NULL;
|
led->m_blinkTask = NULL;
|
||||||
vTaskDelete(NULL);
|
vTaskDelete(NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,14 +5,15 @@
|
|||||||
#define DEBUGLOG_DEFAULT_LOG_LEVEL_INFO
|
#define DEBUGLOG_DEFAULT_LOG_LEVEL_INFO
|
||||||
#include <DebugLog.h>
|
#include <DebugLog.h>
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
namespace drivers
|
namespace drivers
|
||||||
{
|
{
|
||||||
|
|
||||||
class Led
|
class Led
|
||||||
{
|
{
|
||||||
const uint8_t c_ledPin = 38;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
uint8_t r;
|
uint8_t r;
|
||||||
@@ -31,31 +32,36 @@ namespace drivers
|
|||||||
const color_t COLOR_VIOLET = {127, 0, 255};
|
const color_t COLOR_VIOLET = {127, 0, 255};
|
||||||
const color_t COLOR_MAGENTA = {255, 0, 255};
|
const color_t COLOR_MAGENTA = {255, 0, 255};
|
||||||
|
|
||||||
private:
|
public:
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
color_t color1;
|
|
||||||
color_t color2;
|
|
||||||
uint8_t pin;
|
|
||||||
uint16_t tOn;
|
|
||||||
uint16_t tOff;
|
|
||||||
TaskHandle_t blinkTask;
|
|
||||||
} led_params_t;
|
|
||||||
|
|
||||||
public:
|
|
||||||
Led();
|
Led();
|
||||||
~Led();
|
~Led();
|
||||||
|
|
||||||
void setColor(const color_t color);
|
void setColor(const color_t color);
|
||||||
|
void flashColor(const uint16_t tOn, const color_t color);
|
||||||
void blinkColor(const uint16_t tOn, const uint16_t tOff, const color_t color);
|
void blinkColor(const uint16_t tOn, const uint16_t tOff, const color_t color);
|
||||||
void blinkAlternate(const uint16_t tOn, const uint16_t tOff, const color_t color1, const color_t color2);
|
void blinkAlternate(const uint16_t tOn, const uint16_t tOff, const color_t color1, const color_t color2);
|
||||||
void blinkStop();
|
void blinkStop();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
static void flashHandle(TimerHandle_t th);
|
||||||
static void blinkTask(void *params);
|
static void blinkTask(void *params);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
led_params_t m_lp;
|
const uint8_t c_ledPin = 38;
|
||||||
|
|
||||||
|
color_t m_color1;
|
||||||
|
color_t m_color2;
|
||||||
|
color_t m_colorDefault;
|
||||||
|
|
||||||
|
uint16_t m_tOn;
|
||||||
|
uint16_t m_tOff;
|
||||||
|
|
||||||
|
TaskHandle_t m_blinkTask;
|
||||||
|
TimerHandle_t m_flashTimer;
|
||||||
|
|
||||||
|
bool m_flashing;
|
||||||
|
|
||||||
|
std::mutex m_ledMutex;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "PCF85063_Driver.h"
|
#include "PCF85063_Driver.h"
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
|
#include <utils.h>
|
||||||
|
|
||||||
namespace drivers
|
namespace drivers
|
||||||
{
|
{
|
||||||
@@ -157,6 +158,23 @@ namespace drivers
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const bool PCF85063::setOffset(const uint8_t ofst)
|
||||||
|
{
|
||||||
|
LOG_DEBUG("RTC set offset [", printHex(ofst).c_str(), "]");
|
||||||
|
return m_i2c.write(m_address, RTC_OFFSET_ADDR, {ofst});
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint8_t PCF85063::getOffset()
|
||||||
|
{
|
||||||
|
std::vector<uint8_t> buf;
|
||||||
|
if (m_i2c.read(m_address, RTC_OFFSET_ADDR, 1, buf))
|
||||||
|
{
|
||||||
|
LOG_DEBUG("RTC get offset [", printHex(buf.front()).c_str(), "]");
|
||||||
|
return buf.front();
|
||||||
|
}
|
||||||
|
return UINT8_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
const std::string PCF85063::getTimeStr()
|
const std::string PCF85063::getTimeStr()
|
||||||
{
|
{
|
||||||
datetime_t dt;
|
datetime_t dt;
|
||||||
@@ -182,10 +200,17 @@ namespace drivers
|
|||||||
{
|
{
|
||||||
tm dtime = datetime2tm(datetime);
|
tm dtime = datetime2tm(datetime);
|
||||||
const std::string buf(std::asctime(&dtime));
|
const std::string buf(std::asctime(&dtime));
|
||||||
return buf.substr(0, std::min(buf.find('\n'),buf.find('\r')));
|
return buf.substr(0, std::min(buf.find('\n'), buf.find('\r')));
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::tm PCF85063::datetime2tm(const datetime_t& datetime) {
|
const std::string PCF85063::tm2str(const std::tm &datetime)
|
||||||
|
{
|
||||||
|
const std::string buf(std::asctime(&datetime));
|
||||||
|
return buf.substr(0, std::min(buf.find('\n'), buf.find('\r')));
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::tm PCF85063::datetime2tm(const datetime_t &datetime)
|
||||||
|
{
|
||||||
tm dtime;
|
tm dtime;
|
||||||
dtime.tm_sec = datetime.second;
|
dtime.tm_sec = datetime.second;
|
||||||
dtime.tm_min = datetime.minute;
|
dtime.tm_min = datetime.minute;
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include <DebugLog.h>
|
#include <DebugLog.h>
|
||||||
#include "I2C_Driver.h"
|
#include "I2C_Driver.h"
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
// PCF85063_ADDRESS
|
// PCF85063_ADDRESS
|
||||||
#define PCF85063_ADDRESS (0x51)
|
#define PCF85063_ADDRESS (0x51)
|
||||||
@@ -82,7 +83,7 @@ namespace drivers
|
|||||||
} datetime_t;
|
} datetime_t;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PCF85063(I2C &i2c, const uint8_t address, const uint8_t ctrl1 = RTC_CTRL_1_DEFAULT, const uint8_t ctrl2 = RTC_CTRL_2_DEFAULT);
|
PCF85063(I2C &i2c, const uint8_t address = PCF85063_ADDRESS, const uint8_t ctrl1 = RTC_CTRL_1_DEFAULT, const uint8_t ctrl2 = RTC_CTRL_2_DEFAULT);
|
||||||
|
|
||||||
const bool reset(void);
|
const bool reset(void);
|
||||||
|
|
||||||
@@ -99,9 +100,13 @@ namespace drivers
|
|||||||
const bool readAlarm(datetime_t &time);
|
const bool readAlarm(datetime_t &time);
|
||||||
const bool getAlarmFlag(uint8_t &flags);
|
const bool getAlarmFlag(uint8_t &flags);
|
||||||
|
|
||||||
|
const bool setOffset(const uint8_t ofst);
|
||||||
|
const uint8_t getOffset();
|
||||||
|
|
||||||
const std::string getTimeStr();
|
const std::string getTimeStr();
|
||||||
|
|
||||||
static const std::string datetime2str(const datetime_t &datetime);
|
static const std::string datetime2str(const datetime_t &datetime);
|
||||||
|
static const std::string tm2str(const std::tm &datetime);
|
||||||
static const std::tm datetime2tm(const datetime_t& datetime);
|
static const std::tm datetime2tm(const datetime_t& datetime);
|
||||||
static const PCF85063::datetime_t fromEpoch(const time_t currentTime);
|
static const PCF85063::datetime_t fromEpoch(const time_t currentTime);
|
||||||
|
|
||||||
|
|||||||
244
src/commands.cpp
244
src/commands.cpp
@@ -1,4 +1,5 @@
|
|||||||
#include <commands.h>
|
#include <commands.h>
|
||||||
|
#include <cronjobs.h>
|
||||||
|
|
||||||
namespace commands
|
namespace commands
|
||||||
{
|
{
|
||||||
@@ -8,6 +9,13 @@ namespace commands
|
|||||||
esp_restart();
|
esp_restart();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ArduinoJson::JsonDocument Commands::setBuzz(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms)
|
||||||
|
{
|
||||||
|
ArduinoJson::JsonDocument response;
|
||||||
|
dev.buzzer.beep(500, NOTE_Bb);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
// CONFIG //
|
// CONFIG //
|
||||||
// CONFIG //
|
// CONFIG //
|
||||||
const ArduinoJson::JsonDocument Commands::setConfig(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms)
|
const ArduinoJson::JsonDocument Commands::setConfig(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms)
|
||||||
@@ -50,22 +58,129 @@ namespace commands
|
|||||||
|
|
||||||
// CRONJOBS //
|
// CRONJOBS //
|
||||||
// CRONJOBS //
|
// CRONJOBS //
|
||||||
const ArduinoJson::JsonDocument Commands::setCronjob(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms)
|
const ArduinoJson::JsonDocument Commands::loadCronJob(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms)
|
||||||
{
|
{
|
||||||
ArduinoJson::JsonDocument response;
|
ArduinoJson::JsonDocument response;
|
||||||
LOG_WARN("setCronjob not yet implemented");
|
response["cmd"] = "loadCronJob";
|
||||||
|
auto &cron = Cron::getInstance(dev);
|
||||||
|
if (!cron.loadEvents())
|
||||||
|
{
|
||||||
|
LOG_ERROR("loadCronJob failed to load events from flash");
|
||||||
|
response["values"]["status"] = "invalid";
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
response["values"]["status"] = "valid";
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
const ArduinoJson::JsonDocument Commands::getCronjob(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms)
|
|
||||||
|
const ArduinoJson::JsonDocument Commands::setCronJob(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms)
|
||||||
{
|
{
|
||||||
ArduinoJson::JsonDocument response;
|
ArduinoJson::JsonDocument response;
|
||||||
LOG_WARN("getCronjob not yet implemented");
|
response["cmd"] = "setCronJob";
|
||||||
|
|
||||||
|
const auto &eventName = params["name"].as<std::string>();
|
||||||
|
const auto &timeStr = params["cronExpr"].as<std::string>();
|
||||||
|
const auto &actionStr = params["action"].as<std::string>();
|
||||||
|
response["values"]["name"] = eventName;
|
||||||
|
|
||||||
|
ArduinoJson::JsonDocument action;
|
||||||
|
if (ArduinoJson::deserializeJson(action, actionStr) != ArduinoJson::DeserializationError::Ok)
|
||||||
|
{
|
||||||
|
LOG_ERROR("setCronJob unable to deserialize cron job [", actionStr.c_str(), "]");
|
||||||
|
response["values"]["status"] = "invalid";
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto &cron = Cron::getInstance(dev);
|
||||||
|
if (!cron.addEvent(eventName, timeStr, action))
|
||||||
|
{
|
||||||
|
LOG_ERROR("setCronJob unable to add job [", actionStr.c_str(), "]");
|
||||||
|
response["values"]["status"] = "invalid";
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
LOG_INFO("setCronJob added job [", actionStr.c_str(), "]");
|
||||||
|
response["values"]["status"] = "valid";
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
const ArduinoJson::JsonDocument Commands::delCronjob(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms)
|
const ArduinoJson::JsonDocument Commands::getCronJob(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms)
|
||||||
{
|
{
|
||||||
ArduinoJson::JsonDocument response;
|
ArduinoJson::JsonDocument response;
|
||||||
LOG_WARN("delCronjob not yet implemented");
|
response["cmd"] = "getCronJob";
|
||||||
|
auto &cron = Cron::getInstance(dev);
|
||||||
|
auto eventName = params["name"].as<std::string>();
|
||||||
|
|
||||||
|
if (eventName.empty())
|
||||||
|
{
|
||||||
|
LOG_ERROR("getCronJob empty job name");
|
||||||
|
response["values"]["status"] = "invalid";
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (eventName == "all")
|
||||||
|
{
|
||||||
|
const auto &eventMap = cron.getAllEvents();
|
||||||
|
uint8_t eventNum(0);
|
||||||
|
for (const auto &[name, event] : eventMap)
|
||||||
|
{
|
||||||
|
const auto cmd = std::get<0>(event);
|
||||||
|
response["values"][name] = cmd;
|
||||||
|
eventNum++;
|
||||||
|
}
|
||||||
|
LOG_INFO("getCronJob got [", eventNum, "] events");
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
Cron::CronEvent event;
|
||||||
|
response["values"]["name"] = eventName;
|
||||||
|
if (!cron.getEvent(eventName, event))
|
||||||
|
{
|
||||||
|
LOG_ERROR("getCronJob failed to get job [", eventName.c_str(), "]");
|
||||||
|
response["values"]["status"] = "invalid";
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto cmd = std::get<0>(event);
|
||||||
|
auto cronExpr = std::get<1>(event);
|
||||||
|
auto cmdParams = std::get<3>(event);
|
||||||
|
|
||||||
|
ArduinoJson::JsonDocument action;
|
||||||
|
action["cmd"] = cmd;
|
||||||
|
action["params"] = cmdParams;
|
||||||
|
response["values"]["cronExpr"] = cron::to_cronstr(cronExpr);
|
||||||
|
response["values"]["action"] = action;
|
||||||
|
|
||||||
|
LOG_INFO("getCronJob get job [", eventName.c_str(), "]");
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
const ArduinoJson::JsonDocument Commands::delCronJob(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms)
|
||||||
|
{
|
||||||
|
ArduinoJson::JsonDocument response;
|
||||||
|
response["cmd"] = "delCronJob";
|
||||||
|
auto &cron = Cron::getInstance(dev);
|
||||||
|
auto eventName = params["name"].as<std::string>();
|
||||||
|
response["values"]["name"] = eventName;
|
||||||
|
if (eventName.empty() || !cron.delEvent(eventName))
|
||||||
|
{
|
||||||
|
LOG_ERROR("delCronJob failed to delete job [", eventName.c_str(), "]");
|
||||||
|
response["values"]["status"] = "invalid";
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
response["values"]["status"] = "valid";
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ArduinoJson::JsonDocument Commands::storeCronJob(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms)
|
||||||
|
{
|
||||||
|
ArduinoJson::JsonDocument response;
|
||||||
|
response["cmd"] = "storeCronJob";
|
||||||
|
auto &cron = Cron::getInstance(dev);
|
||||||
|
if (!cron.storeEvents())
|
||||||
|
{
|
||||||
|
LOG_ERROR("storeCronJob failed to store events in flash");
|
||||||
|
response["values"]["status"] = "invalid";
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
response["values"]["status"] = "valid";
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
// CRONJOBS //
|
// CRONJOBS //
|
||||||
@@ -76,62 +191,62 @@ namespace commands
|
|||||||
const ArduinoJson::JsonDocument Commands::setHPlimit(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms)
|
const ArduinoJson::JsonDocument Commands::setHPlimit(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms)
|
||||||
{
|
{
|
||||||
ArduinoJson::JsonDocument response;
|
ArduinoJson::JsonDocument response;
|
||||||
{
|
response["cmd"] = "setHPlimit";
|
||||||
std::string msg;
|
|
||||||
serializeJson(params, msg);
|
|
||||||
LOG_INFO("setHPlimit params ->", msg.c_str());
|
|
||||||
};
|
|
||||||
if (!params["level"].is<std::string>())
|
if (!params["level"].is<std::string>())
|
||||||
{
|
{
|
||||||
LOG_ERROR("setHPlimit incorrect parameters");
|
LOG_ERROR("setHPlimit incorrect parameters");
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
const auto level = params["level"].as<std::string>();
|
const auto level = params["level"].as<std::string>();
|
||||||
|
response["values"]["level"] = level;
|
||||||
if (!c_hpLimitsMap.contains(level))
|
if (!c_hpLimitsMap.contains(level))
|
||||||
{
|
{
|
||||||
LOG_ERROR("setHPlimit invalid level", level.c_str());
|
LOG_ERROR("setHPlimit invalid level", level.c_str());
|
||||||
|
response["values"]["status"] = "invalid";
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
for (auto k : c_hpLimitsMap)
|
for (const auto [lvl, ro] : c_hpLimitsMap)
|
||||||
{
|
{
|
||||||
if (level == k.first && level != "UNLIMITED")
|
if (level == lvl && level != "UNLIMITED")
|
||||||
dev.io.digitalOutWrite(k.second, true);
|
dev.io.digitalOutWrite(ro, true);
|
||||||
else
|
else
|
||||||
dev.io.digitalOutWrite(k.second, false);
|
dev.io.digitalOutWrite(ro, false);
|
||||||
}
|
}
|
||||||
LOG_INFO("setHPlimit -> level", level.c_str());
|
LOG_INFO("setHPlimit -> level", level.c_str());
|
||||||
|
response["values"]["status"] = "valid";
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ArduinoJson::JsonDocument Commands::setHeating(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms)
|
const ArduinoJson::JsonDocument Commands::setHeating(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms)
|
||||||
{
|
{
|
||||||
ArduinoJson::JsonDocument response;
|
ArduinoJson::JsonDocument response;
|
||||||
{
|
response["cmd"] = "setHeating";
|
||||||
std::string msg;
|
|
||||||
serializeJson(params, msg);
|
|
||||||
LOG_INFO("setHeating params ->", msg.c_str());
|
|
||||||
};
|
|
||||||
if (params.isNull())
|
if (params.isNull())
|
||||||
{
|
{
|
||||||
LOG_ERROR("setHeating incorrect paramaters");
|
LOG_ERROR("setHeating incorrect paramaters");
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
for (auto v : c_heatingValveMap)
|
for (const auto [lvl, ro] : c_heatingValveMap)
|
||||||
{
|
{
|
||||||
if (params[v.first].isNull())
|
if (params[lvl].isNull())
|
||||||
continue;
|
continue;
|
||||||
if (params[v.first] == "ON")
|
if (params[lvl] == "ON")
|
||||||
{
|
{
|
||||||
dev.io.digitalOutWrite(v.second, true);
|
dev.io.digitalOutWrite(ro, true);
|
||||||
LOG_INFO("setHeating -> ", v.first.c_str(), "ON");
|
response["values"][lvl] = "ON";
|
||||||
|
LOG_INFO("setHeating -> ", lvl.c_str(), "ON");
|
||||||
}
|
}
|
||||||
else if (params[v.first] == "OFF")
|
else if (params[lvl] == "OFF")
|
||||||
{
|
{
|
||||||
dev.io.digitalOutWrite(v.second, false);
|
dev.io.digitalOutWrite(ro, false);
|
||||||
LOG_INFO("setHeating -> ", v.first.c_str(), "OFF");
|
response["values"][lvl] = "OFF";
|
||||||
|
LOG_INFO("setHeating -> ", lvl.c_str(), "OFF");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
response["values"][lvl] = "invalid";
|
||||||
LOG_ERROR("setHeating invalid valve state");
|
LOG_ERROR("setHeating invalid valve state");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
@@ -164,11 +279,6 @@ namespace commands
|
|||||||
{
|
{
|
||||||
ArduinoJson::JsonDocument response;
|
ArduinoJson::JsonDocument response;
|
||||||
auto &conf = Config::getInstance();
|
auto &conf = Config::getInstance();
|
||||||
{
|
|
||||||
std::string msg;
|
|
||||||
serializeJson(params, msg);
|
|
||||||
LOG_INFO("setIrrigation params ->", msg.c_str());
|
|
||||||
};
|
|
||||||
response["cmd"] = "setIrrigation";
|
response["cmd"] = "setIrrigation";
|
||||||
if (params.isNull())
|
if (params.isNull())
|
||||||
{
|
{
|
||||||
@@ -179,6 +289,8 @@ namespace commands
|
|||||||
const uint16_t tOn(params["timeOn"].as<uint16_t>());
|
const uint16_t tOn(params["timeOn"].as<uint16_t>());
|
||||||
const uint16_t tPause(params["timePause"].as<uint16_t>());
|
const uint16_t tPause(params["timePause"].as<uint16_t>());
|
||||||
|
|
||||||
|
response["values"]["zone"] = zone;
|
||||||
|
|
||||||
if (zone == "stop")
|
if (zone == "stop")
|
||||||
{ // stop all zones and reset timers
|
{ // stop all zones and reset timers
|
||||||
LOG_INFO("setIrrigation stop all zones");
|
LOG_INFO("setIrrigation stop all zones");
|
||||||
@@ -208,6 +320,8 @@ namespace commands
|
|||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
response["values"]["timeOn"] = tOn;
|
||||||
|
response["values"]["timePause"] = tPause;
|
||||||
if (!c_irrigationValveMap.contains(zone) || tOn <= 0 || tPause <= 0) // verify if zone is a valid map key
|
if (!c_irrigationValveMap.contains(zone) || tOn <= 0 || tPause <= 0) // verify if zone is a valid map key
|
||||||
{
|
{
|
||||||
LOG_ERROR("setIrrigation incorrect zone[", zone.c_str(), "] or time values tOn[", tOn, "] tPause[", tPause, "]");
|
LOG_ERROR("setIrrigation incorrect zone[", zone.c_str(), "] or time values tOn[", tOn, "] tPause[", tPause, "]");
|
||||||
@@ -253,11 +367,36 @@ namespace commands
|
|||||||
dev.io.digitalOutWrite(zoneIoNumber, true);
|
dev.io.digitalOutWrite(zoneIoNumber, true);
|
||||||
xTimerStart(shTimer, 0);
|
xTimerStart(shTimer, 0);
|
||||||
timerHandle = shTimer;
|
timerHandle = shTimer;
|
||||||
response["values"]["status"] = "ok";
|
response["values"]["status"] = "valid";
|
||||||
LOG_INFO("setIrrigation zone [", timerName, "] tOn[", tOn, "] tPause[", tPause, "]");
|
LOG_INFO("setIrrigation zone [", timerName, "] tOn[", tOn, "] tPause[", tPause, "]");
|
||||||
}
|
}
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ArduinoJson::JsonDocument Commands::setTimeNTP(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms)
|
||||||
|
{
|
||||||
|
ArduinoJson::JsonDocument response;
|
||||||
|
response["cmd"] = "setTimeNTP";
|
||||||
|
auto ð = dev.eth;
|
||||||
|
auto &rtc = dev.rtc;
|
||||||
|
|
||||||
|
time_t ntpTime;
|
||||||
|
auto ntpOk = eth.getNtpTime(ntpTime);
|
||||||
|
|
||||||
|
drivers::PCF85063::datetime_t rtcTime(drivers::PCF85063::fromEpoch(ntpTime));
|
||||||
|
auto rtcOk = rtc.setDatetime(rtcTime);
|
||||||
|
|
||||||
|
if (!rtcOk || !ntpOk)
|
||||||
|
{
|
||||||
|
response["values"]["status"] = "invalid";
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
response["values"]["status"] = "valid";
|
||||||
|
response["values"]["time"] = rtc.getTimeStr();
|
||||||
|
LOG_INFO("setTimeNTP -> RTC is [", response["status"]["time"].as<std::string>().c_str(), "]");
|
||||||
|
return response;
|
||||||
|
}
|
||||||
// SETTERS //
|
// SETTERS //
|
||||||
// SETTERS //
|
// SETTERS //
|
||||||
|
|
||||||
@@ -266,8 +405,8 @@ namespace commands
|
|||||||
const ArduinoJson::JsonDocument Commands::getHPpower(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms)
|
const ArduinoJson::JsonDocument Commands::getHPpower(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms)
|
||||||
{
|
{
|
||||||
ArduinoJson::JsonDocument response;
|
ArduinoJson::JsonDocument response;
|
||||||
const auto pinfo = dev.seneca.getAll();
|
|
||||||
response["cmd"] = "getHPpower";
|
response["cmd"] = "getHPpower";
|
||||||
|
const auto pinfo = dev.seneca.getAll();
|
||||||
auto values = response["values"].to<JsonObject>();
|
auto values = response["values"].to<JsonObject>();
|
||||||
values["power"] = pinfo.pAct;
|
values["power"] = pinfo.pAct;
|
||||||
values["current"] = pinfo.a;
|
values["current"] = pinfo.a;
|
||||||
@@ -325,6 +464,41 @@ namespace commands
|
|||||||
LOG_WARN("Comand not yet implemented");
|
LOG_WARN("Comand not yet implemented");
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ArduinoJson::JsonDocument Commands::getTimeDrift(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms)
|
||||||
|
{
|
||||||
|
ArduinoJson::JsonDocument response;
|
||||||
|
response["cmd"] = "getTimeDrift";
|
||||||
|
auto ð = dev.eth;
|
||||||
|
auto &rtc = dev.rtc;
|
||||||
|
|
||||||
|
time_t ntpTime;
|
||||||
|
auto ntpOk = eth.getNtpTime(ntpTime);
|
||||||
|
|
||||||
|
drivers::PCF85063::datetime_t rtcTime;
|
||||||
|
auto rtcOk = rtc.readDatetime(rtcTime);
|
||||||
|
auto rtcTimeTm = drivers::PCF85063::datetime2tm(rtcTime);
|
||||||
|
|
||||||
|
if (!rtcOk || !ntpOk)
|
||||||
|
{
|
||||||
|
response["values"]["status"] = "invalid";
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ntpTimePoint = std::chrono::system_clock::from_time_t(ntpTime);
|
||||||
|
auto rtcTimePoint = std::chrono::system_clock::from_time_t(std::mktime(&rtcTimeTm));
|
||||||
|
|
||||||
|
auto timeDiff = std::chrono::duration_cast<std::chrono::seconds>(ntpTimePoint - rtcTimePoint);
|
||||||
|
auto direction = timeDiff.count() >= 0 ? "BEYOND" : "AHEAD";
|
||||||
|
|
||||||
|
response["values"]["status"] = "valid";
|
||||||
|
response["values"]["drift"] = (uint32_t)timeDiff.count();
|
||||||
|
response["values"]["direction"] = "RTC is [" + std::string(direction) + "] NTP time";
|
||||||
|
|
||||||
|
LOG_INFO("getTimeDrift -> RTC is [", (int32_t)timeDiff.count(), "] sec, [", std::string(direction).c_str(), "] NTP time");
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
// GETTERS //
|
// GETTERS //
|
||||||
// GETTERS //
|
// GETTERS //
|
||||||
|
|
||||||
|
|||||||
@@ -64,19 +64,25 @@ namespace commands
|
|||||||
Commands() = delete;
|
Commands() = delete;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
// TEST //
|
||||||
|
static const ArduinoJson::JsonDocument setBuzz(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms);
|
||||||
|
|
||||||
// CONFIG //
|
// CONFIG //
|
||||||
static const ArduinoJson::JsonDocument setConfig(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms);
|
static const ArduinoJson::JsonDocument setConfig(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms);
|
||||||
static const ArduinoJson::JsonDocument getConfig(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms);
|
static const ArduinoJson::JsonDocument getConfig(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms);
|
||||||
|
|
||||||
// CRONJOBS //
|
// CRONJOBS //
|
||||||
static const ArduinoJson::JsonDocument setCronjob(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms);
|
static const ArduinoJson::JsonDocument loadCronJob(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms);
|
||||||
static const ArduinoJson::JsonDocument getCronjob(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms);
|
static const ArduinoJson::JsonDocument setCronJob(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms);
|
||||||
static const ArduinoJson::JsonDocument delCronjob(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms);
|
static const ArduinoJson::JsonDocument getCronJob(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms);
|
||||||
|
static const ArduinoJson::JsonDocument delCronJob(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms);
|
||||||
|
static const ArduinoJson::JsonDocument storeCronJob(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms);
|
||||||
|
|
||||||
// SETTERS //
|
// SETTERS //
|
||||||
static const ArduinoJson::JsonDocument setHPlimit(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms);
|
static const ArduinoJson::JsonDocument setHPlimit(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms);
|
||||||
static const ArduinoJson::JsonDocument setHeating(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms);
|
static const ArduinoJson::JsonDocument setHeating(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms);
|
||||||
static const ArduinoJson::JsonDocument setIrrigation(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms);
|
static const ArduinoJson::JsonDocument setIrrigation(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms);
|
||||||
|
static const ArduinoJson::JsonDocument setTimeNTP(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms);
|
||||||
|
|
||||||
// GETTERS //
|
// GETTERS //
|
||||||
static const ArduinoJson::JsonDocument getHPpower(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms);
|
static const ArduinoJson::JsonDocument getHPpower(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms);
|
||||||
@@ -87,15 +93,21 @@ namespace commands
|
|||||||
static const ArduinoJson::JsonDocument getTankInfo(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms);
|
static const ArduinoJson::JsonDocument getTankInfo(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms);
|
||||||
static const ArduinoJson::JsonDocument getRainInfo(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms);
|
static const ArduinoJson::JsonDocument getRainInfo(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms);
|
||||||
static const ArduinoJson::JsonDocument getIrrigation(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms);
|
static const ArduinoJson::JsonDocument getIrrigation(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms);
|
||||||
|
static const ArduinoJson::JsonDocument getTimeDrift(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms);
|
||||||
};
|
};
|
||||||
|
|
||||||
static const std::map<const std::string, Command> commandMap = {
|
static const std::map<const std::string, Command> s_commandMap = {
|
||||||
|
|
||||||
|
{"setBuzz", Commands::setBuzz},
|
||||||
|
|
||||||
{"setConfig", Commands::setConfig},
|
{"setConfig", Commands::setConfig},
|
||||||
{"getConfig", Commands::getConfig},
|
{"getConfig", Commands::getConfig},
|
||||||
|
|
||||||
{"setCronjob", Commands::setCronjob},
|
{"loadCronJob", Commands::loadCronJob},
|
||||||
{"getCronjob", Commands::getCronjob},
|
{"setCronJob", Commands::setCronJob},
|
||||||
{"delCronjob", Commands::delCronjob},
|
{"getCronJob", Commands::getCronJob},
|
||||||
|
{"delCronJob", Commands::delCronJob},
|
||||||
|
{"storeCronJob", Commands::storeCronJob},
|
||||||
|
|
||||||
{"setHPlimit", Commands::setHPlimit},
|
{"setHPlimit", Commands::setHPlimit},
|
||||||
{"setHeating", Commands::setHeating},
|
{"setHeating", Commands::setHeating},
|
||||||
@@ -103,6 +115,9 @@ namespace commands
|
|||||||
|
|
||||||
{"getHPpower", Commands::getHPpower},
|
{"getHPpower", Commands::getHPpower},
|
||||||
{"setHeating", Commands::setHeating},
|
{"setHeating", Commands::setHeating},
|
||||||
|
|
||||||
|
{"getTimeDrift", Commands::getTimeDrift},
|
||||||
|
{"setTimeNTP", Commands::setTimeNTP},
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
168
src/cronjobs.cpp
168
src/cronjobs.cpp
@@ -1,30 +1,97 @@
|
|||||||
#include <cronjobs.h>
|
#include <cronjobs.h>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
#include <fsmount.h>
|
||||||
|
|
||||||
#define STACK_DEPTH 4096
|
#define STACK_DEPTH 4096
|
||||||
#define PRIORITY 3
|
#define PRIORITY 3
|
||||||
|
|
||||||
Cron::Cron(devices_t &dev) : m_dev(dev)
|
const bool Cron::loadEvents()
|
||||||
{
|
{
|
||||||
}
|
FSmount fs;
|
||||||
|
File cronFile = FFat.open("/cronjobs.json", FILE_READ, false);
|
||||||
|
if (!cronFile)
|
||||||
|
{
|
||||||
|
LOG_ERROR("Cron failed to open cronjobs.json");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ArduinoJson::JsonDocument cronFileContent;
|
||||||
|
if (ArduinoJson::deserializeJson(cronFileContent, cronFile) != ArduinoJson::DeserializationError::Ok)
|
||||||
|
{
|
||||||
|
LOG_ERROR("Cron unable to deserialize cronjobs.json");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
Cron::~Cron()
|
std::string buf;
|
||||||
{
|
ArduinoJson::serializeJsonPretty(cronFileContent, buf);
|
||||||
}
|
LOG_INFO("Cron loadEvents loaded cronjobs.json\n", buf.c_str());
|
||||||
|
|
||||||
const bool Cron::loadEvents(fs::File &file)
|
ArduinoJson::JsonArray cronjobList = cronFileContent.as<JsonArray>();
|
||||||
{
|
LOG_INFO("Cron loadEvents loaded [", cronjobList.size(), "] events");
|
||||||
|
for (const auto &job : cronjobList)
|
||||||
|
{
|
||||||
|
const auto &eventName = job["name"].as<std::string>();
|
||||||
|
const auto &cronExpr = job["cronExpr"].as<std::string>();
|
||||||
|
ArduinoJson::JsonDocument action(job["action"]);
|
||||||
|
if (!addEvent(eventName, cronExpr, action))
|
||||||
|
LOG_ERROR("Cron failed to load event [", eventName.c_str(), "]");
|
||||||
|
else
|
||||||
|
LOG_INFO("Cron loaded event [", eventName.c_str(), "]");
|
||||||
|
delay(10);
|
||||||
|
}
|
||||||
|
cronFile.close();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool Cron::addEvent(const std::string &name, const std::string &command, const std::string &expr)
|
const bool Cron::storeEvents()
|
||||||
{
|
{
|
||||||
|
FSmount fs;
|
||||||
|
std::lock_guard<std::mutex> lock(m_mutex);
|
||||||
|
File cronFile = FFat.open("/cronjobs.json", FILE_WRITE, true);
|
||||||
|
if (!cronFile)
|
||||||
|
{
|
||||||
|
LOG_ERROR("Cron failed to open cronjobs.json");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ArduinoJson::JsonDocument cronFileContent;
|
||||||
|
ArduinoJson::JsonArray cronFileArray = cronFileContent.to<JsonArray>();
|
||||||
|
|
||||||
|
for (const auto &job : m_cronMap) // convert cron events map to json file
|
||||||
|
{
|
||||||
|
const auto &eventName = job.first;
|
||||||
|
const auto ¶ms = job.second;
|
||||||
|
|
||||||
|
const auto &cmd = std::get<0>(params);
|
||||||
|
const auto &cronExpr = std::get<1>(params);
|
||||||
|
const auto &cmdParams = std::get<3>(params);
|
||||||
|
|
||||||
|
ArduinoJson::JsonDocument thisJob;
|
||||||
|
thisJob["name"] = eventName;
|
||||||
|
thisJob["cronExpr"] = cron::to_cronstr(cronExpr);
|
||||||
|
thisJob["action"]["cmd"] = cmd;
|
||||||
|
thisJob["action"]["params"] = cmdParams;
|
||||||
|
|
||||||
|
cronFileArray.add(thisJob);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string buf;
|
||||||
|
ArduinoJson::serializeJsonPretty(cronFileContent, buf);
|
||||||
|
LOG_INFO("Cron storeEvents generated cronjobs.json\n", buf.c_str());
|
||||||
|
|
||||||
|
ArduinoJson::serializeJson(cronFileContent, cronFile);
|
||||||
|
cronFile.close();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool Cron::addEvent(const std::string &name, const std::string &expr, const ArduinoJson::JsonDocument action)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(m_mutex);
|
||||||
if (m_cronMap.contains(name))
|
if (m_cronMap.contains(name))
|
||||||
{
|
{
|
||||||
LOG_ERROR("Cron event [", name.c_str(), "] already scheduled");
|
LOG_ERROR("Cron event [", name.c_str(), "] already scheduled");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (name.empty() || command.empty() || expr.empty())
|
if (name.empty() || expr.empty() || action.isNull())
|
||||||
{
|
{
|
||||||
LOG_ERROR("Cron event invalid parameters");
|
LOG_ERROR("Cron event invalid parameters");
|
||||||
return false;
|
return false;
|
||||||
@@ -33,20 +100,13 @@ const bool Cron::addEvent(const std::string &name, const std::string &command, c
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
const auto eventExpr(cron::make_cron(expr));
|
const auto eventExpr(cron::make_cron(expr));
|
||||||
ArduinoJson::JsonDocument action;
|
|
||||||
if (ArduinoJson::deserializeJson(action, command) != ArduinoJson::DeserializationError::Ok)
|
|
||||||
{
|
|
||||||
LOG_ERROR("Cron unable to deserialize command [", command.c_str(), "]");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const auto cmd = action["cmd"].as<std::string>();
|
const auto cmd = action["cmd"].as<std::string>();
|
||||||
const auto params = action["params"].as<JsonObject>();
|
const auto params = action["params"];
|
||||||
if (!commands::commandMap.contains(cmd))
|
if (!commands::s_commandMap.contains(cmd))
|
||||||
{
|
{
|
||||||
LOG_ERROR("Cron unknown command [", command.c_str(), "]");
|
LOG_ERROR("Cron unknown command [", cmd.c_str(), "]");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
LOG_INFO("Cron added event [", name.c_str(), "]");
|
|
||||||
drivers::PCF85063::datetime_t now;
|
drivers::PCF85063::datetime_t now;
|
||||||
if (!m_dev.rtc.readDatetime(now))
|
if (!m_dev.rtc.readDatetime(now))
|
||||||
{
|
{
|
||||||
@@ -54,7 +114,10 @@ const bool Cron::addEvent(const std::string &name, const std::string &command, c
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
std::tm nowTm = drivers::PCF85063::datetime2tm(now);
|
std::tm nowTm = drivers::PCF85063::datetime2tm(now);
|
||||||
m_cronMap[name] = std::make_tuple(cmd, eventExpr, cron::cron_next(eventExpr, nowTm), params);
|
auto next = cron::cron_next(eventExpr, nowTm);
|
||||||
|
JsonDocument act(params);
|
||||||
|
LOG_INFO("Cron adding event [", name.c_str(), "] next execution [", drivers::PCF85063::tm2str(next).c_str(), "]");
|
||||||
|
m_cronMap[name] = std::make_tuple(cmd, eventExpr, next, act);
|
||||||
}
|
}
|
||||||
catch (cron::bad_cronexpr const &ex)
|
catch (cron::bad_cronexpr const &ex)
|
||||||
{
|
{
|
||||||
@@ -64,8 +127,22 @@ const bool Cron::addEvent(const std::string &name, const std::string &command, c
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const bool Cron::getEvent(const std::string &name, CronEvent &event)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(m_mutex);
|
||||||
|
if (!m_cronMap.contains(name))
|
||||||
|
{
|
||||||
|
LOG_ERROR("Cron event [", name.c_str(), "] does not exist");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
LOG_INFO("Cron get event [", name.c_str(), "]");
|
||||||
|
event = m_cronMap.at(name);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
const bool Cron::delEvent(const std::string &name)
|
const bool Cron::delEvent(const std::string &name)
|
||||||
{
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(m_mutex);
|
||||||
if (!m_cronMap.contains(name))
|
if (!m_cronMap.contains(name))
|
||||||
{
|
{
|
||||||
LOG_WARN("Cron event [", name.c_str(), "] does not exist");
|
LOG_WARN("Cron event [", name.c_str(), "] does not exist");
|
||||||
@@ -76,23 +153,34 @@ const bool Cron::delEvent(const std::string &name)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cronLoop(void* params) {
|
const Cron::CronEventMap &Cron::getAllEvents()
|
||||||
auto cron = (Cron*)(params);
|
{
|
||||||
while (true) {
|
return m_cronMap;
|
||||||
cron->processEvents();
|
}
|
||||||
|
|
||||||
|
void cronLoop(void *cronPtr)
|
||||||
|
{
|
||||||
|
auto &cron = *(Cron *)cronPtr;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
cron.processEvents();
|
||||||
delay(1000);
|
delay(1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cron::startCron() {
|
void Cron::startCron()
|
||||||
if (!m_cronTaskHandle) {
|
{
|
||||||
|
if (!m_cronTaskHandle)
|
||||||
|
{
|
||||||
LOG_INFO("Cron starting loop");
|
LOG_INFO("Cron starting loop");
|
||||||
xTaskCreate(cronLoop, "cronLoop", STACK_DEPTH, this, PRIORITY, &m_cronTaskHandle);
|
xTaskCreate(cronLoop, "cronLoop", STACK_DEPTH, this, PRIORITY, &m_cronTaskHandle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cron::stopCron() {
|
void Cron::stopCron()
|
||||||
if (m_cronTaskHandle) {
|
{
|
||||||
|
if (m_cronTaskHandle)
|
||||||
|
{
|
||||||
LOG_WARN("Cron stopping loop");
|
LOG_WARN("Cron stopping loop");
|
||||||
vTaskDelete(m_cronTaskHandle);
|
vTaskDelete(m_cronTaskHandle);
|
||||||
m_cronTaskHandle = NULL;
|
m_cronTaskHandle = NULL;
|
||||||
@@ -101,6 +189,7 @@ void Cron::stopCron() {
|
|||||||
|
|
||||||
const bool Cron::processEvents()
|
const bool Cron::processEvents()
|
||||||
{
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(m_mutex);
|
||||||
LOG_DEBUG("Cron processEvents [", m_cronMap.size(), "]");
|
LOG_DEBUG("Cron processEvents [", m_cronMap.size(), "]");
|
||||||
|
|
||||||
drivers::PCF85063::datetime_t now;
|
drivers::PCF85063::datetime_t now;
|
||||||
@@ -120,20 +209,31 @@ const bool Cron::processEvents()
|
|||||||
auto &cmd = std::get<0>(eventAction);
|
auto &cmd = std::get<0>(eventAction);
|
||||||
auto &cronexrp = std::get<1>(eventAction);
|
auto &cronexrp = std::get<1>(eventAction);
|
||||||
auto &next = std::get<2>(eventAction);
|
auto &next = std::get<2>(eventAction);
|
||||||
auto ¶ms = std::get<3>(eventAction);
|
auto &cmdParams = std::get<3>(eventAction);
|
||||||
|
|
||||||
const auto nowPoint = std::chrono::system_clock::from_time_t(std::mktime(&nowTm));
|
const auto nowPoint = std::chrono::system_clock::from_time_t(std::mktime(&nowTm));
|
||||||
const auto nextEventPoint = std::chrono::system_clock::from_time_t(std::mktime(&next));
|
const auto nextEventPoint = std::chrono::system_clock::from_time_t(std::mktime(&next));
|
||||||
|
|
||||||
LOG_DEBUG("Cron current time [", std::asctime(&nowTm), "]");
|
LOG_DEBUG("Cron current time [", std::asctime(&nowTm), "]");
|
||||||
LOG_DEBUG("Cron checking event [", eventName.c_str(), "] executionTime [", std::asctime(&next), "]");
|
LOG_DEBUG("Cron checking event [", eventName.c_str(), "] executionTime [", drivers::PCF85063::tm2str(next).c_str(), "]");
|
||||||
|
|
||||||
if (nextEventPoint <= nowPoint) // execution time hs passed, run event
|
if (nextEventPoint <= nowPoint) // execution time hs passed, run event
|
||||||
{
|
{
|
||||||
LOG_INFO("Cron executing event [", eventName.c_str(), "]");
|
next = cron::cron_next(cronexrp, nowTm); // update next execution time only if event was executed
|
||||||
commands::commandMap.at(cmd)(m_dev, params); // here the magic happens
|
// otherwise time tracking is lost
|
||||||
next = cron::cron_next(cronexrp, nowTm); // update next execution time only if event was executed
|
LOG_INFO("Cron running event [", eventName.c_str(), "] next execution time [", drivers::PCF85063::tm2str(next).c_str(), "]");
|
||||||
} // otherwise time tracking is lost
|
auto action = commands::s_commandMap.at(cmd)(m_dev, cmdParams); // here the magic happens
|
||||||
|
ArduinoJson::JsonDocument resp;
|
||||||
|
resp["cmd"] = "logCronJob";
|
||||||
|
resp["values"]["name"] = eventName;
|
||||||
|
resp["values"]["now"] = drivers::PCF85063::tm2str(nowTm).c_str();
|
||||||
|
resp["values"]["next"] = drivers::PCF85063::tm2str(next).c_str();
|
||||||
|
resp["values"]["action"] = action;
|
||||||
|
if (m_callback)
|
||||||
|
{
|
||||||
|
m_callback(resp);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -8,27 +8,51 @@
|
|||||||
#include <PCF85063_Driver.h>
|
#include <PCF85063_Driver.h>
|
||||||
|
|
||||||
#include <commands.h>
|
#include <commands.h>
|
||||||
|
#include <mqtt.h>
|
||||||
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <croncpp.h>
|
#include <croncpp.h>
|
||||||
|
|
||||||
class Cron
|
class Cron
|
||||||
{
|
{
|
||||||
|
public: // eventName cronExpression nextExec command parameters
|
||||||
|
using CronEvent = std::tuple<std::string, cron::cronexpr, std::tm, ArduinoJson::JsonDocument>;
|
||||||
|
using CronEventMap = std::map<std::string, CronEvent>;
|
||||||
|
using CronCallback = std::function<void(const ArduinoJson::JsonDocument &)>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Cron(devices_t &dev);
|
static Cron &getInstance(const devices_t &dev)
|
||||||
~Cron();
|
{
|
||||||
|
static Cron instance(dev);
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
const bool loadEvents(fs::File &file);
|
private:
|
||||||
const bool addEvent(const std::string &name, const std::string &command, const std::string &expr);
|
Cron(const devices_t &dev) : m_dev(dev) {};
|
||||||
|
Cron(const Cron &) = delete;
|
||||||
|
Cron &operator=(const Cron &) = delete;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void setResponseCallback(CronCallback &cb)
|
||||||
|
{
|
||||||
|
m_callback = cb;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool loadEvents();
|
||||||
|
const bool storeEvents();
|
||||||
|
const bool addEvent(const std::string &name, const std::string &expr, const ArduinoJson::JsonDocument action);
|
||||||
|
const bool getEvent(const std::string &name, CronEvent &event);
|
||||||
const bool delEvent(const std::string &name);
|
const bool delEvent(const std::string &name);
|
||||||
|
const CronEventMap &getAllEvents();
|
||||||
|
|
||||||
void startCron();
|
void startCron();
|
||||||
void stopCron();
|
void stopCron();
|
||||||
const bool processEvents();
|
const bool processEvents();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
devices_t &m_dev;
|
const devices_t &m_dev;
|
||||||
|
CronCallback m_callback;
|
||||||
|
CronEventMap m_cronMap;
|
||||||
TaskHandle_t m_cronTaskHandle;
|
TaskHandle_t m_cronTaskHandle;
|
||||||
std::map<std::string, std::tuple<std::string, cron::cronexpr, std::tm, ArduinoJson::JsonDocument>> m_cronMap;
|
std::mutex m_mutex;
|
||||||
};
|
};
|
||||||
@@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
|
drivers::Ethernet ð
|
||||||
drivers::PCF85063 &rtc;
|
drivers::PCF85063 &rtc;
|
||||||
drivers::R4DCB08 &tmp;
|
drivers::R4DCB08 &tmp;
|
||||||
drivers::S50140 &seneca;
|
drivers::S50140 &seneca;
|
||||||
|
|||||||
93
src/main.cpp
93
src/main.cpp
@@ -34,48 +34,33 @@ void loop()
|
|||||||
// Declared here to keep devices local to the main loop otherwise the kernel crashes //
|
// Declared here to keep devices local to the main loop otherwise the kernel crashes //
|
||||||
auto i2c = drivers::I2C();
|
auto i2c = drivers::I2C();
|
||||||
auto bus = drivers::MODBUS(9600, SERIAL_8N1);
|
auto bus = drivers::MODBUS(9600, SERIAL_8N1);
|
||||||
auto rtc = drivers::PCF85063(i2c, PCF85063_ADDRESS);
|
auto rtc = drivers::PCF85063(i2c);
|
||||||
auto eth = drivers::Ethernet(conf.m_ethHostname);
|
auto eth = drivers::Ethernet(conf.m_ethHostname, conf.m_ntpPool, conf.m_ntpTimezone, conf.m_ntpUpdateInterval);
|
||||||
auto tmp = drivers::R4DCB08(bus, conf.m_modbusTemperatureAddr);
|
auto tmp = drivers::R4DCB08(bus, conf.m_modbusTemperatureAddr);
|
||||||
auto seneca = drivers::S50140(bus, conf.m_modbusSenecaAddr);
|
auto seneca = drivers::S50140(bus, conf.m_modbusSenecaAddr);
|
||||||
auto buzzer = drivers::Buzzer();
|
auto buzzer = drivers::Buzzer();
|
||||||
auto led = drivers::Led();
|
auto led = drivers::Led();
|
||||||
delay(500);
|
delay(500);
|
||||||
auto io = digitalIO(i2c, bus, {conf.m_modbusRelayAddr});
|
auto io = digitalIO(i2c, bus, {conf.m_modbusRelayAddr});
|
||||||
|
// get RTC time drift offset value
|
||||||
|
rtc.setOffset(conf.m_ntpRtcOffsetRegister);
|
||||||
|
LOG_INFO("RTC offset register -> ", printHex(rtc.getOffset()).c_str());
|
||||||
// Initialize temperature sensors
|
// Initialize temperature sensors
|
||||||
sensors = tmp.getNum();
|
sensors = tmp.getNum();
|
||||||
tmp.setCorrection(conf.m_tempCorrectionValues);
|
tmp.setCorrection(conf.m_tempCorrectionValues);
|
||||||
LOG_INFO("Temperature sensors connected ->", sensors);
|
LOG_INFO("Temperature sensors connected ->", sensors);
|
||||||
|
|
||||||
// Create device structure to pass all devices in the callbacks as needed
|
// Create device structure to pass all devices in the callbacks as needed
|
||||||
devices_t devices(rtc, tmp, seneca, buzzer, led, io);
|
devices_t devices(eth, rtc, tmp, seneca, buzzer, led, io);
|
||||||
//////////////// DEVICES ////////////////
|
//////////////// DEVICES ////////////////
|
||||||
|
|
||||||
//////////////// NETWORK ////////////////
|
//////////////// MQTT ////////////////
|
||||||
auto mqtt = MQTTwrapper();
|
auto mqtt = MQTTwrapper();
|
||||||
//////////////// NETWORK ////////////////
|
//////////////// MQTT ////////////////
|
||||||
|
|
||||||
|
//////////////// MQTT //////////////
|
||||||
//////////////// CRONJOB ////////////////
|
|
||||||
auto cron = Cron(devices);
|
|
||||||
ArduinoJson::JsonDocument job;
|
|
||||||
job["cmd"] = "setIrrigation";
|
|
||||||
job["params"]["zone"] = "zone1";
|
|
||||||
job["params"]["timeOn"] = 30;
|
|
||||||
job["params"]["timePause"] = 2;
|
|
||||||
|
|
||||||
{
|
|
||||||
std::string buf;
|
|
||||||
serializeJson(job,buf);
|
|
||||||
LOG_INFO("Example Cronjob -> ", buf.c_str());
|
|
||||||
cron.addEvent("exampleCronEvent", buf, "0 */10 * * * *");
|
|
||||||
cron.startCron();
|
|
||||||
};
|
|
||||||
//////////////// CRONJOB ////////////////
|
|
||||||
|
|
||||||
//////////////// MQTT ////////////////
|
|
||||||
/////////////// CALLBACK //////////////
|
/////////////// CALLBACK //////////////
|
||||||
std::function<void(const ArduinoJson::JsonDocument &)> commandsCallback =
|
MQTTwrapper::ActionCallback commandsCallback =
|
||||||
[&mqtt, &devices](const ArduinoJson::JsonDocument &doc)
|
[&mqtt, &devices](const ArduinoJson::JsonDocument &doc)
|
||||||
{
|
{
|
||||||
if (!doc["cmd"].is<std::string>())
|
if (!doc["cmd"].is<std::string>())
|
||||||
@@ -84,11 +69,11 @@ void loop()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const std::string cmd = doc["cmd"].as<std::string>();
|
const std::string cmd = doc["cmd"].as<std::string>();
|
||||||
ArduinoJson::JsonDocument params = doc["params"];
|
const ArduinoJson::JsonDocument params = doc["params"];
|
||||||
if (commands::commandMap.contains(cmd))
|
if (commands::s_commandMap.contains(cmd))
|
||||||
{ // call command from command map in this same thread (the MQTT thread)
|
{ // call command from command map in this same thread (the MQTT thread)
|
||||||
LOG_INFO("Executing command", cmd.c_str());
|
LOG_INFO("Executing command", cmd.c_str());
|
||||||
auto answer = std::move(commands::commandMap.at(cmd)(devices, params)); // here the magic happens
|
const auto answer = std::move(commands::s_commandMap.at(cmd)(devices, params)); // here the magic happens
|
||||||
if (answer.isNull())
|
if (answer.isNull())
|
||||||
return;
|
return;
|
||||||
mqtt.publish(conf.m_mqttPublish["answers"], answer);
|
mqtt.publish(conf.m_mqttPublish["answers"], answer);
|
||||||
@@ -99,6 +84,34 @@ void loop()
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
MQTTwrapper::MessageCallback onMessage = [&devices](const MQTTwrapper::Topic &topic, const MQTTwrapper::Message &message)
|
||||||
|
{
|
||||||
|
LOG_DEBUG("onMessage callback [", topic.c_str(), "]");
|
||||||
|
devices.led.setColor(devices.led.COLOR_MAGENTA);
|
||||||
|
};
|
||||||
|
|
||||||
|
MQTTwrapper::MessageCallback onPublish = [&devices](const MQTTwrapper::Topic &topic, const MQTTwrapper::Message &message)
|
||||||
|
{
|
||||||
|
LOG_DEBUG("onPublish callback [", topic.c_str(), "]");
|
||||||
|
devices.led.setColor(devices.led.COLOR_SKYBLUE);
|
||||||
|
};
|
||||||
|
|
||||||
|
///////////// CRONJOB //////////////
|
||||||
|
/////////////// CALLBACK //////////////
|
||||||
|
Cron::CronCallback cronCallback = [&mqtt](const ArduinoJson::JsonDocument &resp)
|
||||||
|
{
|
||||||
|
if (resp.isNull())
|
||||||
|
return;
|
||||||
|
mqtt.publish(conf.m_mqttPublish["cronjobs"], resp);
|
||||||
|
};
|
||||||
|
|
||||||
|
//////////////// CRONJOB ////////////////
|
||||||
|
auto &cron = Cron::getInstance(devices);
|
||||||
|
cron.setResponseCallback(cronCallback);
|
||||||
|
cron.loadEvents();
|
||||||
|
cron.startCron();
|
||||||
|
//////////////// CRONJOB ////////////////
|
||||||
|
|
||||||
//////////////// NETWORK ////////////////
|
//////////////// NETWORK ////////////////
|
||||||
/////////////// CALLBACK ////////////////
|
/////////////// CALLBACK ////////////////
|
||||||
Network.onEvent(
|
Network.onEvent(
|
||||||
@@ -116,25 +129,31 @@ void loop()
|
|||||||
uint8_t mqttRetries(0);
|
uint8_t mqttRetries(0);
|
||||||
while (timeRetries++ < conf.m_ntpRetries)
|
while (timeRetries++ < conf.m_ntpRetries)
|
||||||
{
|
{
|
||||||
if (eth.getNtpTime(ntpTime) && rtc.setDatetime(drivers::PCF85063::fromEpoch(ntpTime)))
|
eth.setNtpTimeOffset(conf.m_ntpTimezone);
|
||||||
{
|
LOG_INFO("NTP Timezone UTC", conf.m_ntpTimezone >= 0 ? "+" : "", conf.m_ntpTimezone);
|
||||||
|
if (eth.getNtpTime(ntpTime))
|
||||||
|
{ // skip NTP update for drift testing
|
||||||
buzzer.beep(250, NOTE_A);
|
buzzer.beep(250, NOTE_A);
|
||||||
led.setColor(led.COLOR_ORANGE);
|
led.setColor(led.COLOR_ORANGE);
|
||||||
|
// rtc.setDatetime(drivers::PCF85063::fromEpoch(ntpTime));
|
||||||
const drivers::PCF85063::datetime_t dt(drivers::PCF85063::fromEpoch(ntpTime));
|
const drivers::PCF85063::datetime_t dt(drivers::PCF85063::fromEpoch(ntpTime));
|
||||||
LOG_INFO("NTP Time Update: ", drivers::PCF85063::datetime2str(dt).c_str());
|
LOG_INFO("NTP Time: ", drivers::PCF85063::datetime2str(dt).c_str());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
delay(100);
|
delay(250);
|
||||||
}
|
}
|
||||||
while (mqttRetries++ < conf.m_mqttRetries)
|
while (mqttRetries++ < conf.m_mqttRetries)
|
||||||
{
|
{
|
||||||
if (mqtt.connect())
|
if (mqtt.connect())
|
||||||
{
|
{
|
||||||
|
buzzer.beep(250, NOTE_B);
|
||||||
led.setColor(led.COLOR_GREEN);
|
led.setColor(led.COLOR_GREEN);
|
||||||
mqtt.subscribe(conf.m_mqttSubscribe["commands"], commandsCallback);
|
mqtt.subscribe(conf.m_mqttSubscribe["commands"], commandsCallback);
|
||||||
|
mqtt.setOnMessageCb(onMessage);
|
||||||
|
mqtt.setOnPublishCb(onPublish);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
delay(100);
|
delay(250);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -145,8 +164,10 @@ void loop()
|
|||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
const uint32_t start(millis());
|
const uint32_t start(millis());
|
||||||
const std::string timeStr(rtc.getTimeStr());
|
drivers::PCF85063::datetime_t datetime;
|
||||||
LOG_INFO("[", k++, "] Loop - Current Datetime", timeStr.c_str());
|
rtc.readDatetime(datetime);
|
||||||
|
const std::string timeStr(drivers::PCF85063::datetime2str(datetime));
|
||||||
|
LOG_INFO("[", k++, "] Loop - Current Datetime UTC", timeStr.c_str());
|
||||||
|
|
||||||
{
|
{
|
||||||
ArduinoJson::JsonDocument poll;
|
ArduinoJson::JsonDocument poll;
|
||||||
@@ -178,7 +199,7 @@ void loop()
|
|||||||
{
|
{
|
||||||
LOG_WARN("RESTART!");
|
LOG_WARN("RESTART!");
|
||||||
buzzer.beep(450, NOTE_D);
|
buzzer.beep(450, NOTE_D);
|
||||||
delay(100);
|
delay(450);
|
||||||
esp_restart();
|
esp_restart();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
30
src/mqtt.cpp
30
src/mqtt.cpp
@@ -44,7 +44,7 @@ const bool MQTTwrapper::disconnect()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool MQTTwrapper::subscribe(topic_t topic, action_t action)
|
const bool MQTTwrapper::subscribe(const Topic &topic, const ActionCallback action)
|
||||||
{
|
{
|
||||||
if (m_actionMap.contains(topic))
|
if (m_actionMap.contains(topic))
|
||||||
{
|
{
|
||||||
@@ -61,7 +61,7 @@ const bool MQTTwrapper::subscribe(topic_t topic, action_t action)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool MQTTwrapper::unsubscribe(topic_t topic)
|
const bool MQTTwrapper::unsubscribe(const Topic &topic)
|
||||||
{
|
{
|
||||||
if (!m_actionMap.contains(topic))
|
if (!m_actionMap.contains(topic))
|
||||||
{
|
{
|
||||||
@@ -83,7 +83,7 @@ const bool MQTTwrapper::connected()
|
|||||||
return m_loopHandle != NULL;
|
return m_loopHandle != NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool MQTTwrapper::publish(topic_t topic, const ArduinoJson::JsonDocument obj)
|
const bool MQTTwrapper::publish(const Topic &topic, const ArduinoJson::JsonDocument obj)
|
||||||
{
|
{
|
||||||
std::string message;
|
std::string message;
|
||||||
if (!m_client.connected())
|
if (!m_client.connected())
|
||||||
@@ -99,12 +99,32 @@ const bool MQTTwrapper::publish(topic_t topic, const ArduinoJson::JsonDocument o
|
|||||||
if (m_client.publish(topic.c_str(), message.c_str()))
|
if (m_client.publish(topic.c_str(), message.c_str()))
|
||||||
{
|
{
|
||||||
LOG_DEBUG("MQTT published topic [", topic.c_str(), "] - message [", message.c_str(), "]");
|
LOG_DEBUG("MQTT published topic [", topic.c_str(), "] - message [", message.c_str(), "]");
|
||||||
|
if (m_onPublish)
|
||||||
|
{
|
||||||
|
m_onPublish(topic, message);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
LOG_ERROR("MQTT failed to publish topic [", topic.c_str(), "] - message [", message.c_str(), "]");
|
LOG_ERROR("MQTT failed to publish topic [", topic.c_str(), "] - message [", message.c_str(), "]");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MQTTwrapper::setOnMessageCb(MessageCallback cb)
|
||||||
|
{
|
||||||
|
if (cb)
|
||||||
|
m_onReceive = cb;
|
||||||
|
else
|
||||||
|
LOG_ERROR("MQTT invalid onReceive Callback");
|
||||||
|
}
|
||||||
|
|
||||||
|
void MQTTwrapper::setOnPublishCb(MessageCallback cb)
|
||||||
|
{
|
||||||
|
if (cb)
|
||||||
|
m_onPublish = cb;
|
||||||
|
else
|
||||||
|
LOG_ERROR("MQTT invalid onPublish Callback");
|
||||||
|
}
|
||||||
|
|
||||||
void MQTTwrapper::callback(char *topic, uint8_t *payload, unsigned int length)
|
void MQTTwrapper::callback(char *topic, uint8_t *payload, unsigned int length)
|
||||||
{
|
{
|
||||||
std::string pl;
|
std::string pl;
|
||||||
@@ -120,13 +140,15 @@ void MQTTwrapper::callback(char *topic, uint8_t *payload, unsigned int length)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MQTTwrapper::onMessage(const std::string topic, const std::string message)
|
void MQTTwrapper::onMessage(const Topic topic, const Message message)
|
||||||
{
|
{
|
||||||
ArduinoJson::JsonDocument obj;
|
ArduinoJson::JsonDocument obj;
|
||||||
LOG_DEBUG("MQTT received topic [", topic.c_str(), "] - message [", message.c_str(), "]");
|
LOG_DEBUG("MQTT received topic [", topic.c_str(), "] - message [", message.c_str(), "]");
|
||||||
if (ArduinoJson::deserializeJson(obj, message) == ArduinoJson::DeserializationError::Ok)
|
if (ArduinoJson::deserializeJson(obj, message) == ArduinoJson::DeserializationError::Ok)
|
||||||
{
|
{
|
||||||
m_actionMap[topic](obj);
|
m_actionMap[topic](obj);
|
||||||
|
if (m_onReceive)
|
||||||
|
m_onReceive(topic, message);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
LOG_ERROR("MQTT failed to deserialize message\n", message.c_str());
|
LOG_ERROR("MQTT failed to deserialize message\n", message.c_str());
|
||||||
|
|||||||
31
src/mqtt.h
31
src/mqtt.h
@@ -1,5 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#define DEBUGLOG_DEFAULT_LOG_LEVEL_DEBUG
|
||||||
|
|
||||||
#include <DebugLog.h>
|
#include <DebugLog.h>
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <ArduinoJson.h>
|
#include <ArduinoJson.h>
|
||||||
@@ -11,12 +13,16 @@
|
|||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
typedef std::string topic_t;
|
|
||||||
typedef std::function<void(const ArduinoJson::JsonDocument &)> action_t; // the actions receive a JsonObject containing the received message
|
|
||||||
typedef std::map<topic_t, action_t> action_map_t;
|
|
||||||
|
|
||||||
class MQTTwrapper
|
class MQTTwrapper
|
||||||
{
|
{
|
||||||
|
public:
|
||||||
|
using Topic = std::string;
|
||||||
|
using Message = std::string;
|
||||||
|
using MessageCallback = std::function<void(const Topic &topic, const Message &message)>;
|
||||||
|
using ActionCallback = std::function<void(const ArduinoJson::JsonDocument &)>; // the actions receive a JsonObject containing the received message
|
||||||
|
using StateChangeCallback = std::function<void(void)>;
|
||||||
|
|
||||||
|
using ActionMap = std::map<Topic, ActionCallback>;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const std::map<int, std::string> stateMap = {
|
const std::map<int, std::string> stateMap = {
|
||||||
@@ -29,8 +35,7 @@ private:
|
|||||||
{2, "MQTT_CONNECT_BAD_CLIENT_ID"},
|
{2, "MQTT_CONNECT_BAD_CLIENT_ID"},
|
||||||
{3, "MQTT_CONNECT_UNAVAILABLE"},
|
{3, "MQTT_CONNECT_UNAVAILABLE"},
|
||||||
{4, "MQTT_CONNECT_BAD_CREDENTIALS"},
|
{4, "MQTT_CONNECT_BAD_CREDENTIALS"},
|
||||||
{5, "MQTT_CONNECT_UNAUTHORIZED"}
|
{5, "MQTT_CONNECT_UNAUTHORIZED"}};
|
||||||
};
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static MQTTwrapper *
|
static MQTTwrapper *
|
||||||
@@ -52,10 +57,13 @@ public:
|
|||||||
const bool disconnect();
|
const bool disconnect();
|
||||||
const bool connected();
|
const bool connected();
|
||||||
|
|
||||||
const bool subscribe(topic_t topic, action_t action);
|
const bool subscribe(const Topic &topic, const ActionCallback action);
|
||||||
const bool unsubscribe(topic_t topic);
|
const bool unsubscribe(const Topic &topic);
|
||||||
|
|
||||||
const bool publish(topic_t topic, const ArduinoJson::JsonDocument obj);
|
const bool publish(const Topic &topic, const ArduinoJson::JsonDocument obj);
|
||||||
|
|
||||||
|
void setOnMessageCb(MessageCallback cb);
|
||||||
|
void setOnPublishCb(MessageCallback cb);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void callback(char *topic, uint8_t *payload, unsigned int length); // C-style callback only to invoke onMessage
|
static void callback(char *topic, uint8_t *payload, unsigned int length); // C-style callback only to invoke onMessage
|
||||||
@@ -66,8 +74,11 @@ private:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
const Config &m_config;
|
const Config &m_config;
|
||||||
action_map_t m_actionMap;
|
ActionMap m_actionMap;
|
||||||
NetworkClient m_tcp;
|
NetworkClient m_tcp;
|
||||||
PubSubClient m_client;
|
PubSubClient m_client;
|
||||||
TaskHandle_t m_loopHandle;
|
TaskHandle_t m_loopHandle;
|
||||||
|
|
||||||
|
MessageCallback m_onPublish;
|
||||||
|
MessageCallback m_onReceive;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user