#include "esp_system.h" #include "sntp.h" #include "time.h" #include #include #include #include "scheduler.h" #include "scheduler_prefs.h" // Init const and global objects // PREFRENCES Preferences preferences; #define RW_MODE false #define RO_MODE true // WIFI const char* ssid = "Wokwi-GUEST"; const char* password = ""; const uint8_t wifi_loop_max = 10; // NTP bool sntp_initialized = false; const char* ntpServer1 = "pool.ntp.org"; const char* ntpServer2 = "time.nist.gov"; const long gmtOffset_sec = 2 * 3600; // GMT + 2 const int daylightOffset_sec = 0; // TIMER hw_timer_t*timer_main = NULL; uint32_t timer_interval = 3000000; uint16_t timer_interval_in_secs = timer_interval / 1000000; ; // SCHEDULER bool brun_scheduler = true; uint8_t current_hour = HOUR_DEFAULT; uint8_t current_minute = MINUTE_DEFAULT; uint8_t current_second = 0; uint32_t current_ts = 0; uint32_t next_event_ts = 0; uint16_t scheduler_1[24] = {A60,A60,A60,A60,A60,A60,A60,A60,A60,A60,A60,A60,A60,A60,A60,A60,A60,A60,A60,A60,A60,A60,A60,A60}; uint16_t scheduler_2[24] = {A00,A00,A00,A00,A00,A00,A00,A00,A00,A00,A00,A00,A00,A00,A00,A00,A00,A00,A00,A00,A00,A00,A00,A00}; uint16_t* scheduler_list[] = { scheduler_1, scheduler_2 }; // PINS unsigned int PIN_1 = 2; unsigned int PIN_2 = 4; unsigned int pin_list[2] = { PIN_1, PIN_2 }; // Local functions // Time helper uint32_t get_timestamp( uint8_t hour, uint8_t minute, uint8_t second){ return ( 3600 * hour + 60 * minute + second) % 86400; } bool event_keep_waiting( uint32_t current_ts, uint32_t event_ts ){ // basic case // ts: 11h 41m 11s // ev: 11h 44m 0s // ts < ev => TRUE if ( current_ts < event_ts ){ return true; } // if time looped at midnight // ts: 23h 57m 21s // ev: 0h 1m 0s // ts > ev => TRUE (for high to very low values ) if( (current_ts - event_ts) > 600 && event_ts < 300 ) { return true; } // ts: 11h 44m 11s // ev: 11h 44m 0s // ts > ev => FALSE return false; } // NTP Callback void timeavailable(struct timeval *t) { Serial.println("NTP::Got time adjustment from NTP!"); sntp_initialized = true; return; } // TIMER Callback void IRAM_ATTR onTimer(){ brun_scheduler = true; } void run_scheduler(){ // Serial.println("onTimer::run"); // Get the current time via NTP, or downgrade if ( sntp_initialized ){ struct tm timeinfo; time_t now; time(&now); localtime_r(&now, &timeinfo); current_hour = timeinfo.tm_hour; current_minute = timeinfo.tm_min; current_second = timeinfo.tm_sec; // If no NTP clock }else{ // Serial.println("onTimer::NO NTP"); current_second += timer_interval_in_secs; if(current_second >= 60) { current_second = 0; current_minute += 1; if(current_minute >= 60) { current_minute = 0; current_hour += 1; if(current_hour >= 24) { current_hour = 0; } } } } // If not expected to run exit current_ts = get_timestamp( current_hour, current_minute, current_second ); Serial.printf("onTimer::check event %d %d\n", current_ts, next_event_ts); if( event_keep_waiting( current_ts, next_event_ts ) ){ return; } // Set the next target next_event_ts = get_timestamp( current_hour, current_minute + EVENT_INC_MINUTE, current_second ); Serial.println("Reconfigure the Relays!"); // Run the relays reconfiguration for ( int i = 0; i < 2; i++ ) { // Get a pointer to the current array uint16_t* scheduler = scheduler_list[i]; unsigned int pin = pin_list[i]; // Set the expected status uint8_t minutes_by_5 = (current_minute / 5); uint16_t hourly_12_values = scheduler[current_hour]; // Serial.printf("Check hour(%d) >> minutes(%d x 5)\n", hourly_12_values, minutes_by_5); char buffer[40]; if (( hourly_12_values >> minutes_by_5 ) & 1) { digitalWrite(pin, HIGH); sprintf (buffer, "Pin %d is up ", pin); } else { digitalWrite(pin, LOW); sprintf (buffer, "Pin %d is down ", pin); } Serial.println(buffer); // Get the expected status } Serial.println("Reconfiguration over"); return; } void setup(){ // HARDWARE Serial.begin(115200); delay(1000); Serial.println("Starting."); pinMode(PIN_1, OUTPUT); pinMode(PIN_2, OUTPUT); // PREFERENCES Serial.println("Setup::PREFERENCES"); preferences.begin(PREF_NAMESPACE, RW_MODE); bool pref_init = preferences.isKey("nvsInit"); if (pref_init == false) { preferences.putBytes(RELAY_1_SCHEDULE, scheduler_1_default, sizeof(scheduler_1_default)); preferences.putBytes(RELAY_2_SCHEDULE, scheduler_2_default, sizeof(scheduler_2_default)); /// ... more to come } preferences.getBytes(RELAY_1_SCHEDULE, scheduler_1, preferences.getBytesLength(RELAY_1_SCHEDULE)); preferences.getBytes(RELAY_2_SCHEDULE, scheduler_2, preferences.getBytesLength(RELAY_2_SCHEDULE)); preferences.end(); // WIFI Serial.println("Setup::WIFI"); Serial.printf("Connecting to %s ", ssid); WiFi.begin(ssid, password); uint8_t wifi_loop_count = 0; while (WiFi.status() != WL_CONNECTED or wifi_loop_count < wifi_loop_max ) { delay(500); wifi_loop_count++; Serial.print("."); } Serial.print("Connect to IP Address: "); Serial.print("http://"); Serial.println(WiFi.localIP()); // TIMER Serial.println("Setup::Timer"); timer_main = timerBegin(0, 80, true); timerAttachInterrupt(timer_main, &onTimer, true); timerAlarmWrite(timer_main, timer_interval, true); timerAlarmEnable(timer_main); // NTP Serial.println("Setup::NTP"); sntp_set_time_sync_notification_cb(timeavailable); configTime(gmtOffset_sec, daylightOffset_sec, ntpServer1, ntpServer2); delay(5000); // SCHEDULER Serial.println("Setup::SCHEDULER"); next_event_ts = get_timestamp( HOUR_DEFAULT, MINUTE_DEFAULT, 10 ); // HTTP Serial.println("Setup::HTTP"); // Todo } void loop(){ Serial.println("Loop::Enter"); if( brun_scheduler == true ){ brun_scheduler = false; run_scheduler(); } delay(1000); }