Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support running without RTC and add option to set ntp interval for Ring front #31

Draft
wants to merge 9 commits into
base: main
Choose a base branch
from
11 changes: 11 additions & 0 deletions software/wordclock/platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,17 @@ lib_deps =
makuna/NeoPixelBus@^2.7.7
256dpi/MQTT@^2.5.1

[env:ring]
platform = espressif32
board = esp32-c3-devkitm-1
framework = arduino
monitor_dtr = 0
build_flags = -DDEBUG -DRING
lib_deps =
https://github.com/adafruit/RTClib.git
https://github.com/Makuna/NeoPixelBus.git
https://github.com/prampec/IotWebConf.git

[env:nodo1]
platform = espressif32
board = pico32
Expand Down
23 changes: 23 additions & 0 deletions software/wordclock/src/src/ClockFace.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -968,3 +968,26 @@ case 0:
}
return true;
}





bool RingClockFace::stateForTime(int hour, int minute, int second, bool show_ampm)
{
if (hour == _hour && minute == _minute)
{
return false;
}
_hour = hour;
_minute = minute;

DLOGLN("update state");
clearDisplay();
int h = (hour%12)*2+((minute >= 30)?1:0);
updateSegment(h,0,1);
updateSegment(24+minute,0,1);


return true;
}
12 changes: 11 additions & 1 deletion software/wordclock/src/src/ClockFace.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class ClockFace
const std::vector<bool> &getState() { return _state; };

// Returns the index of the LED in the strip given a position on the grid.
uint16_t map(int16_t x, int16_t y);
virtual uint16_t map(int16_t x, int16_t y);

// The first four LED are the corner ones, counting minutes. They are assumed
// to be wired in clockwise order, starting from the light sensor position.
Expand Down Expand Up @@ -93,3 +93,13 @@ class ItalianClockFace : public ClockFace

virtual bool stateForTime(int hour, int minute, int second, bool show_ampm);
};


class RingClockFace : public ClockFace
{
public:
RingClockFace(LightSensorPosition position) : ClockFace(position){};

virtual bool stateForTime(int hour, int minute, int second, bool show_ampm);
virtual uint16_t map(int16_t x, int16_t y) {return x;}
};
144 changes: 96 additions & 48 deletions software/wordclock/src/src/Iot.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
extern "C" {
#include <esp32-hal-time.c>
}
#include "logging.h"
#include "Iot.h"
#include <IotWebConfESP32HTTPUpdateServer.h>
Expand Down Expand Up @@ -25,8 +28,6 @@
#define HTTP_OK 200
// NTP server.
#define NTP_SERVER "pool.ntp.org"
// When NTP is enabled, define how often to update the RTC.
#define NTP_POLL_DELAY_SECONDS 86400 // Once a day is plenty enough.

namespace
{
Expand Down Expand Up @@ -223,7 +224,11 @@ body > div > div:last-child {\
{
DLOG("[INFO] Could not parse number value \"");
DLOG(str);
DLOGLN("\".");
DLOG("\" ");
DLOG(min_value);
DLOG(" <, >");
DLOG(max_value);
DLOGLN(".");
return default_value;
}
return parsed_value;
Expand Down Expand Up @@ -287,6 +292,9 @@ Iot::Iot(Display *display, RTC_DS3231 *rtc)
"style='border-width: 1px; padding: 1px;'"),

time_group_("time_group", "Time"),
ntp_interval_param_(
"Refresh interval in hours for ntp synchronization (requires WiFi)", "ntp_interval", ntp_interval_value_,
IOT_CONFIG_VALUE_LENGTH, "24", 1, 24,1, "data-controlledby='ntp_enabled' data-showon='1'"),
ntp_enabled_param_(
"Use network time (requires WiFi)", "ntp_enabled", ntp_enabled_value_,
IOT_CONFIG_VALUE_LENGTH, "0", 0, 1, 1, "style='width: 40px;' data-labels='Off|On'"),
Expand All @@ -311,6 +319,7 @@ Iot::Iot(Display *display, RTC_DS3231 *rtc)
this->ldr_sensitivity_value_[0] = '\0';
this->color_value_[0] = '\0';
this->ntp_enabled_value_[0] = '\0';
this->ntp_interval_value_[0] = '\0';
this->timezone_value_[0] = '\0';
this->mqtt_enabled_value_[0] = '\0';
this->mqtt_server_value_[0] = '\0';
Expand All @@ -330,39 +339,45 @@ void Iot::clearTransientParams_()
// workaround of representing booleans as 0 or 1 integers.
void Iot::updateClockFromParams_()
{
switch (parseNumberValue(clockface_language_value_, 0, 10, 0))
{
case 1:
{
display_->setClockFace(&clockFaceNL);
DLOGLN("Language set to Dutch");
break;
}
case 2:
{
display_->setClockFace(&clockFaceFR);
DLOGLN("Language set to French");
break;
}
case 3:
{
display_->setClockFace(&clockFaceIT);
DLOGLN("Language set to Italian");
break;
}
default:
{
display_->setClockFace(&clockFaceEN);
DLOGLN("Language set to English");
break;
}
switch(parseNumberValue(clockface_language_value_, 0, 10, 0)) {
case 1:
{
display_->setClockFace(&clockFaceNL);
DLOGLN("Language set to Dutch");
break;
}
case 2:
{
display_->setClockFace(&clockFaceFR);
DLOGLN("Language set to French");
break;
}
case 3:
{
display_->setClockFace(&clockFaceIT);
DLOGLN("Language set to Italian");
break;
}
case 4:
{
display_->setClockFace(&clockFaceRING);
DLOGLN("Language set to Ring");
break;
}
default:
{
display_->setClockFace(&clockFaceEN);
DLOGLN("Language set to English");
break;
}
}

display_->setColor(
parseColorValue(color_value_, RgbColor(255, 255, 255)));
display_->setShowAmPm(parseBooleanValue(show_ampm_value_));
display_->setSensorSentivity(parseNumberValue(ldr_sensitivity_value_, 0, 10, 5));

this->ntp_poll_interval_ = parseNumberValue(ntp_enabled_value_, 1,24,24);
if (parseBooleanValue(ntp_enabled_value_))
{
ntp_poll_timer_.start();
Expand All @@ -381,7 +396,7 @@ void Iot::setup()

ntp_poll_timer_.setup([this]()
{ maybeSetRTCfromNTP_(); },
NTP_POLL_DELAY_SECONDS);
ntp_poll_interval_*60*60);

this->show_ampm_value_[0] = '\0';
this->ldr_sensitivity_value_[0] = '\0';
Expand All @@ -407,6 +422,7 @@ void Iot::setup()
iot_web_conf_.addParameterGroup(&display_group_);

time_group_.addItem(&ntp_enabled_param_);
time_group_.addItem(&ntp_interval_param_);
time_group_.addItem(&timezone_param_);
time_group_.addItem(&manual_time_param_);
iot_web_conf_.addParameterGroup(&time_group_);
Expand Down Expand Up @@ -504,6 +520,9 @@ void Iot::maybeSetRTCfromNTP_()
}

int tz = parseNumberValue(timezone_value_, 0, 459, 0);
#if SNTP_GET_SERVERS_FROM_DHCP || SNTP_GET_SERVERS_FROM_DHCPV6
sntp_servermode_dhcp(1);
#endif
configTzTime(posix[tz], NTP_SERVER);

struct tm timeinfo;
Expand All @@ -520,7 +539,10 @@ void Iot::maybeSetRTCfromNTP_()
digitalWrite(LED_PIN, HIGH);
#endif

rtc_->adjust(DateTime(timeinfo.tm_year, timeinfo.tm_mon, timeinfo.tm_mday, timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec));
if (get_rtc_found()) {
rtc_->adjust(DateTime(timeinfo.tm_year, timeinfo.tm_mon, timeinfo.tm_mday,
timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec));
}
DLOG("RTC set to:");
DLOGLN(&timeinfo, "%A, %B %d %Y %H:%M:%S");
DLOG("Timezone:");
Expand All @@ -529,23 +551,49 @@ void Iot::maybeSetRTCfromNTP_()

void Iot::setRTCfromConfig_()
{
const DateTime now = rtc_->now();
uint16_t year = now.year();
uint8_t month = now.month();
uint8_t day = now.day();
uint8_t hour = now.hour();
uint8_t minute = now.minute();
uint8_t second = now.second();
bool datetime_changed = false;

if (manual_time_value_[0] != 0 && parseTimeValue(manual_time_value_, &hour, &minute, &second))
{
datetime_changed = true;
}
if (get_rtc_found()) {
const DateTime now = rtc_->now();
uint16_t year = now.year();
uint8_t month = now.month();
uint8_t day = now.day();
uint8_t hour = now.hour();
uint8_t minute = now.minute();
uint8_t second = now.second();
bool datetime_changed = false;

if (manual_time_value_[0] != 0 &&
parseTimeValue(manual_time_value_, &hour, &minute, &second)) {
datetime_changed = true;
}

if (datetime_changed)
{
rtc_->adjust(DateTime(year, month, day, hour, minute, second));
if (datetime_changed) {
rtc_->adjust(DateTime(year, month, day, hour, minute, second));
}
} else {
uint16_t year = 0;
uint8_t month = 0;
uint8_t day = 0;
uint8_t hour = 0;
uint8_t minute = 0;
uint8_t second = 0;
if (manual_time_value_[0] != 0 &&
parseTimeValue(manual_time_value_, &hour, &minute, &second)) {
struct tm timeinfo = {0, };
if (!getLocalTime(&timeinfo))
{
DLOG("Local time invalid. set some 'usable' data");
timeinfo.tm_year = (2016 - 1900)+1;
timeinfo.tm_mon = 1;
}
timeinfo.tm_hour = hour;
timeinfo.tm_min = minute;
timeinfo.tm_sec = second;
time_t t = mktime(&timeinfo);
struct timeval tv;
tv.tv_usec = 0;
tv.tv_sec = t;
settimeofday(&tv, NULL);
}
}
}

Expand Down Expand Up @@ -650,4 +698,4 @@ void Iot::mqttMessageReceived_(String &topic, String &payload)
// color_value_, payload.c_str(),
// IOT_CONFIG_VALUE_LENGTH);
// iot_web_conf_.saveConfig();
}
}
14 changes: 13 additions & 1 deletion software/wordclock/src/src/Iot.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ class Iot
void setup();
void loop();

// accessor for rtc_found
bool get_rtc_found() { return rtc_found_; }
void set_rtc_found(bool b) { rtc_found_ = b; }

private:
// Clears the values of transient parameters.
void clearTransientParams_();
Expand Down Expand Up @@ -75,8 +79,11 @@ class Iot

// RTC.
RTC_DS3231 *rtc_ = nullptr;
bool rtc_found_ = false;


// NTP poll timer.
// NTP poll timer;
int ntp_poll_interval_ = 24;
Timer ntp_poll_timer_;

// Whether to reboot the ESP (after config updates if MQTT is enabled).
Expand Down Expand Up @@ -145,6 +152,11 @@ class Iot
// MQTT password value.
char mqtt_password_value_[IOT_CONFIG_VALUE_LENGTH];

// Enables the ntp interval.
IotRangeValueParameter ntp_interval_param_;
// Value of the ntp interval setting option.
char ntp_interval_value_[IOT_CONFIG_VALUE_LENGTH];

// Config form groups.
iotwebconf::ParameterGroup time_group_;
iotwebconf::ParameterGroup display_group_;
Expand Down
3 changes: 2 additions & 1 deletion software/wordclock/src/src/clockfaces.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ auto position = ClockFace::LightSensorPosition::Bottom;
EnglishClockFace clockFaceEN(position);
FrenchClockFace clockFaceFR(position);
DutchClockFace clockFaceNL(position);
ItalianClockFace clockFaceIT(position);
ItalianClockFace clockFaceIT(position);
RingClockFace clockFaceRING(position);
5 changes: 5 additions & 0 deletions software/wordclock/src/src/nodo.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,9 @@
#define LDR_PIN 1
#define NEOPIXEL_PIN 0
#endif
#else
#ifdef RING
#define NEOPIXEL_PIN 1
#define LDR_PIN 2
#endif // RING
#endif
Loading