#include "esp_system.h" #include "sntp.h" #include "time.h" #include #include #include #include "scheduler.h" #include "scheduler_prefs.h" // #include "wifi_prefs.h" // -------------------------------------------- // Init const and global objects // -------------------------------------------- const char* html_app="ESP32 timed Switch

Scheduler

"; // PREFERENCES 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; int 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}; int 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}; int scheduler_3[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}; int scheduler_4[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}; int scheduler_5[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}; int scheduler_6[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}; int scheduler_7[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}; int scheduler_8[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}; int* scheduler_list[] = { scheduler_1, scheduler_2, scheduler_3, scheduler_4, scheduler_5, scheduler_6, scheduler_7, scheduler_8, }; // PINS unsigned int PIN_1 = 12; unsigned int PIN_2 = 14; unsigned int PIN_3 = 27; unsigned int PIN_4 = 26; unsigned int PIN_5 = 20; unsigned int PIN_6 = 33; unsigned int PIN_7 = 32; unsigned int PIN_8 = 35; unsigned int pin_list[8] = { PIN_1, PIN_2, PIN_3, PIN_4, PIN_5, PIN_6, PIN_7, PIN_8 }; // HTTP WiFiClient client; WiFiServer http_server(3000); String request; // -------------------------------------------- // 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 < 8; i++ ) { // Get a pointer to the current array int* scheduler = scheduler_list[i]; unsigned int pin = pin_list[i]; // Set the expected status uint8_t minutes_by_5 = (current_minute / 5); int 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; } // HTTP const char* getPreferenceName( int plug_id){ switch (plug_id) { case 0: return RELAY_1_SCHEDULE; break; case 1: return RELAY_2_SCHEDULE; break; case 2: return RELAY_3_SCHEDULE; break; case 3: return RELAY_4_SCHEDULE; break; case 4: return RELAY_5_SCHEDULE; break; case 5: return RELAY_6_SCHEDULE; break; case 6: return RELAY_7_SCHEDULE; break; case 7: return RELAY_8_SCHEDULE; break; } } void saveSchedule( int plug_id, uint16_t data[24]){ const char* preference_name = getPreferenceName( plug_id ); preferences.putBytes(preference_name, data, 48); Serial.print("saving size:"); Serial.print(sizeof(data)); Serial.print(" to "); Serial.println(preference_name); uint16_t buf[24]; preferences.getBytes(preference_name, buf, 48); for ( int iterator=0; iterator < 24; iterator++){ Serial.print(iterator); Serial.print(" : "); Serial.print(data[iterator]); Serial.print(" / "); Serial.println(buf[iterator]); } delay(2000); } void sendSchedule(WiFiClient &client, int plug_id) { uint16_t buf[24]; const char* preference_name = getPreferenceName( plug_id ); Serial.print("sending "); Serial.println(preference_name); preferences.getBytes(preference_name, buf, 48); for ( int iterator=0; iterator < 24; iterator++){ Serial.print(iterator); Serial.print(" : "); Serial.println(buf[iterator]); } Serial.println("---------------------------"); client.write((const uint8_t*)buf, 24 * sizeof(uint16_t)); } // -------------------------------------------- // ARDUINO Functions // -------------------------------------------- void setup(){ // HARDWARE Serial.begin(115200); delay(1000); Serial.println("Starting."); pinMode(PIN_1, OUTPUT); pinMode(PIN_2, OUTPUT); pinMode(PIN_3, OUTPUT); pinMode(PIN_4, OUTPUT); pinMode(PIN_5, OUTPUT); pinMode(PIN_6, OUTPUT); pinMode(PIN_7, OUTPUT); pinMode(PIN_8, OUTPUT); // PREFERENCES Serial.println("Setup::PREFERENCES"); preferences.begin(PREF_NAMESPACE, RW_MODE); // preferences.clear(); bool pref_init = preferences.isKey("test"); if (true || pref_init == false) { Serial.println("preferences not exist"); preferences.putBool("test", true); saveSchedule(1,scheduler_default_deactivate); saveSchedule(2,scheduler_default_deactivate); saveSchedule(3,scheduler_default_deactivate); saveSchedule(4,scheduler_default_deactivate); /* saveSchedule(5,scheduler_default_deactivate); saveSchedule(6,scheduler_default_deactivate); saveSchedule(7,scheduler_default_deactivate); saveSchedule(8,scheduler_default_deactivate); */ } 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.getBytes(RELAY_3_SCHEDULE, scheduler_2, preferences.getBytesLength(RELAY_3_SCHEDULE)); preferences.getBytes(RELAY_4_SCHEDULE, scheduler_2, preferences.getBytesLength(RELAY_4_SCHEDULE)); preferences.getBytes(RELAY_5_SCHEDULE, scheduler_2, preferences.getBytesLength(RELAY_5_SCHEDULE)); preferences.getBytes(RELAY_6_SCHEDULE, scheduler_2, preferences.getBytesLength(RELAY_6_SCHEDULE)); preferences.getBytes(RELAY_7_SCHEDULE, scheduler_2, preferences.getBytesLength(RELAY_7_SCHEDULE)); preferences.getBytes(RELAY_8_SCHEDULE, scheduler_2, preferences.getBytesLength(RELAY_8_SCHEDULE)); preferences.end(); Serial.println("Setup::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(1000); wifi_loop_count++; Serial.print("."); } // 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"); http_server.begin(); Serial.print("Connect to IP Address: "); Serial.print("http://"); Serial.println(WiFi.localIP()); // Todo } void loop(){ if( brun_scheduler == true ){ brun_scheduler = false; run_scheduler(); } client = http_server.available(); if (!client) { // Serial.println("Server not available... skip..."); return; } String request = client.readStringUntil('\r'); Serial.print("request: "); Serial.println(request); // GET request if (request.indexOf("GET /api/schedule/") >= 0) { int startIdIndex = 18; int endIdIndex = 19; String plug_idStr = request.substring(startIdIndex, endIdIndex); int plug_id = plug_idStr.toInt() - 1; // Adjust for 0 index Serial.print("plug_id " ); Serial.println(plug_id); if (plug_id >= 0 && plug_id < 8) { client.println("HTTP/1.1 200 OK"); client.println("Content-Type: text/html"); client.println("Connection: close"); client.println(); sendSchedule(client, plug_id); } else { client.println("HTTP/1.1 404 Not Found"); client.println("Connection: close"); client.println(); } // GET homepage } else if (request.indexOf("GET /") >= 0) { client.println("HTTP/1.1 200 OK"); client.println("Content-Type: text/html"); client.println("Connection: close"); client.println(); client.println(html_app); } else { // POST request int slashIndex = request.indexOf("POST /api/schedule/"); if (slashIndex != -1) { Serial.print("slashIndex: "); Serial.println(slashIndex); int nextSlashIndex = slashIndex + 19; // Extract the plug ID String plug_idStr = request.substring(nextSlashIndex, nextSlashIndex + 1); Serial.print("plug_idStr "); Serial.println(plug_idStr); int plug_id = plug_idStr.toInt() - 1; // Force a new line client.readStringUntil('\n'); // Read the next line which should have the POST body/content String header ; while ( header = client.readStringUntil('\n') ){ Serial.print("header: '"); Serial.print(header); Serial.print(" / "); Serial.print(header.length()); Serial.println("'"); if( header.length() < 2 ){ Serial.println("parsed headers"); break; } } Serial.println("Done reading. Going for binary."); char post_body[48]; client.readBytes(post_body, 48); Serial.print("Read post_body: "); Serial.println(post_body); // Update the schedule for the specified plug if (plug_id >= 0 && plug_id < 8) { uint16_t byte_value[24]; for (int i=0; i < 24; i++) { byte_value[i] = (char) post_body[i*2] * 256 + post_body[(i*2)+1]; Serial.print("Byte "); Serial.print(i); Serial.print(" : "); Serial.print(byte_value[i]); Serial.println(""); } saveSchedule(plug_id, byte_value ); Serial.println("Schedule updated for plug " + plug_idStr); } else { Serial.println("Invalid plug ID"); } client.println("HTTP/1.1 200 OK"); client.println("Content-Type: text/plain"); client.println("Connection: close"); client.println(); } } client.stop(); }