Adding sunrise_sunset to NTP (#1001)

This commit is contained in:
rickbronson
2023-12-19 12:03:31 -08:00
committed by GitHub
parent b0bfc23098
commit fe686b4799
6 changed files with 269 additions and 28 deletions

View File

@ -0,0 +1,19 @@
NOTE: Set Time offset, latitude, longitude accordingly
// autoexec for mini smart switch
PowerSave 1
addEventHandler OnHold 8 SafeMode
startDriver ntp
ntp_timeZoneOfs -8
ntp_setLatlong 44.002130 -123.091473
setPinRole 6 WifiLed_n
setPinRole 8 Btn_Tgl_All
setPinRole 14 TglChanOnTgl
setPinChannel 14 0
setPinRole 15 Rel
setPinChannel 15 0
removeClockEvent 12
removeClockEvent 13
waitFor NTPState 1
addClockEvent sunrise 0x7f 12 POWER OFF
addClockEvent sunset 0x7f 13 POWER ON

View File

@ -98,9 +98,9 @@
{
"title": "[Custom countdown/timer system with HTTP GUI for TuyaMCU relay](https://www.elektroda.com/rtvforum/topic4009196.html)",
"file": "autoexecs/custom_timer_for_relay.bat"
},
}
{
"title": "[Choose command/color table example (with index wrap)](https://www.elektroda.com/rtvforum/viewtopic.php?p=20863886#20863886)",
"file": "autoexecs/choose_command.bat"
"title": "[Setup for EZB-WBZS1H16N-A V1.0 Tuya mini smart switch showing sunrise/sunset events](https://www.elektroda.com/rtvforum/topic3967141.html)",
"file": "autoexecs/sunrise_sunset.bat"
}
]

View File

@ -14,6 +14,9 @@
#include "drv_ntp.h"
extern void NTP_Init_Events(void);
extern void NTP_RunEvents(unsigned int newTime, bool bTimeValid);
#define LOG_FEATURE LOG_FEATURE_NTP
typedef struct
@ -59,7 +62,8 @@ static int adrLen;
static int g_ntp_delay = 0;
static bool g_synced;
// time offset (time zone?) in seconds
static int g_timeOffsetSeconds;
#define CFG_DEFAULT_TIMEOFFSETSECONDS (-8 * 60 * 60)
static int g_timeOffsetSeconds = CFG_DEFAULT_TIMEOFFSETSECONDS;
// current time
unsigned int g_ntpTime;
@ -95,9 +99,43 @@ commandResult_t NTP_SetTimeZoneOfs(const void *context, const char *cmd, const c
}
g_ntpTime -= oldOfs;
g_ntpTime += g_timeOffsetSeconds;
addLogAdv(LOG_INFO, LOG_FEATURE_NTP,"NTP offset set");
addLogAdv(LOG_INFO, LOG_FEATURE_NTP,"NTP offset set");
return CMD_RES_OK;
}
#if ENABLE_NTP_SUNRISE_SUNSET
/* sunrise/sunset defaults */
#define CFG_DEFAULT_LATITUDE 43.994131
#define CFG_DEFAULT_LONGITUDE -123.095854
struct SUN_DATA sun_data =
{
.latitude = (int) (CFG_DEFAULT_LATITUDE * 1000000),
.longitude = (int) (CFG_DEFAULT_LONGITUDE * 1000000),
};
//Set Latitude and Longitude for sunrise/sunset calc
commandResult_t NTP_SetLatlong(const void *context, const char *cmd, const char *args, int cmdFlags) {
const char *newValue;
Tokenizer_TokenizeString(args,0);
// following check must be done after 'Tokenizer_TokenizeString',
// so we know arguments count in Tokenizer. 'cmd' argument is
// only for warning display
if (Tokenizer_CheckArgsCountAndPrintWarning(cmd, 2)) {
return CMD_RES_NOT_ENOUGH_ARGUMENTS;
}
newValue = Tokenizer_GetArg(0);
sun_data.latitude = (int) (atof(newValue) * 1000000);
addLogAdv(LOG_INFO, LOG_FEATURE_NTP, "NTP latitude set to %s", newValue);
newValue = Tokenizer_GetArg(1);
sun_data.longitude = (int) (atof(newValue) * 1000000);
addLogAdv(LOG_INFO, LOG_FEATURE_NTP, "NTP longitude set to %s", newValue);
return CMD_RES_OK;
}
#endif
//Set custom NTP server
commandResult_t NTP_SetServer(const void *context, const char *cmd, const char *args, int cmdFlags) {
@ -229,6 +267,13 @@ void NTP_Init() {
//cmddetail:"fn":"NTP_SetServer","file":"driver/drv_ntp.c","requires":"",
//cmddetail:"examples":""}
CMD_RegisterCommand("ntp_setServer", NTP_SetServer, NULL);
#if ENABLE_NTP_SUNRISE_SUNSET
//cmddetail:{"name":"ntp_setLatlong","args":"[Latlong]",
//cmddetail:"descr":"Sets the NTP latitude and longitude",
//cmddetail:"fn":"NTP_SetLatlong","file":"driver/drv_ntp.c","requires":"",
//cmddetail:"examples":"NTP_SetLatlong -34.911498 138.809488"}
CMD_RegisterCommand("ntp_setLatLong",NTP_SetLatlong, NULL);
#endif
//cmddetail:{"name":"ntp_info","args":"",
//cmddetail:"descr":"Display NTP related settings",
//cmddetail:"fn":"NTP_Info","file":"driver/drv_ntp.c","requires":"",
@ -357,7 +402,7 @@ void NTP_CheckForReceive() {
#endif
if(recv_len < 0){
addLogAdv(LOG_INFO, LOG_FEATURE_NTP,"NTP_CheckForReceive: Error while receiving server's msg");
addLogAdv(LOG_INFO, LOG_FEATURE_NTP,"NTP_CheckForReceive: Error while receiving server's msg");
return;
}
highWord = MAKE_WORD(ptr[40], ptr[41]);

View File

@ -24,11 +24,12 @@ void NTP_SetSimulatedTime(unsigned int timeNow);
int NTP_PrintEventList();
int NTP_RemoveClockEvent(int id);
int NTP_ClearEvents();
void NTP_Init_Events();
int NTP_ClearEvents();
void NTP_RunEvents(unsigned int newTime, bool bTimeValid);
extern unsigned int g_ntpTime;
extern struct SUN_DATA { /* sunrise / sunset globals */
int latitude; /* latitude * 1000000 */
int longitude; /* longitude * 1000000 */
} sun_data;
#endif /* __DRV_NTP_H__ */

View File

@ -1,5 +1,6 @@
//
#include <time.h>
#include <math.h>
#include "../new_common.h"
#include "../new_cfg.h"
@ -11,6 +12,7 @@
#include "drv_ntp.h"
#define M_PI 3.14159265358979323846264338327950288
#define LOG_FEATURE LOG_FEATURE_NTP
unsigned int ntp_eventsTime = 0;
@ -20,6 +22,12 @@ typedef struct ntpEvent_s {
byte minute;
byte second;
byte weekDayFlags;
#if ENABLE_NTP_SUNRISE_SUNSET
byte lastDay; /* used so we don't repeat sunrise sunset events the same day */
byte sunflags; /* flags for sunrise/sunset as follows: */
#define SUNRISE_FLAG (1 << 0)
#define SUNSET_FLAG (1 << 1)
#endif
int id;
char *command;
struct ntpEvent_s *next;
@ -27,6 +35,130 @@ typedef struct ntpEvent_s {
ntpEvent_t *ntp_events = 0;
#if ENABLE_NTP_SUNRISE_SUNSET
/* Sunrise/sunset algorithm, somewhat based on https://edwilliams.org/sunrise_sunset_algorithm.htm and tasmota code */
const float pi2 = (M_PI * 2);
const float pi = M_PI;
const float RAD = (M_PI / 180.0);
/* Force a float value between two ranges, and add or substract the range until we fit */
static inline float ModulusRangef(float val, float min, float max)
{
if (max <= min) {
return min; /* inconsistent, do what we can */
}
float range = max - min;
val = val - min; /* now range of val should be 0..range */
val = fmodf(val, range); /* actual range is now -range..range */
if (val < 0.0f) {
val += range;
} /* actual range is now 0..range */
return val + min; /* returns range min..max */
}
/* Force value in the 0..pi2 range */
static inline float InPi(float val)
{
return ModulusRangef(val, 0.0f, pi2);
}
#define CENTURY_DAYS 36524.0f /* days in century (1900's = 36525, 2000's = 36524 */
/* Time formula */
/* Tdays is the number of days since Jan 1 2000, and replaces T as the Tropical Century. T = Tdays / CENTURY_DAYS */
static inline float TimeFormula(float *declination, uint32_t Tdays)
{
float RA_Mean = 18.71506921f + (2400.0513369f / CENTURY_DAYS) * Tdays; /* we keep only first order value as T is between 0.20 and 0.30 */
float meanAnomaly = InPi( (pi2 * 0.993133f) + (pi2 * 99.997361f / CENTURY_DAYS) * Tdays);
float trueLongitude = InPi( (pi2 * 0.7859453f) + meanAnomaly + (6893.0f * sinf(meanAnomaly)
+ 72.0f * sinf(meanAnomaly + meanAnomaly) + (6191.2f / CENTURY_DAYS) * Tdays) * (pi2 / 1296.0e3f));
float cos_eps = 0.91750f; /* precompute cos(eps) */
float sin_eps = 0.39773f; /* precompute sin(eps) */
float rightAscension = atanf(tanf(trueLongitude) * cos_eps);
if (rightAscension < 0.0f)
rightAscension += pi;
if (trueLongitude > pi)
rightAscension += pi;
rightAscension = rightAscension * (24.0f / pi2);
*declination = asinf(sin_eps * sinf(trueLongitude));
RA_Mean = ModulusRangef(RA_Mean, 0.0f, 24.0f);
float dRA = ModulusRangef(RA_Mean - rightAscension, -12.0f, 12.0f);
dRA = dRA * 1.0027379f;
return dRA;
}
/* Compute the Julian day number from the Calendar date, using only unsigned ints for code compactness */
static inline uint32_t JulianDay(void)
{
/* https://en.wikipedia.org/wiki/Julian_day */
uint32_t Year = NTP_GetYear(); /* Year ex:2020 */
uint32_t Month = NTP_GetMonth(); /* 1..12 */
uint32_t Day = NTP_GetMDay(); /* 1..31 */
uint32_t Julian; /* Julian day number */
if (Month <= 2) {
Month += 12;
Year -= 1;
}
/* NOTE: 1461 = 365.25 * 4, Julian = (1461 * Year + 6884472) / 4 + (153 * Month - 457) / 5 + Day -1 -13; */
Julian = (1461 * Year + 6884416) / 4 + (153 * Month - 457) / 5 + Day; /* -1 -13 included in 6884472 - 14*4 = 6884416 */
return Julian;
}
/* Sunrise and Sunset DawnType */
#define DAWN_NORMAL -0.8333
#define DAWN_CIVIL -6.0
#define DAWN_NAUTIC -12.0
#define DAWN_ASTRONOMIC -18.0
static void dusk2Dawn(struct SUN_DATA *Settings, byte sunflags, uint8_t *hour, uint8_t *minute, int day_offset)
{
float eventTime, declination, localTime;
const uint32_t JD2000 = 2451545;
uint32_t Tdays = JulianDay() - JD2000 + day_offset; /* number of days since Jan 1 2000, plus offset */
/* ex 2458977 (2020 May 7) - 2451545 -> 7432 -> 0,2034 */
const float sin_h = sinf(DAWN_NORMAL * RAD); /* let GCC pre-compute the sin() at compile time */
float geoLatitude = Settings->latitude / (1000000.0f / RAD);
float geoLongitude = ((float) Settings->longitude) / 1000000;
float timeZone = ((float) NTP_GetTimesZoneOfsSeconds()) / 3600; /* convert to hours */
float timeEquation = TimeFormula(&declination, Tdays);
float timeDiff = acosf((sin_h - sinf(geoLatitude) * sinf(declination)) / (cosf(geoLatitude) * cosf(declination))) * (12.0f / pi);
if (sunflags & SUNRISE_FLAG) {
localTime = 12.0f - timeDiff - timeEquation;
}
else {
localTime = 12.0f + timeDiff - timeEquation;
}
float worldTime = localTime - geoLongitude / 15.0f;
eventTime = worldTime + timeZone + (1 / 120.0f); /* In Hours, with rounding to nearest minute (1/60 * .5) */
eventTime = ModulusRangef(eventTime, 0.0f, 24.0f); /* force 0 <= x < 24.0 */
*hour = (uint8_t) eventTime;
*minute = (uint8_t)(60.0f * fmodf(eventTime, 1.0f));
}
/* calc number of days until next sun event */
static int calc_day_offset(int tm_wday, int weekDayFlags)
{
int day_offset, mask;
if (tm_wday > 6) /* we can be called with 0-7, 0 and 7 represent Sunday (0) */
tm_wday = 0;
mask = 1 << tm_wday;
for (day_offset = 0; day_offset < 7; day_offset++) {
if (mask & weekDayFlags)
break;
mask <<= 1;
if (mask & 0x80) /* mask is only 7 bits */
mask = 1; /* start over on Sunday */
}
return (day_offset);
}
#endif
void NTP_RunEventsForSecond(unsigned int runTime) {
ntpEvent_t *e;
@ -47,6 +179,20 @@ void NTP_RunEventsForSecond(unsigned int runTime) {
if (e->hour == ltm->tm_hour && e->second == ltm->tm_sec && e->minute == ltm->tm_min) {
// weekday check
if (BIT_CHECK(e->weekDayFlags, ltm->tm_wday)) {
#if ENABLE_NTP_SUNRISE_SUNSET
if (e->sunflags & (SUNRISE_FLAG || SUNSET_FLAG)) {
if (e->lastDay != ltm->tm_wday) {
e->lastDay = ltm->tm_wday; /* stop any further sun events today */
dusk2Dawn(&sun_data, e->sunflags, &e->hour, &e->minute,
calc_day_offset(ltm->tm_wday + 1, e->weekDayFlags)); /* setup for tomorrow */
CMD_ExecuteCommand(e->command, 0);
}
else {
e->lastDay = -1; /* mark with anything but a valid day of week */
}
}
else
#endif
CMD_ExecuteCommand(e->command, 0);
}
}
@ -54,6 +200,7 @@ void NTP_RunEventsForSecond(unsigned int runTime) {
e = e->next;
}
}
void NTP_RunEvents(unsigned int newTime, bool bTimeValid) {
unsigned int delta;
unsigned int i;
@ -85,7 +232,12 @@ void NTP_RunEvents(unsigned int newTime, bool bTimeValid) {
}
ntp_eventsTime = newTime;
}
#if ENABLE_NTP_SUNRISE_SUNSET
void NTP_AddClockEvent(int hour, int minute, int second, int weekDayFlags, int id, int sunflags, const char* command) {
#else
void NTP_AddClockEvent(int hour, int minute, int second, int weekDayFlags, int id, const char* command) {
#endif
ntpEvent_t* newEvent = (ntpEvent_t*)malloc(sizeof(ntpEvent_t));
if (newEvent == NULL) {
// handle error
@ -96,6 +248,10 @@ void NTP_AddClockEvent(int hour, int minute, int second, int weekDayFlags, int i
newEvent->minute = minute;
newEvent->second = second;
newEvent->weekDayFlags = weekDayFlags;
#if ENABLE_NTP_SUNRISE_SUNSET
newEvent->lastDay = -1; /* mark with anything but a valid day of week */
newEvent->sunflags = sunflags;
#endif
newEvent->id = id;
newEvent->command = strdup(command);
newEvent->next = ntp_events;
@ -140,11 +296,18 @@ int NTP_RemoveClockEvent(int id) {
// addClockEvent 8:00:00 0x01 234 backlog led_temperature 500; led_dimmer 100; led_enableAll 1;
// Example
// addClockEvent 15:06:00 0xff 123 POWER TOGGLE
// Example: do event every Wednesday at sunrise
// addClockEvent sunrise 0x08 12 POWER OFF
commandResult_t CMD_NTP_AddClockEvent(const void *context, const char *cmd, const char *args, int cmdFlags) {
int hour, minute = 0, second = 0;
const char *s;
int flags;
int id;
#if ENABLE_NTP_SUNRISE_SUNSET
uint8_t hour_b, minute_b;
int sunflags = 0;
struct tm *ltm = localtime((time_t*) &ntp_eventsTime);
#endif
Tokenizer_TokenizeString(args, 0);
// following check must be done after 'Tokenizer_TokenizeString',
@ -154,22 +317,38 @@ commandResult_t CMD_NTP_AddClockEvent(const void *context, const char *cmd, cons
return CMD_RES_NOT_ENOUGH_ARGUMENTS;
}
s = Tokenizer_GetArg(0);
// FIX: Somehow sscanf fails if string starts with "08:20", etc,
// but it works if it starts like "8:20"
if (isdigit(s[0]) && isdigit(s[1])) {
if (*s == '0')
s++;
}
if (sscanf(s, "%i:%i:%i", &hour, &minute, &second) <= 1) {
return CMD_RES_BAD_ARGUMENT;
}
flags = Tokenizer_GetArgInteger(1);
if (sscanf(s, "%i:%i:%i", &hour, &minute, &second) <= 1) {
#if ENABLE_NTP_SUNRISE_SUNSET
if (strcasestr(s, "sunrise")) {
sunflags |= SUNRISE_FLAG;
}
else {
if (strcasestr(s, "sunset")) {
sunflags |= SUNSET_FLAG;
}
else {
return CMD_RES_BAD_ARGUMENT;
}
}
#else
return CMD_RES_BAD_ARGUMENT;
#endif
}
id = Tokenizer_GetArgInteger(2);
s = Tokenizer_GetArgFrom(3);
#if ENABLE_NTP_SUNRISE_SUNSET
if (sunflags) {
dusk2Dawn(&sun_data, sunflags, &hour_b, &minute_b, calc_day_offset(ltm->tm_wday, flags));
hour = hour_b;
minute = minute_b;
}
NTP_AddClockEvent(hour, minute, second, flags, id, sunflags, s);
#else
NTP_AddClockEvent(hour, minute, second, flags, id, s);
return CMD_RES_OK;
#endif
return CMD_RES_OK;
}
// addPeriodValue [ChannelIndex] [Start_DayOfWeek] [Start_HH:MM:SS] [End_DayOfWeek] [End_HH:MM:SS] [Value] [UniqueID] [Flags]
//commandResult_t CMD_NTP_AddPeriodValue(const void *context, const char *cmd, const char *args, int cmdFlags) {
@ -235,7 +414,7 @@ int NTP_PrintEventList() {
while (e) {
// Print the command
addLogAdv(LOG_INFO, LOG_FEATURE_CMD, "Ev %i - %i:%i:%i, days %i, cmd %s\n", (int)e->id, (int)e->hour, (int)e->minute, (int)e->second, (int)e->weekDayFlags, e->command);
addLogAdv(LOG_INFO, LOG_FEATURE_CMD, "Ev %i - %i:%i:%i, days 0x%02x, cmd %s\n", (int)e->id, (int)e->hour, (int)e->minute, (int)e->second, (int)e->weekDayFlags, e->command);
t++;
e = e->next;
@ -278,11 +457,11 @@ commandResult_t CMD_NTP_ClearEvents(const void* context, const char* cmd, const
}
void NTP_Init_Events() {
//cmddetail:{"name":"addClockEvent","args":"[Time] [WeekDayFlags] [UniqueIDForRemoval][Command]",
//cmddetail:"descr":"Schedule command to run on given time in given day of week. NTP must be running. Time is a time like HH:mm or HH:mm:ss, WeekDayFlag is a bitflag on which day to run, 0xff mean all days, 0x01 means sunday, 0x02 monday, 0x03 sunday and monday, etc, id is an unique id so event can be removede later",
//cmddetail:{"name":"addClockEvent","args":"[Time or sunrise or sunset] [WeekDayFlags] [UniqueIDForRemoval][Command]",
//cmddetail:"descr":"Schedule command to run on given time in given day of week. NTP must be running. Time is a time like HH:mm or HH:mm:ss, WeekDayFlag is a bitflag on which day to run, 0xff mean all days, 0x01 means sunday, 0x02 monday, 0x03 sunday and monday, etc, id is an unique id so event can be removed later. (NOTE: Use of sunrise/sunset requires compiling with ENABLE_NTP_SUNRISE_SUNSET set which adds about 11k of code",
//cmddetail:"fn":"CMD_NTP_AddClockEvent","file":"driver/drv_ntp_events.c","requires":"",
//cmddetail:"examples":""}
CMD_RegisterCommand("addClockEvent",CMD_NTP_AddClockEvent, NULL);
CMD_RegisterCommand("addClockEvent",CMD_NTP_AddClockEvent, NULL);
//cmddetail:{"name":"removeClockEvent","args":"[ID]",
//cmddetail:"descr":"Removes clock event wtih given ID",
//cmddetail:"fn":"CMD_NTP_RemoveClockEvent","file":"driver/drv_ntp_events.c","requires":"",

View File

@ -53,8 +53,6 @@
#define ENABLE_DRIVER_BATTERY 1
#define ENABLE_DRIVER_PT6523 1
#define ENABLE_DRIVER_TEXTSCROLLER 1
#define ENABLE_DRIVER_TMGN 1
#define ENABLE_DRIVER_BMP280 1
#elif PLATFORM_BL602
@ -72,6 +70,7 @@
// set to 0 to disable
#define ENABLE_LITTLEFS 1
#define ENABLE_NTP 1
// #define ENABLE_NTP_SUNRISE_SUNSET 1
#define ENABLE_DRIVER_LED 1
#define ENABLE_DRIVER_BL0937 1
#define ENABLE_DRIVER_BL0942 1
@ -82,7 +81,6 @@
#define ENABLE_DRIVER_TUYAMCU 1
//#define ENABLE_DRIVER_HT16K33 1
//#define ENABLE_DRIVER_MAX72XX 1
//#define ENABLE_DRIVER_TMGN 1
#define ENABLE_I2C 1
//#define ENABLE_TEST_COMMANDS 1
#define ENABLE_CALENDAR_EVENTS 1
@ -93,7 +91,6 @@
#define ENABLE_DRIVER_HUE 1
#define ENABLE_DRIVER_CHARGINGLIMIT 1
#define ENABLE_DRIVER_BATTERY 1
#define ENABLE_DRIVER_BMP280 1
#else