diff --git a/.gitignore b/.gitignore index 722d5e7..e621bec 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ .vscode +.idea +venv diff --git a/esp32-timed-switch.ino b/esp32-timed-switch.ino index 0571beb..890e873 100644 --- a/esp32-timed-switch.ino +++ b/esp32-timed-switch.ino @@ -8,7 +8,10 @@ #include "scheduler_prefs.h" +// -------------------------------------------- // Init const and global objects +// -------------------------------------------- + // PREFRENCES Preferences preferences; @@ -63,11 +66,15 @@ unsigned int pin_list[2] = { // HTTP WiFiClient http_client; -WiFiServer http_server(80); +WiFiServer http_server(3000); String request; + +// -------------------------------------------- // Local functions +// -------------------------------------------- + // Time helper uint32_t get_timestamp( uint8_t hour, uint8_t minute, uint8_t second){ @@ -179,6 +186,22 @@ void run_scheduler(){ 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 @@ -191,7 +214,7 @@ void setup(){ 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"); @@ -212,11 +235,11 @@ void setup(){ WiFi.begin(ssid, password); uint8_t wifi_loop_count = 0; while (WiFi.status() != WL_CONNECTED or wifi_loop_count < wifi_loop_max ) { - delay(1000); + delay(1000); wifi_loop_count++; Serial.print("."); } - + // TIMER Serial.println("Setup::Timer"); timer_main = timerBegin(0, 80, true); @@ -237,7 +260,7 @@ void setup(){ // HTTP Serial.println("Setup::HTTP"); - http_server.begin(); + http_server.begin(); Serial.print("Connect to IP Address: "); Serial.print("http://"); Serial.println(WiFi.localIP()); @@ -256,7 +279,7 @@ void html() { http_client.println(""); http_client.println(""); http_client.println(""); - + } void loop(){ @@ -272,27 +295,56 @@ void loop(){ return; } - while (http_client.connected()) { - if (http_client.available()) { - char c = http_client.read(); + String request = client.readStringUntil('\r'); + client.flush(); - request += c; + // 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 (c == '\n') { - String postres = http_client.readString(); - int indp = postres.indexOf("progo=")+6; - Serial.println(postres); - Serial.println(request); + if (plugId >= 0 && plugId < 8) { + client.println("HTTP/1.1 200 OK"); + client.println("Content-Type: text/plain"); + client.println("Connection: close"); + client.println(); - - html(); - break; - } + sendSchedule(client, scheduler_list[plugId]); + } else { + client.println("HTTP/1.1 404 Not Found"); + client.println("Connection: close"); + client.println(); } } - delay(1); - request = ""; + // 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(); diff --git a/html/index.html b/html/index.html new file mode 100644 index 0000000..589503b --- /dev/null +++ b/html/index.html @@ -0,0 +1,281 @@ + + + ESP32 timed Switch + + + +

Scheduler

+ + + + +
+ + + + + + + + +
+ + + + + diff --git a/html/mockup.py b/html/mockup.py new file mode 100755 index 0000000..d2c43ed --- /dev/null +++ b/html/mockup.py @@ -0,0 +1,76 @@ +#! /usr/bin/env python3 + +from flask import Flask, request, send_from_directory, Response +import random +import os + +app = Flask(__name__) +PORT_FROM_ENV = os.environ.get('PORT') +if PORT_FROM_ENV: + PORT = PORT_FROM_ENV +else: + PORT = "3000" + +static_file_dir = os.path.dirname(os.path.realpath(__file__)) + + +@app.route('/', methods=['GET']) +def serve_index(): + return send_from_directory(static_file_dir, 'index.html') + + +@app.route('/', methods=['GET']) +def serve_file_in_dir(path): + if not os.path.isfile(os.path.join(static_file_dir, path)): + path = os.path.join(path, 'index.html') + print("Serving local file : {}".format(path)) + return send_from_directory(static_file_dir, path) + +# Generate a random 12-bit integer (0 to 4095) +def random_32_bit_int(): + return random.randint(0, 0xFFF) + +# Convert 24 integers to a binary stream +def ints_to_binary_stream(integers): + binary_data = bytearray() + for integer in integers: + # Ensure integer is in 32-bit range + integer &= 0xFFF + # Convert to bytes and append + binary_data.extend(integer.to_bytes(4, byteorder='big')) # 2 bytes per integer (32-bit) + return binary_data + +# Convert a binary stream to 24 integers +def binary_stream_to_ints(binary_data): + integers = [] + # Every 32-bit integer is represented by 2 bytes + for i in range(0, len(binary_data), 4): + integers.append(int.from_bytes(binary_data[i:i+4], byteorder='big') & 0xFFF) # Extract 32-bit value + return integers + +@app.route('/api/schedule/', methods=['GET']) +def get_schedule(plug_id): + # Generate 24 random 32-bit integers + schedule = [random_32_bit_int() for _ in range(24)] + # Convert to binary stream + binary_data = ints_to_binary_stream(schedule) + # Return as a binary response + return Response(binary_data, content_type='application/octet-stream') + +@app.route('/api/schedule/', methods=['POST']) +def update_schedule(plug_id): + # Get binary data from the request + binary_data = request.data + # Convert binary stream to integers + new_schedule = binary_stream_to_ints(binary_data) + print(f"Received new schedule for plug {plug_id}: {new_schedule}") + # Respond with a success message in binary format + return Response(b'Update successful', status=200, mimetype='text/plain') + + +if __name__ == '__main__': + app.run( + host="0.0.0.0", + port=PORT, + use_debugger=True + )