esp32-timed-switch/esp32-timed-switch.ino

352 lines
9.1 KiB
C++

#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
// --------------------------------------------
// PREFRENCES
Preferences preferences;
#define RW_MODE false
#define RO_MODE true
// WIFI
const char* ssid = "/tmp/lab";
const char* password = "bitte2cucung";
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_list[] = {
scheduler_1,
scheduler_2
};
// 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[2] = {
PIN_1,
PIN_2
};
// HTTP
WiFiClient http_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 < 2; 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
void deserializeSchedule(String content, int schedule[24]) {
client.readBytes((char*)schedule, 24 * sizeof(int));
}
void sendSchedule(WiFiClient &client, int schedule[24]) {
client.write((const uint8_t*)schedule, 24 * sizeof(int));
}
// --------------------------------------------
// ARDUINO Functions
// --------------------------------------------
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);
preferences.clear();
bool pref_init = preferences.isKey("test");
if (pref_init == false) {
Serial.println("preferences not exist");
preferences.putBool("test", true);
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
}
bool test = preferences.getBool("test");
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();
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 html() {
http_client.println("HTTP/1.1 200 OK");
http_client.println("Content-Type: text/html");
http_client.println("Connection: close");
http_client.println();
http_client.println("<!DOCTYPE HTML>");
http_client.println("<html>");
http_client.println("</body>");
http_client.println("</html>");
}
void loop(){
if( brun_scheduler == true ){
brun_scheduler = false;
run_scheduler();
}
http_client = http_server.available();
if (!http_client) {
return;
}
String request = client.readStringUntil('\r');
client.flush();
// GET request
if (request.indexOf("GET /api/schedule/") >= 0) {
int apiIndex = request.indexOf("/api/schedule/");
int startIdIndex = apiIndex + strlen("/api/schedule/");
int endIdIndex = request.indexOf(" ", startIdIndex);
String plugIdStr = request.substring(startIdIndex, endIdIndex);
int plugId = plugIdStr.toInt() - 1; // Adjust for 0 index
if (plugId >= 0 && plugId < 8) {
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/plain");
client.println("Connection: close");
client.println();
sendSchedule(client, scheduler_list[plugId]);
} else {
client.println("HTTP/1.1 404 Not Found");
client.println("Connection: close");
client.println();
}
}
// POST request
int slashIndex = request.indexOf("POST /api/schedule/");
if (slashIndex != -1) {
int nextSlashIndex = request.indexOf("/", slashIndex + 14);
if (nextSlashIndex == -1) {
nextSlashIndex = request.indexOf(" ", slashIndex + 14);
}
// Extract the plug ID
String plugIdStr = request.substring(slashIndex + 14, nextSlashIndex);
int plugId = plugIdStr.toInt();
// Read the next line which should have the POST body/content
String postBody = client.readStringUntil('\n');
// Update the schedule for the specified plug
if (plugId >= 0 && plugId < 8) {
deserializeSchedule(postBody, scheduler_list[plugId]);
Serial.println("Schedule updated for plug " + plugIdStr);
} else {
Serial.println("Invalid plug ID");
}
}
http_client.stop();
}