2023-10-28 15:53:13 +00:00
|
|
|
#include "esp_system.h"
|
|
|
|
#include "sntp.h"
|
|
|
|
#include "time.h"
|
|
|
|
#include <WiFi.h>
|
|
|
|
#include <WiFiUdp.h>
|
|
|
|
#include <Preferences.h>
|
|
|
|
#include "scheduler.h"
|
|
|
|
#include "scheduler_prefs.h"
|
|
|
|
|
|
|
|
|
|
|
|
// Init const and global objects
|
|
|
|
|
2023-11-02 20:12:18 +00:00
|
|
|
// Preferences
|
2023-10-28 15:53:13 +00:00
|
|
|
Preferences preferences;
|
|
|
|
#define RW_MODE false
|
|
|
|
#define RO_MODE true
|
|
|
|
|
2023-11-02 20:12:18 +00:00
|
|
|
// Wifi
|
2023-10-28 15:53:13 +00:00
|
|
|
const char* ssid = "Wokwi-GUEST";
|
|
|
|
const char* password = "";
|
2023-10-28 17:06:10 +00:00
|
|
|
const uint8_t wifi_loop_max = 10;
|
2023-10-28 15:53:13 +00:00
|
|
|
|
|
|
|
// 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;
|
|
|
|
|
2023-11-02 20:12:18 +00:00
|
|
|
// Timer
|
2023-10-28 15:53:13 +00:00
|
|
|
hw_timer_t*timer_main = NULL;
|
|
|
|
uint32_t timer_interval = 3000000;
|
|
|
|
uint16_t timer_interval_in_secs = timer_interval / 1000000;
|
|
|
|
;
|
|
|
|
|
2023-11-02 20:12:18 +00:00
|
|
|
// Scheduler
|
|
|
|
bool scheduler_flag = true;
|
2023-10-28 15:53:13 +00:00
|
|
|
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
|
|
|
|
};
|
|
|
|
|
2023-11-02 20:12:18 +00:00
|
|
|
// Pins and Hardware
|
2023-10-28 15:53:13 +00:00
|
|
|
unsigned int PIN_1 = 2;
|
|
|
|
unsigned int PIN_2 = 4;
|
|
|
|
unsigned int pin_list[2] = {
|
|
|
|
PIN_1,
|
|
|
|
PIN_2
|
|
|
|
};
|
|
|
|
|
2023-11-02 20:12:18 +00:00
|
|
|
// ----------------------------------------------------------------------
|
2023-10-28 15:53:13 +00:00
|
|
|
// Local functions
|
2023-11-02 20:12:18 +00:00
|
|
|
// ----------------------------------------------------------------------
|
2023-10-28 15:53:13 +00:00
|
|
|
|
|
|
|
// Time helper
|
|
|
|
uint32_t get_timestamp( uint8_t hour, uint8_t minute, uint8_t second){
|
|
|
|
return ( 3600 * hour + 60 * minute + second) % 86400;
|
|
|
|
}
|
|
|
|
|
2023-11-02 20:12:18 +00:00
|
|
|
// Naive Timestamp based event igniter
|
|
|
|
bool scheduler_event_fire( uint32_t current_ts, uint32_t event_ts ){
|
2023-10-28 15:53:13 +00:00
|
|
|
|
2023-11-02 20:12:18 +00:00
|
|
|
// basic case = ts:11h41m11s versus ev:11h44m0s
|
|
|
|
// ts < ev means wait
|
2023-10-28 15:53:13 +00:00
|
|
|
if ( current_ts < event_ts ){
|
2023-11-02 20:12:18 +00:00
|
|
|
return false;
|
2023-10-28 15:53:13 +00:00
|
|
|
}
|
2023-11-02 20:12:18 +00:00
|
|
|
// if time looped at midnight = ts:23h57m21s versus ev:0h1m0s
|
|
|
|
// in case of high against very low values ts > ev means wait
|
2023-10-28 15:53:13 +00:00
|
|
|
if( (current_ts - event_ts) > 600 && event_ts < 300 ) {
|
2023-11-02 20:12:18 +00:00
|
|
|
return false;
|
2023-10-28 15:53:13 +00:00
|
|
|
}
|
2023-11-02 20:12:18 +00:00
|
|
|
// ts:11h44m11s versus ev:11h44m0s
|
|
|
|
// ts > ev means fire event
|
|
|
|
return true;
|
2023-10-28 15:53:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 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(){
|
2023-11-02 20:12:18 +00:00
|
|
|
scheduler_flag = true;
|
2023-10-28 17:06:10 +00:00
|
|
|
}
|
2023-11-02 20:12:18 +00:00
|
|
|
|
|
|
|
// updater local time variables for NTP and no FTP cases
|
|
|
|
void scheduler_time_update(){
|
2023-10-28 15:53:13 +00:00
|
|
|
// 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;
|
2023-10-28 17:06:10 +00:00
|
|
|
|
2023-10-28 15:53:13 +00:00
|
|
|
// If no NTP clock
|
|
|
|
}else{
|
|
|
|
|
2023-10-28 17:06:10 +00:00
|
|
|
// Serial.println("onTimer::NO NTP");
|
2023-10-28 15:53:13 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-11-02 20:12:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Scheduler's main loop
|
|
|
|
void scheduler_action(){
|
|
|
|
|
|
|
|
// Serial.println("onTimer::run");
|
|
|
|
scheduler_time_update();
|
2023-10-28 15:53:13 +00:00
|
|
|
|
|
|
|
// If not expected to run exit
|
|
|
|
current_ts = get_timestamp( current_hour, current_minute, current_second );
|
2023-11-02 20:12:18 +00:00
|
|
|
// Serial.printf("onTimer::check event %d %d\n", current_ts, next_event_ts);
|
|
|
|
if( ! scheduler_event_fire( current_ts, next_event_ts ) ){
|
2023-10-28 15:53:13 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2023-11-02 20:12:18 +00:00
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
// Arduino functions
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
|
2023-10-28 15:53:13 +00:00
|
|
|
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);
|
2023-11-02 20:12:18 +00:00
|
|
|
bool pref_init = preferences.isKey("init");
|
2023-10-28 15:53:13 +00:00
|
|
|
if (pref_init == false) {
|
2023-11-02 20:12:18 +00:00
|
|
|
preferences.putBool ("init", true);
|
2023-10-28 15:53:13 +00:00
|
|
|
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
|
|
|
|
}
|
2023-11-02 20:12:18 +00:00
|
|
|
// huh this causes a crash
|
2023-10-28 15:53:13 +00:00
|
|
|
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);
|
2023-10-28 17:06:10 +00:00
|
|
|
uint8_t wifi_loop_count = 0;
|
2023-11-02 20:12:18 +00:00
|
|
|
while (WiFi.status() != WL_CONNECTED && wifi_loop_count < wifi_loop_max ) {
|
2023-10-28 15:53:13 +00:00
|
|
|
delay(500);
|
2023-10-28 17:06:10 +00:00
|
|
|
wifi_loop_count++;
|
2023-10-28 15:53:13 +00:00
|
|
|
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");
|
2023-10-28 17:06:10 +00:00
|
|
|
next_event_ts = get_timestamp( HOUR_DEFAULT, MINUTE_DEFAULT, 10 );
|
2023-10-28 15:53:13 +00:00
|
|
|
|
|
|
|
|
|
|
|
// HTTP
|
|
|
|
Serial.println("Setup::HTTP");
|
|
|
|
// Todo
|
|
|
|
}
|
|
|
|
void loop(){
|
|
|
|
|
2023-11-02 20:12:18 +00:00
|
|
|
if( scheduler_flag == true ){
|
|
|
|
scheduler_flag = false;
|
|
|
|
scheduler_action();
|
2023-10-28 17:06:10 +00:00
|
|
|
}
|
2023-10-28 15:53:13 +00:00
|
|
|
|
|
|
|
delay(1000);
|
|
|
|
|
2023-10-28 17:06:10 +00:00
|
|
|
}
|