4 Commits

Author SHA1 Message Date
Emanuele Trabattoni
4a1e944ea2 set ntp parameters via config file 2025-08-03 12:11:00 +02:00
Emanuele Trabattoni
a1a66ebf8e RTC fix time lag correction 2025-08-03 11:27:07 +02:00
Emanuele Trabattoni
b19ed89158 clock correction 2025-08-02 18:04:40 +02:00
Emanuele Trabattoni
25251785fa led flash not working ma vabbeh 2025-08-02 17:39:02 +02:00
10 changed files with 77 additions and 28 deletions

Binary file not shown.

View File

@@ -56,7 +56,7 @@ public:
file.close(); // close config file before unmounting filesystem
};
ArduinoJson::JsonDocument& getConfig()
ArduinoJson::JsonDocument &getConfig()
{
std::lock_guard<std::mutex> lock(m_mutex);
serialize();
@@ -152,6 +152,7 @@ private:
ntp["timezone"] = m_ntpTimezone;
ntp["updateInterval"] = m_ntpUpdateInterval;
ntp["retries"] = m_ntpRetries;
ntp["ntpRtcOffsetRegister"] = m_ntpRtcOffsetRegister;
};
{
@@ -220,9 +221,10 @@ private:
{
auto ntp = m_configJson["ntp"];
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_ntpRetries = ntp["retries"].as<uint8_t>();
m_ntpRtcOffsetRegister = ntp["ntpRtcOffsetRegister"].as<uint8_t>();
};
{
@@ -272,9 +274,10 @@ public:
// NTP
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
uint8_t m_ntpRetries = 5;
uint8_t m_ntpRtcOffsetRegister = 0xE7; // -25 pulses in fast mode
// MQTT
std::string m_mqttHost = "10.0.2.249";

View File

@@ -3,12 +3,12 @@
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);
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();
}
@@ -30,6 +30,17 @@ namespace drivers
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()
{
return m_connected;

View File

@@ -30,15 +30,17 @@ namespace drivers
{
public:
Ethernet(const std::string hostname);
Ethernet(const std::string &hostname, const std::string &ntpPool, const int8_t tz, const uint16_t updateInterval);
~Ethernet();
void onEvent(arduino_event_id_t event, arduino_event_info_t info);
const bool isConnected();
const bool getNtpTime(time_t &time);
const bool setNtpTimeOffset(const int8_t tz);
private:
const std::string m_hostname;
const std::string m_ntpPool;
bool m_connected;
NetworkUDP m_udp;
IPAddress m_localIP;

View File

@@ -31,23 +31,27 @@ namespace drivers
void Led::flashHandle(TimerHandle_t th)
{
Led *led = (Led *)pvTimerGetTimerID(th);
std::lock_guard<std::mutex> lock(led->m_ledMutex);
rgbLedWrite(led->c_ledPin, led->m_colorDefault.g, led->m_colorDefault.r, led->m_colorDefault.b); // reset color to saved color
LOG_DEBUG("Led Flash timer expired");
xTimerDelete(th, 0);
led->m_flashTimer = NULL;
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)
{
blinkStop();
rgbLedWrite(c_ledPin, color.g, color.r, color.b); // set color to flash
m_flashTimer = xTimerCreate("flasher", pdMS_TO_TICKS(tOn), pdFALSE, this, flashHandle);
m_flashTimer = xTimerCreate("flasher", pdMS_TO_TICKS(tOn), pdFALSE, NULL, flashHandle);
xTimerStart(m_flashTimer, 0);
LOG_DEBUG("Led Flash timer created");
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;
}
}

View File

@@ -59,6 +59,8 @@ namespace drivers
TaskHandle_t m_blinkTask;
TimerHandle_t m_flashTimer;
bool m_flashing;
std::mutex m_ledMutex;
};

View File

@@ -1,5 +1,6 @@
#include "PCF85063_Driver.h"
#include <ctime>
#include <utils.h>
namespace drivers
{
@@ -157,6 +158,23 @@ namespace drivers
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()
{
datetime_t dt;
@@ -182,16 +200,17 @@ namespace drivers
{
tm dtime = datetime2tm(datetime);
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::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')));
return buf.substr(0, std::min(buf.find('\n'), buf.find('\r')));
}
const std::tm PCF85063::datetime2tm(const datetime_t& datetime) {
const std::tm PCF85063::datetime2tm(const datetime_t &datetime)
{
tm dtime;
dtime.tm_sec = datetime.second;
dtime.tm_min = datetime.minute;

View File

@@ -83,7 +83,7 @@ namespace drivers
} datetime_t;
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);
@@ -100,6 +100,9 @@ namespace drivers
const bool readAlarm(datetime_t &time);
const bool getAlarmFlag(uint8_t &flags);
const bool setOffset(const uint8_t ofst);
const uint8_t getOffset();
const std::string getTimeStr();
static const std::string datetime2str(const datetime_t &datetime);

View File

@@ -108,7 +108,6 @@ namespace commands
response["cmd"] = "getCronJob";
auto &cron = Cron::getInstance(dev);
auto eventName = params["name"].as<std::string>();
response["values"]["name"] = eventName;
if (eventName.empty())
{
@@ -132,6 +131,7 @@ namespace commands
}
Cron::CronEvent event;
response["values"]["name"] = eventName;
if (!cron.getEvent(eventName, event))
{
LOG_ERROR("getCronJob failed to get job [", eventName.c_str(), "]");

View File

@@ -34,14 +34,17 @@ void loop()
// 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, PCF85063_ADDRESS);
auto eth = drivers::Ethernet(conf.m_ethHostname);
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();
delay(500);
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
sensors = tmp.getNum();
tmp.setCorrection(conf.m_tempCorrectionValues);
@@ -83,14 +86,14 @@ void loop()
MQTTwrapper::MessageCallback onMessage = [&devices](const MQTTwrapper::Topic &topic, const MQTTwrapper::Message &message)
{
LOG_INFO("onMessage callback [", topic.c_str(),"]");
devices.led.flashColor(250, devices.led.COLOR_YELLOW);
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_INFO("onPublish callback [", topic.c_str(),"]");
devices.led.flashColor(250, devices.led.COLOR_BLUE);
LOG_DEBUG("onPublish callback [", topic.c_str(), "]");
devices.led.setColor(devices.led.COLOR_SKYBLUE);
};
///////////// CRONJOB //////////////
@@ -126,6 +129,8 @@ void loop()
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);
@@ -144,8 +149,8 @@ void loop()
buzzer.beep(250, NOTE_B);
led.setColor(led.COLOR_GREEN);
mqtt.subscribe(conf.m_mqttSubscribe["commands"], commandsCallback);
//mqtt.setOnMessageCb(onMessage);
//mqtt.setOnPublishCb(onPublish);
mqtt.setOnMessageCb(onMessage);
mqtt.setOnPublishCb(onPublish);
break;
}
delay(250);