244 lines
7.7 KiB
C++
244 lines
7.7 KiB
C++
#define DEBUGLOG_DEFAULT_LOG_LEVEL_INFO
|
|
|
|
#include <Arduino.h>
|
|
#include <DebugLog.h>
|
|
#include <DebugLogEnable.h>
|
|
|
|
#include <config.h>
|
|
#include <commands.h>
|
|
#include <cronjobs.h>
|
|
#include <mqtt.h>
|
|
#include <ota.h>
|
|
|
|
#include <devices.h>
|
|
#include <utils.h>
|
|
#include <pinlist.h>
|
|
|
|
/////////////// GLOBALS ///////////////
|
|
Config &conf = Config::getInstance();
|
|
/////////////// GLOBALS ///////////////
|
|
|
|
void setup()
|
|
{
|
|
Serial.begin(9600);
|
|
LOG_ATTACH_SERIAL(Serial);
|
|
LOG_SET_LEVEL(DebugLogLevel::LVL_INFO);
|
|
conf.init(); // read the configuration from internal flash
|
|
LOG_INFO("ESP32 Chip:", ESP.getChipModel());
|
|
LOG_INFO("ESP32 PSram:", ESP.getPsramSize());
|
|
LOG_INFO("ESP32 Flash:", ESP.getFlashChipSize());
|
|
LOG_INFO("ESP32 Heap:", ESP.getHeapSize());
|
|
LOG_INFO("ESP32 Sketch:", ESP.getFreeSketchSpace());
|
|
}
|
|
|
|
void loop()
|
|
{
|
|
uint16_t k(0);
|
|
uint8_t sensors(0);
|
|
bool buzzing(false);
|
|
NetworkClient logStream;
|
|
LOG_ATTACH_STREAM(logStream);
|
|
|
|
//////////////// DEVICES ////////////////
|
|
// Declared here to keep devices local to the main loop otherwise the kernel crashes //
|
|
auto i2c = drivers::I2C();
|
|
auto bus = drivers::MODBUS(9600, SERIAL_8N1);
|
|
auto rtc = drivers::PCF85063(i2c);
|
|
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 seneca = drivers::S50140(bus, conf.m_modbusSenecaAddr);
|
|
auto buzzer = drivers::Buzzer();
|
|
auto led = drivers::Led();
|
|
auto io = digitalIO(i2c, bus, {conf.m_modbusRelayAddr});
|
|
// Create device structure to pass all devices in the callbacks as needed
|
|
devices_t devices(eth, rtc, tmp, seneca, buzzer, led, io);
|
|
//
|
|
// get RTC time drift offset value
|
|
rtc.setOffset(conf.m_ntpRtcOffsetRegister);
|
|
LOG_INFO("RTC offset register -> ", printHex(rtc.getOffset()).c_str());
|
|
// Initialize temperature sensors
|
|
sensors = tmp.getNum();
|
|
tmp.setCorrection(conf.m_tempCorrectionValues);
|
|
LOG_INFO("Temperature sensors connected ->", sensors);
|
|
// Initialize OTA updater if needed
|
|
auto ota = OTA(devices);
|
|
//////////////// DEVICES ////////////////
|
|
|
|
//////////////// MQTT ////////////////
|
|
auto mqtt = MQTTwrapper();
|
|
//////////////// MQTT ////////////////
|
|
|
|
//////////////// MQTT //////////////
|
|
/////////////// CALLBACK //////////////
|
|
MQTTwrapper::ActionCallback commandsCallback =
|
|
[&mqtt, &devices](const ArduinoJson::JsonDocument &doc)
|
|
{
|
|
if (!doc["cmd"].is<std::string>())
|
|
{
|
|
LOG_ERROR("Invalid Json Command");
|
|
return;
|
|
}
|
|
const std::string cmd = doc["cmd"].as<std::string>();
|
|
const ArduinoJson::JsonDocument params = doc["params"];
|
|
if (commands::s_commandMap.contains(cmd))
|
|
{ // call command from command map in this same thread (the MQTT thread)
|
|
LOG_INFO("Executing command", cmd.c_str());
|
|
const auto answer = std::move(commands::s_commandMap.at(cmd)(devices, params)); // here the magic happens
|
|
if (answer.isNull())
|
|
return;
|
|
mqtt.publish(conf.m_mqttPublish["answers"], answer);
|
|
}
|
|
else
|
|
{
|
|
LOG_ERROR("Unknown command", cmd.c_str());
|
|
}
|
|
};
|
|
|
|
MQTTwrapper::MessageCallback onMessage = [&devices](const MQTTwrapper::Topic &topic, const MQTTwrapper::Message &message)
|
|
{
|
|
LOG_DEBUG("onMessage callback [", topic.c_str(), "]\n", message.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(), "]\n", message.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 ////////////////
|
|
/////////////// CALLBACK ////////////////
|
|
Network.onEvent(
|
|
[&](arduino_event_id_t event, arduino_event_info_t info) -> void
|
|
{
|
|
eth.onEvent(event, info); // Arduino Ethernet event handler
|
|
if (!eth.isConnected())
|
|
{
|
|
led.setColor(led.COLOR_RED);
|
|
logStream.stop();
|
|
return;
|
|
}
|
|
if (io.digitalInRead(DI::OTAENABLE)) // Initialize OTA, BLUE
|
|
{
|
|
buzzer.beepRepeat(25, 25, NOTE_A);
|
|
delay(1000);
|
|
if (io.digitalInRead(DI::OTAENABLE))
|
|
{ // maintain keyPress for 1s
|
|
ota.begin();
|
|
}
|
|
buzzer.beep(100, NOTE_G);
|
|
delay(100);
|
|
}
|
|
// Get RTC time at ethernet connection
|
|
time_t ntpTime;
|
|
uint8_t timeRetries(0);
|
|
uint8_t mqttRetries(0);
|
|
while (timeRetries++ < conf.m_ntpRetries)
|
|
{
|
|
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);
|
|
led.setColor(led.COLOR_ORANGE);
|
|
const drivers::PCF85063::datetime_t dt(drivers::PCF85063::fromEpoch(ntpTime));
|
|
LOG_INFO("NTP Time: ", drivers::PCF85063::datetime2str(dt).c_str());
|
|
break;
|
|
}
|
|
delay(250);
|
|
}
|
|
while (mqttRetries++ < conf.m_mqttRetries)
|
|
{
|
|
if (mqtt.connect())
|
|
{
|
|
buzzer.beep(250, NOTE_B);
|
|
led.setColor(led.COLOR_GREEN);
|
|
mqtt.subscribe(conf.m_mqttSubscribe["commands"], commandsCallback);
|
|
mqtt.setOnMessageCb(onMessage);
|
|
mqtt.setOnPublishCb(onPublish);
|
|
break;
|
|
}
|
|
delay(250);
|
|
}
|
|
});
|
|
|
|
////////////////////////////////////////
|
|
///////// MAIN LOOP INSIDE LOOP ////////
|
|
////////////////////////////////////////
|
|
|
|
while (true)
|
|
{
|
|
const uint32_t start(millis());
|
|
drivers::PCF85063::datetime_t datetime;
|
|
|
|
if (!logStream.connected())
|
|
{
|
|
logStream.stop();
|
|
logStream.clearWriteError();
|
|
logStream.setConnectionTimeout(100);
|
|
logStream.connect(conf.m_mqttHost.c_str(), 9876);
|
|
LOG_WARN("TCP LogStream Connected");
|
|
}
|
|
|
|
rtc.readDatetime(datetime);
|
|
const std::string timeStr(drivers::PCF85063::datetime2str(datetime));
|
|
LOG_INFO("[", k++, "] Loop - Current Datetime UTC", timeStr.c_str());
|
|
|
|
{
|
|
ArduinoJson::JsonDocument poll;
|
|
poll["cmd"] = "POLL";
|
|
auto params = poll["values"].to<ArduinoJson::JsonObject>();
|
|
params["time"] = timeStr;
|
|
params["number"] = k;
|
|
mqtt.publish(conf.m_mqttPublish["answers"], poll);
|
|
};
|
|
|
|
{
|
|
ArduinoJson::JsonDocument ti;
|
|
auto tempinfo = tmp.getTempAll();
|
|
ti["solar"] = tempinfo.at(0);
|
|
ti["acs"] = tempinfo.at(1);
|
|
ti["heating"] = tempinfo.at(2);
|
|
mqtt.publish(conf.m_mqttPublish["temperatures"], ti);
|
|
};
|
|
|
|
if (io.digitalInRead(DI::CONFRESET)) // ROSSO - Config Reset
|
|
{
|
|
LOG_WARN("Config RESET!");
|
|
buzzer.beep(450, NOTE_E);
|
|
delay(500);
|
|
conf.resetConfig();
|
|
}
|
|
|
|
if (io.digitalInRead(DI::RESTART)) // GIALLO - Restart
|
|
{
|
|
LOG_WARN("RESTART!");
|
|
buzzer.beep(450, NOTE_D);
|
|
delay(450);
|
|
esp_restart();
|
|
}
|
|
|
|
delay(conf.m_globalLoopDelay - (start - millis())); // to avoid too fast loop, keep precise timing computing loop time
|
|
}
|
|
|
|
////////////////////////////////////////
|
|
///////// MAIN LOOP INSIDE LOOP ////////
|
|
////////////////////////////////////////
|
|
}
|