Added basic WebSocket Functionality
This commit is contained in:
17
.vscode/arduino.json
vendored
17
.vscode/arduino.json
vendored
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"port": "",
|
||||
"configuration": "",
|
||||
"output": "build",
|
||||
"board": "",
|
||||
"programmer": "",
|
||||
"useProgrammer": false,
|
||||
"configurationRequired": false,
|
||||
"monitorPortSettings": {
|
||||
"port": "",
|
||||
"baudRate": 115200,
|
||||
"lineEnding": "\r\n",
|
||||
"dataBits": 8,
|
||||
"parity": "none",
|
||||
"stopBits": "one"
|
||||
}
|
||||
}
|
||||
21
.vscode/c_cpp_properties.json
vendored
21
.vscode/c_cpp_properties.json
vendored
@@ -1,21 +0,0 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Win32",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**",
|
||||
"C:/Users/Admin/Documents/Arduino/libraries/**"
|
||||
],
|
||||
"defines": [
|
||||
"_DEBUG",
|
||||
"UNICODE",
|
||||
"_UNICODE"
|
||||
],
|
||||
"compilerPath": null,
|
||||
"cStandard": "c17",
|
||||
"cppStandard": "c++14",
|
||||
"intelliSenseMode": "windows-clang-x64"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
4
.vscode/settings.json
vendored
4
.vscode/settings.json
vendored
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"C_Cpp.default.compilerPath": "",
|
||||
"C_Cpp.errorSquiggles": "disabled"
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
/*KC868-A6 DS1307 CODE*/
|
||||
// Date and time functions using a DS1307 RTC connected via I2C and Wire lib
|
||||
#include "RTClib.h"
|
||||
|
||||
RTC_DS1307 rtc;
|
||||
|
||||
char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
|
||||
|
||||
void setup () {
|
||||
Serial.begin(57600);
|
||||
|
||||
#ifndef ESP8266
|
||||
while (!Serial); // wait for serial port to connect. Needed for native USB
|
||||
#endif
|
||||
|
||||
if (! rtc.begin()) {
|
||||
Serial.println("Couldn't find RTC");
|
||||
Serial.flush();
|
||||
while (1) delay(10);
|
||||
}
|
||||
|
||||
if (! rtc.isrunning()) {
|
||||
Serial.println("RTC is NOT running, let's set the time!");
|
||||
// When time needs to be set on a new device, or after a power loss, the
|
||||
// following line sets the RTC to the date & time this sketch was compiled
|
||||
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
|
||||
// This line sets the RTC with an explicit date & time, for example to set
|
||||
// January 21, 2014 at 3am you would call:
|
||||
// rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
|
||||
}
|
||||
|
||||
// When time needs to be re-set on a previously configured device, the
|
||||
// following line sets the RTC to the date & time this sketch was compiled
|
||||
// rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
|
||||
// This line sets the RTC with an explicit date & time, for example to set
|
||||
// January 21, 2014 at 3am you would call:
|
||||
// rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
|
||||
}
|
||||
|
||||
void loop () {
|
||||
DateTime now = rtc.now();
|
||||
|
||||
Serial.print(now.year(), DEC);
|
||||
Serial.print('/');
|
||||
Serial.print(now.month(), DEC);
|
||||
Serial.print('/');
|
||||
Serial.print(now.day(), DEC);
|
||||
Serial.print(" (");
|
||||
Serial.print(daysOfTheWeek[now.dayOfTheWeek()]);
|
||||
Serial.print(") ");
|
||||
Serial.print(now.hour(), DEC);
|
||||
Serial.print(':');
|
||||
Serial.print(now.minute(), DEC);
|
||||
Serial.print(':');
|
||||
Serial.print(now.second(), DEC);
|
||||
Serial.println();
|
||||
|
||||
Serial.print(" since midnight 1/1/1970 = ");
|
||||
Serial.print(now.unixtime());
|
||||
Serial.print("s = ");
|
||||
Serial.print(now.unixtime() / 86400L);
|
||||
Serial.println("d");
|
||||
|
||||
// calculate a date which is 7 days, 12 hours, 30 minutes, and 6 seconds into the future
|
||||
DateTime future (now + TimeSpan(7,12,30,6));
|
||||
|
||||
Serial.print(" now + 7d + 12h + 30m + 6s: ");
|
||||
Serial.print(future.year(), DEC);
|
||||
Serial.print('/');
|
||||
Serial.print(future.month(), DEC);
|
||||
Serial.print('/');
|
||||
Serial.print(future.day(), DEC);
|
||||
Serial.print(' ');
|
||||
Serial.print(future.hour(), DEC);
|
||||
Serial.print(':');
|
||||
Serial.print(future.minute(), DEC);
|
||||
Serial.print(':');
|
||||
Serial.print(future.second(), DEC);
|
||||
Serial.println();
|
||||
|
||||
Serial.println();
|
||||
delay(3000);
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
#include "Arduino.h"
|
||||
#include "PCF8574.h"
|
||||
|
||||
// Set i2c address
|
||||
PCF8574 pcf8574(0x22,4,15);
|
||||
unsigned long timeElapsed;
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
delay(1000);
|
||||
|
||||
pcf8574.pinMode(P0, INPUT);
|
||||
pcf8574.pinMode(P1, INPUT);
|
||||
pcf8574.pinMode(P2, INPUT);
|
||||
pcf8574.pinMode(P3, INPUT);
|
||||
pcf8574.pinMode(P4, INPUT);
|
||||
pcf8574.pinMode(P5, INPUT);
|
||||
pcf8574.pinMode(P6, INPUT);
|
||||
pcf8574.pinMode(P7, INPUT);
|
||||
|
||||
Serial.print("Init pcf8574...");
|
||||
if (pcf8574.begin()){
|
||||
Serial.println("OK");
|
||||
}else{
|
||||
Serial.println("KO");
|
||||
}
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
uint8_t val1 = pcf8574.digitalRead(P0);
|
||||
uint8_t val2 = pcf8574.digitalRead(P1);
|
||||
uint8_t val3 = pcf8574.digitalRead(P2);
|
||||
uint8_t val4 = pcf8574.digitalRead(P3);
|
||||
uint8_t val5 = pcf8574.digitalRead(P4);
|
||||
uint8_t val6 = pcf8574.digitalRead(P5);
|
||||
uint8_t val7 = pcf8574.digitalRead(P6);
|
||||
uint8_t val8 = pcf8574.digitalRead(P7);
|
||||
if (val1==LOW) Serial.println("KEY1 PRESSED");
|
||||
if (val2==LOW) Serial.println("KEY2 PRESSED");
|
||||
if (val3==LOW) Serial.println("KEY3 PRESSED");
|
||||
if (val4==LOW) Serial.println("KEY4 PRESSED");
|
||||
if (val5==LOW) Serial.println("KEY5 PRESSED");
|
||||
if (val6==LOW) Serial.println("KEY6 PRESSED");
|
||||
if (val7==LOW) Serial.println("KEY7 PRESSED");
|
||||
if (val8==LOW) Serial.println("KEY8 PRESSED");
|
||||
delay(300);
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
/*
|
||||
Blink led on PIN0
|
||||
by Mischianti Renzo <http://www.mischianti.org>
|
||||
|
||||
https://www.mischianti.org/2019/01/02/pcf8574-i2c-digital-i-o-expander-fast-easy-usage/
|
||||
*/
|
||||
|
||||
#include "Arduino.h"
|
||||
#include "PCF8574.h"
|
||||
|
||||
// Set i2c address
|
||||
PCF8574 pcf8574(0x24,4,15);
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
// delay(1000);
|
||||
|
||||
// Set pinMode to OUTPUT
|
||||
pcf8574.pinMode(P0, OUTPUT);
|
||||
pcf8574.pinMode(P1, OUTPUT);
|
||||
pcf8574.pinMode(P2, OUTPUT);
|
||||
pcf8574.pinMode(P3, OUTPUT);
|
||||
pcf8574.pinMode(P4, OUTPUT);
|
||||
pcf8574.pinMode(P5, OUTPUT);
|
||||
|
||||
|
||||
pcf8574.digitalWrite(P0, HIGH);
|
||||
pcf8574.digitalWrite(P1, HIGH);
|
||||
pcf8574.digitalWrite(P2, HIGH);
|
||||
pcf8574.digitalWrite(P3, HIGH);
|
||||
pcf8574.digitalWrite(P4, HIGH);
|
||||
pcf8574.digitalWrite(P5, HIGH);
|
||||
|
||||
Serial.print("Init pcf8574...");
|
||||
if (pcf8574.begin()){
|
||||
Serial.println("OK");
|
||||
}else{
|
||||
Serial.println("KO");
|
||||
}
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
delay(300);
|
||||
pcf8574.digitalWrite(P0, LOW);
|
||||
delay(300);
|
||||
pcf8574.digitalWrite(P1, LOW);
|
||||
delay(300);
|
||||
pcf8574.digitalWrite(P2, LOW);
|
||||
delay(300);
|
||||
pcf8574.digitalWrite(P3, LOW);
|
||||
delay(300);
|
||||
pcf8574.digitalWrite(P4, LOW);
|
||||
delay(300);
|
||||
pcf8574.digitalWrite(P5, LOW);
|
||||
delay(300);
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
/*KC868-A6 DS1307 CODE*/
|
||||
// Date and time functions using a DS1307 RTC connected via I2C and Wire lib
|
||||
#include "RTClib.h"
|
||||
|
||||
RTC_DS1307 rtc;
|
||||
|
||||
char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
|
||||
|
||||
void setup () {
|
||||
Serial.begin(57600);
|
||||
|
||||
#ifndef ESP8266
|
||||
while (!Serial); // wait for serial port to connect. Needed for native USB
|
||||
#endif
|
||||
|
||||
if (! rtc.begin()) {
|
||||
Serial.println("Couldn't find RTC");
|
||||
Serial.flush();
|
||||
while (1) delay(10);
|
||||
}
|
||||
|
||||
if (! rtc.isrunning()) {
|
||||
Serial.println("RTC is NOT running, let's set the time!");
|
||||
// When time needs to be set on a new device, or after a power loss, the
|
||||
// following line sets the RTC to the date & time this sketch was compiled
|
||||
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
|
||||
// This line sets the RTC with an explicit date & time, for example to set
|
||||
// January 21, 2014 at 3am you would call:
|
||||
// rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
|
||||
}
|
||||
|
||||
// When time needs to be re-set on a previously configured device, the
|
||||
// following line sets the RTC to the date & time this sketch was compiled
|
||||
// rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
|
||||
// This line sets the RTC with an explicit date & time, for example to set
|
||||
// January 21, 2014 at 3am you would call:
|
||||
// rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
|
||||
}
|
||||
|
||||
void loop () {
|
||||
DateTime now = rtc.now();
|
||||
|
||||
Serial.print(now.year(), DEC);
|
||||
Serial.print('/');
|
||||
Serial.print(now.month(), DEC);
|
||||
Serial.print('/');
|
||||
Serial.print(now.day(), DEC);
|
||||
Serial.print(" (");
|
||||
Serial.print(daysOfTheWeek[now.dayOfTheWeek()]);
|
||||
Serial.print(") ");
|
||||
Serial.print(now.hour(), DEC);
|
||||
Serial.print(':');
|
||||
Serial.print(now.minute(), DEC);
|
||||
Serial.print(':');
|
||||
Serial.print(now.second(), DEC);
|
||||
Serial.println();
|
||||
|
||||
Serial.print(" since midnight 1/1/1970 = ");
|
||||
Serial.print(now.unixtime());
|
||||
Serial.print("s = ");
|
||||
Serial.print(now.unixtime() / 86400L);
|
||||
Serial.println("d");
|
||||
|
||||
// calculate a date which is 7 days, 12 hours, 30 minutes, and 6 seconds into the future
|
||||
DateTime future (now + TimeSpan(7,12,30,6));
|
||||
|
||||
Serial.print(" now + 7d + 12h + 30m + 6s: ");
|
||||
Serial.print(future.year(), DEC);
|
||||
Serial.print('/');
|
||||
Serial.print(future.month(), DEC);
|
||||
Serial.print('/');
|
||||
Serial.print(future.day(), DEC);
|
||||
Serial.print(' ');
|
||||
Serial.print(future.hour(), DEC);
|
||||
Serial.print(':');
|
||||
Serial.print(future.minute(), DEC);
|
||||
Serial.print(':');
|
||||
Serial.print(future.second(), DEC);
|
||||
Serial.println();
|
||||
|
||||
Serial.println();
|
||||
delay(3000);
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
|
||||
void setup() {
|
||||
}
|
||||
|
||||
void loop() {
|
||||
}
|
||||
17
vesper/.vscode/arduino.json
vendored
17
vesper/.vscode/arduino.json
vendored
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"port": "COM8",
|
||||
"configuration": "UploadSpeed=921600,CPUFreq=240,FlashFreq=80,FlashMode=qio,FlashSize=4M,PartitionScheme=default,DebugLevel=none,PSRAM=disabled,LoopCore=1,EventsCore=1,EraseFlash=none,JTAGAdapter=default,ZigbeeMode=default",
|
||||
"output": "build",
|
||||
"board": "esp32:esp32:esp32",
|
||||
"programmer": "",
|
||||
"useProgrammer": false,
|
||||
"configurationRequired": true,
|
||||
"monitorPortSettings": {
|
||||
"port": "COM8",
|
||||
"baudRate": 115200,
|
||||
"lineEnding": "\r\n",
|
||||
"dataBits": 8,
|
||||
"parity": "none",
|
||||
"stopBits": "one"
|
||||
}
|
||||
}
|
||||
101
vesper/.vscode/c_cpp_properties.json
vendored
101
vesper/.vscode/c_cpp_properties.json
vendored
@@ -1,101 +0,0 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Arduino",
|
||||
"includePath": [
|
||||
"C:\\Users\\espi_\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\3.1.1\\cores\\esp32/**",
|
||||
"C:\\Users\\espi_\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\3.1.1\\variants\\esp32/**",
|
||||
"C:\\Users\\espi_\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\3.1.1\\libraries\\WiFi\\src/**",
|
||||
"C:\\Users\\espi_\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\3.1.1\\libraries\\Network\\src/**",
|
||||
"C:\\Users\\espi_\\Documents\\Arduino\\libraries\\AsyncMqttClient\\src/**",
|
||||
"C:\\Users\\espi_\\Documents\\Arduino\\libraries\\Async_TCP\\src/**",
|
||||
"C:\\Users\\espi_\\Documents\\Arduino\\libraries\\ArduinoJson\\src/**",
|
||||
"C:\\Users\\espi_\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\3.1.1\\libraries\\FS\\src/**",
|
||||
"C:\\Users\\espi_\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\3.1.1\\libraries\\SPIFFS\\src/**",
|
||||
"C:\\Users\\espi_\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\3.1.1\\libraries\\Wire\\src/**",
|
||||
"C:\\Users\\espi_\\Documents\\Arduino\\libraries\\Adafruit_PCF8574/**",
|
||||
"C:\\Users\\espi_\\Documents\\Arduino\\libraries\\Adafruit_BusIO/**",
|
||||
"C:\\Users\\espi_\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\3.1.1\\libraries\\SPI\\src/**",
|
||||
"C:\\Users\\espi_\\Documents\\Arduino\\libraries/**",
|
||||
"C:\\Users\\espi_\\Documents\\Arduino\\4. Bell Systems\\1. Main Projects\\Project - Vesper/**",
|
||||
"C:\\Users\\espi_\\AppData\\Local\\Arduino15\\packages\\esp32\\tools\\esp32-arduino-libs\\idf-release_v5.3-cfea4f7c-v1\\esp32\\include/**"
|
||||
],
|
||||
"forcedInclude": [
|
||||
"C:\\Users\\espi_\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\3.1.1\\cores\\esp32\\Arduino.h"
|
||||
],
|
||||
"compilerPath": "C:\\Users\\espi_\\AppData\\Local\\Arduino15\\packages\\esp32\\tools\\esp-x32\\2405/bin/xtensa-esp32-elf-g++",
|
||||
"compilerArgs": [
|
||||
"C:\\Users\\espi_\\AppData\\Local\\Arduino15\\packages\\esp32\\tools\\esp-x32\\2405/bin/xtensa-esp32-elf-g++",
|
||||
"-MMD",
|
||||
"-c",
|
||||
"@C:\\Users\\espi_\\AppData\\Local\\Arduino15\\packages\\esp32\\tools\\esp32-arduino-libs\\idf-release_v5.3-cfea4f7c-v1\\esp32/flags/cpp_flags",
|
||||
"-w",
|
||||
"-Os",
|
||||
"-Werror=return-type",
|
||||
"-DF_CPU=240000000L",
|
||||
"-DARDUINO=10607",
|
||||
"-DARDUINO_ESP32_DEV",
|
||||
"-DARDUINO_ARCH_ESP32",
|
||||
"-DARDUINO_BOARD=\"ESP32_DEV\"",
|
||||
"-DARDUINO_VARIANT=\"esp32\"",
|
||||
"-DARDUINO_PARTITION_default",
|
||||
"-DARDUINO_HOST_OS=\"windows\"",
|
||||
"-DARDUINO_FQBN=\"esp32:esp32:esp32:UploadSpeed=921600,CPUFreq=240,FlashFreq=80,FlashMode=qio,FlashSize=4M,PartitionScheme=default,DebugLevel=none,PSRAM=disabled,LoopCore=1,EventsCore=1,EraseFlash=none,JTAGAdapter=default,ZigbeeMode=default\"",
|
||||
"-DESP32=ESP32",
|
||||
"-DCORE_DEBUG_LEVEL=0",
|
||||
"-DARDUINO_RUNNING_CORE=1",
|
||||
"-DARDUINO_EVENT_RUNNING_CORE=1",
|
||||
"-DARDUINO_USB_CDC_ON_BOOT=0",
|
||||
"@C:\\Users\\espi_\\AppData\\Local\\Arduino15\\packages\\esp32\\tools\\esp32-arduino-libs\\idf-release_v5.3-cfea4f7c-v1\\esp32/flags/defines",
|
||||
"-IC:\\Users\\espi_\\Documents\\Arduino\\4. Bell Systems\\1. Main Projects\\Project - Vesper",
|
||||
"-iprefix",
|
||||
"C:\\Users\\espi_\\AppData\\Local\\Arduino15\\packages\\esp32\\tools\\esp32-arduino-libs\\idf-release_v5.3-cfea4f7c-v1\\esp32/include/",
|
||||
"@C:\\Users\\espi_\\AppData\\Local\\Arduino15\\packages\\esp32\\tools\\esp32-arduino-libs\\idf-release_v5.3-cfea4f7c-v1\\esp32/flags/includes",
|
||||
"-IC:\\Users\\espi_\\AppData\\Local\\Arduino15\\packages\\esp32\\tools\\esp32-arduino-libs\\idf-release_v5.3-cfea4f7c-v1\\esp32/qio_qspi/include",
|
||||
"-IC:\\Users\\espi_\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\3.1.1\\cores\\esp32",
|
||||
"-IC:\\Users\\espi_\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\3.1.1\\variants\\esp32",
|
||||
"-IC:\\Users\\espi_\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\3.1.1\\libraries\\WiFi\\src",
|
||||
"-IC:\\Users\\espi_\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\3.1.1\\libraries\\Network\\src",
|
||||
"-IC:\\Users\\espi_\\Documents\\Arduino\\libraries\\AsyncMqttClient\\src",
|
||||
"-IC:\\Users\\espi_\\Documents\\Arduino\\libraries\\Async_TCP\\src",
|
||||
"-IC:\\Users\\espi_\\Documents\\Arduino\\libraries\\ArduinoJson\\src",
|
||||
"-IC:\\Users\\espi_\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\3.1.1\\libraries\\FS\\src",
|
||||
"-IC:\\Users\\espi_\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\3.1.1\\libraries\\SPIFFS\\src",
|
||||
"-IC:\\Users\\espi_\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\3.1.1\\libraries\\Wire\\src",
|
||||
"-IC:\\Users\\espi_\\Documents\\Arduino\\libraries\\Adafruit_PCF8574",
|
||||
"-IC:\\Users\\espi_\\Documents\\Arduino\\libraries\\Adafruit_BusIO",
|
||||
"-IC:\\Users\\espi_\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\3.1.1\\libraries\\SPI\\src",
|
||||
"@C:\\Users\\espi_\\Documents\\Arduino\\4. Bell Systems\\1. Main Projects\\Project - Vesper\\build/build_opt.h",
|
||||
"@C:\\Users\\espi_\\Documents\\Arduino\\4. Bell Systems\\1. Main Projects\\Project - Vesper\\build/file_opts",
|
||||
"C:\\Users\\espi_\\Documents\\Arduino\\4. Bell Systems\\1. Main Projects\\Project - Vesper\\build\\sketch\\Project - Vesper.ino.cpp",
|
||||
"-o",
|
||||
"C:\\Users\\espi_\\Documents\\Arduino\\4. Bell Systems\\1. Main Projects\\Project - Vesper\\build\\sketch\\Project - Vesper.ino.cpp.o"
|
||||
],
|
||||
"defines": [
|
||||
"F_CPU=240000000L",
|
||||
"ARDUINO=10607",
|
||||
"ARDUINO_ESP32_DEV",
|
||||
"ARDUINO_ARCH_ESP32",
|
||||
"ARDUINO_BOARD=\"ESP32_DEV\"",
|
||||
"ARDUINO_VARIANT=\"esp32\"",
|
||||
"ARDUINO_PARTITION_default",
|
||||
"ARDUINO_HOST_OS=\"windows\"",
|
||||
"ARDUINO_FQBN=\"esp32:esp32:esp32:UploadSpeed=921600,CPUFreq=240,FlashFreq=80,FlashMode=qio,FlashSize=4M,PartitionScheme=default,DebugLevel=none,PSRAM=disabled,LoopCore=1,EventsCore=1,EraseFlash=none,JTAGAdapter=default,ZigbeeMode=default\"",
|
||||
"ESP32=ESP32",
|
||||
"CORE_DEBUG_LEVEL=0",
|
||||
"ARDUINO_RUNNING_CORE=1",
|
||||
"ARDUINO_EVENT_RUNNING_CORE=1",
|
||||
"ARDUINO_USB_CDC_ON_BOOT=0",
|
||||
"ARDUINO_BOARD=\\\"ESP32_DEV\\\"\"",
|
||||
"ARDUINO_VARIANT=\\\"esp32\\\"\"",
|
||||
"ARDUINO_HOST_OS=\\\"windows\\\"\"",
|
||||
"ARDUINO_FQBN=\\\"esp32:esp32:esp32:UploadSpeed=921600,CPUFreq=240,FlashFreq=80,FlashMode=qio,FlashSize=4M,PartitionScheme=default,DebugLevel=none,PSRAM=disabled,LoopCore=1,EventsCore=1,EraseFlash=none,JTAGAdapter=default,ZigbeeMode=default\\\"\"",
|
||||
"ARDUINO_CORE_BUILD",
|
||||
"USBCON"
|
||||
],
|
||||
"cStandard": "c17",
|
||||
"cppStandard": "c++17"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
@@ -16,30 +16,45 @@ JsonDocument handleJSON(char * payload) {
|
||||
return doc;
|
||||
}
|
||||
|
||||
|
||||
void replyOnWebSocket(AsyncWebSocketClient *client, String list) {
|
||||
client->text(list);
|
||||
}
|
||||
|
||||
// Handles the JSON Commands
|
||||
void handleCommand(JsonDocument json, AsyncWebSocketClient *client = nullptr){
|
||||
|
||||
String cmd = json["cmd"];
|
||||
JsonVariant contents = json["contents"];
|
||||
|
||||
if (cmd == "playback") {
|
||||
player.command(contents);
|
||||
} else if (cmd == "set_melody") {
|
||||
player.setMelodyAttributes(contents);
|
||||
player.loadMelodyInRAM(melody_steps);
|
||||
} else if (cmd == "list_melodies") {
|
||||
String list = listFilesAsJson("/melodies");
|
||||
PublishMqtt(list.c_str());
|
||||
if (client) {
|
||||
replyOnWebSocket(client, list); // Only reply via WS if client exists
|
||||
}
|
||||
} else if (cmd == "set_relay_timers") {
|
||||
updateRelayTimings(contents);
|
||||
} else if (cmd == "add_melody"){
|
||||
addMelody(contents);
|
||||
} else {
|
||||
LOG_WARNING("Unknown Command Received");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Subscribes to certain topics on the MQTT Server.
|
||||
void SuscribeMqtt() {
|
||||
|
||||
String topicPlayback = String("vesper/") + DEV_ID + "/control/playback";
|
||||
String topicSetMelody = String("vesper/") + DEV_ID + "/control/setMelody";
|
||||
String topicAddMelody = String("vesper/") + DEV_ID + "/control/addMelody";
|
||||
String topicAddSchedule = String("vesper/") + DEV_ID + "/control/addSchedule";
|
||||
String topicRelayTimers = String("vesper/") + DEV_ID + "/control/settings/relayTimers";
|
||||
|
||||
uint16_t control_id = mqttClient.subscribe(topicPlayback.c_str(), 2);
|
||||
LOG_INFO("Subscribing to Playback Control topic, QoS 2, packetId: %d", control_id);
|
||||
|
||||
uint16_t set_melody_id = mqttClient.subscribe(topicSetMelody.c_str(), 2);
|
||||
LOG_INFO("Subscribing to Set-Melody topic, QoS 2, packetId: %d", set_melody_id);
|
||||
|
||||
// doesn't work yet:
|
||||
uint16_t add_melody_id = mqttClient.subscribe(topicAddMelody.c_str(), 2);
|
||||
LOG_INFO("Subscribing to Add-Melody topic, QoS 2, packetId: %d", add_melody_id);
|
||||
|
||||
uint16_t add_schedule_id = mqttClient.subscribe(topicAddSchedule.c_str(), 2);
|
||||
LOG_INFO("Subscribing to Add-Schedule topic, QoS 2, packetId: %d", add_schedule_id);
|
||||
|
||||
uint16_t relay_timers_id = mqttClient.subscribe(topicRelayTimers.c_str(), 2);
|
||||
LOG_INFO("Subscribing to Relay-Timers topic, QoS 2, packetId: %d", relay_timers_id);
|
||||
String command = String("vesper/") + DEV_ID + "/control";
|
||||
uint16_t command_id = mqttClient.subscribe(command.c_str(), 2);
|
||||
LOG_INFO("Subscribing to Command topic, QoS 2, packetId: %d", command_id);
|
||||
|
||||
}
|
||||
|
||||
@@ -47,41 +62,36 @@ void SuscribeMqtt() {
|
||||
// Could move logic out of this into a dedicated function.
|
||||
void OnMqttReceived(char * topic, char * payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total) {
|
||||
|
||||
String topicPlayback = String("vesper/") + DEV_ID + "/control/playback";
|
||||
String topicSetMelody = String("vesper/") + DEV_ID + "/control/setMelody";
|
||||
String topicAddMelody = String("vesper/") + DEV_ID + "/control/addMelody";
|
||||
String topicAddSchedule = String("vesper/") + DEV_ID + "/control/addSchedule";
|
||||
String topicRelayTimers = String("vesper/") + DEV_ID + "/control/settings/relayTimers";
|
||||
String command = String("vesper/") + DEV_ID + "/control";
|
||||
|
||||
// Don't know what this is. Check it out later.
|
||||
//String payloadContent = String(payload).substring(0, len);
|
||||
|
||||
if (String(topic) == topicPlayback){
|
||||
player.command(payload);
|
||||
if (String(topic) == command){
|
||||
JsonDocument json = handleJSON(payload);
|
||||
handleCommand(json);
|
||||
}
|
||||
}
|
||||
|
||||
else if (String(topic) == topicSetMelody) {
|
||||
player.setMelodyAttributes(handleJSON(payload));
|
||||
player.loadMelodyInRAM(melody_steps);
|
||||
|
||||
// Handles incoming WebSocket messages on subscribed topics.
|
||||
// Could move logic out of this into a dedicated function.
|
||||
void onWebSocketReceived(AsyncWebSocketClient *client, void *arg, uint8_t *data, size_t len) {
|
||||
AwsFrameInfo *info = (AwsFrameInfo*)arg;
|
||||
if (info->final && info->index == 0 && info->len == len && info->opcode == WS_TEXT) {
|
||||
data[len] = '\0'; // Null-terminate the received data
|
||||
Serial.printf("Received message: %s\n", (char*)data);
|
||||
|
||||
JsonDocument json = handleJSON((char*)data);
|
||||
handleCommand(json, client);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
else if (String(topic) == topicAddMelody) {
|
||||
// Handle adding melody
|
||||
LOG_INFO("Adding melody...");
|
||||
// You can call a function here to handle adding the melody
|
||||
}
|
||||
|
||||
else if (String(topic) == topicAddSchedule) {
|
||||
updateSchedule(handleJSON(payload));
|
||||
}
|
||||
|
||||
else if (String(topic) == topicRelayTimers) {
|
||||
updateRelayTimings(handleJSON(payload));
|
||||
}
|
||||
|
||||
else {
|
||||
// Handle unknown topics
|
||||
LOG_WARNING("Unknown topic received.");
|
||||
void onWebSocketEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len) {
|
||||
if (type == WS_EVT_DATA) {
|
||||
onWebSocketReceived(client, arg, data, len);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,21 +12,17 @@ String GetPayloadContent(char * data, size_t len) {
|
||||
return content;
|
||||
}
|
||||
|
||||
void ConnectToMqtt()
|
||||
{
|
||||
void ConnectToMqtt() {
|
||||
LOG_INFO("Connecting to MQTT...");
|
||||
mqttClient.connect();
|
||||
}
|
||||
|
||||
void OnMqttConnect(bool sessionPresent)
|
||||
{
|
||||
void OnMqttConnect(bool sessionPresent) {
|
||||
LOG_INFO("Connected to MQTT.");
|
||||
//LOG_INFO("Session present: %s", sessionPresent ? "Yes":"No");
|
||||
SuscribeMqtt();
|
||||
}
|
||||
|
||||
void OnMqttDisconnect(AsyncMqttClientDisconnectReason reason)
|
||||
{
|
||||
void OnMqttDisconnect(AsyncMqttClientDisconnectReason reason) {
|
||||
LOG_WARNING("Disconnected from MQTT.");
|
||||
|
||||
if(WiFi.isConnected())
|
||||
@@ -35,51 +31,44 @@ void OnMqttDisconnect(AsyncMqttClientDisconnectReason reason)
|
||||
}
|
||||
}
|
||||
|
||||
void OnMqttSubscribe(uint16_t packetId, uint8_t qos)
|
||||
{
|
||||
void OnMqttSubscribe(uint16_t packetId, uint8_t qos) {
|
||||
LOG_INFO("Subscribe acknowledged. PacketID: %d / QoS: %d", packetId, qos);
|
||||
}
|
||||
|
||||
void OnMqttUnsubscribe(uint16_t packetId)
|
||||
{
|
||||
void OnMqttUnsubscribe(uint16_t packetId) {
|
||||
LOG_INFO("Unsubscribe Acknowledged. PacketID: %d",packetId);
|
||||
}
|
||||
|
||||
void OnMqttPublish(uint16_t packetId)
|
||||
{
|
||||
void OnMqttPublish(uint16_t packetId) {
|
||||
LOG_INFO("Publish Acknowledged. PacketID: %d", packetId);
|
||||
}
|
||||
|
||||
void ConnectWiFi_STA(bool useStaticIP = false)
|
||||
{
|
||||
Serial.println("");
|
||||
void ConnectWiFi_STA(bool useStaticIP = true) {
|
||||
WiFi.mode(WIFI_STA);
|
||||
if(useStaticIP) {
|
||||
WiFi.config(ip, gateway, subnet);
|
||||
WiFi.setHostname(hostname);
|
||||
}
|
||||
WiFi.begin(ssid, password);
|
||||
//WiFi.begin(ssid, password);
|
||||
WiFi.begin();
|
||||
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
delay(100);
|
||||
Serial.print('.');
|
||||
delay(10);
|
||||
}
|
||||
|
||||
if (LOG_LEVEL_ENABLED(LOG_LEVEL_INFO)){
|
||||
Serial.println("");
|
||||
Serial.print("Initiating STA:\t");
|
||||
Serial.print("NIGGA - Initiating STA:\t");
|
||||
Serial.println(ssid);
|
||||
Serial.print("IP address:\t");
|
||||
Serial.println(WiFi.localIP());
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectWiFi_AP(bool useStaticIP = false)
|
||||
{
|
||||
void ConnectWiFi_AP(bool useStaticIP = false) {
|
||||
Serial.println("");
|
||||
WiFi.mode(WIFI_AP);
|
||||
while(!WiFi.softAP(ssid, password))
|
||||
{
|
||||
while(!WiFi.softAP(ssid, password)) {
|
||||
Serial.println(".");
|
||||
delay(100);
|
||||
}
|
||||
@@ -95,26 +84,53 @@ void ConnectWiFi_AP(bool useStaticIP = false)
|
||||
}
|
||||
}
|
||||
|
||||
void WiFiEvent(WiFiEvent_t event)
|
||||
{
|
||||
LOG_INFO("[WiFi-event] event: %d\n", event);
|
||||
void NetworkEvent(arduino_event_id_t event, arduino_event_info_t info) {
|
||||
LOG_INFO("(NET) event: %d\n", event);
|
||||
IPAddress ip = WiFi.localIP();
|
||||
switch(event)
|
||||
{
|
||||
case ARDUINO_EVENT_WIFI_STA_GOT_IP:
|
||||
Serial.print("[INFO] - WiFi connected. IP Address: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
LOG_DEBUG("WiFi connected. IP Address: %d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
|
||||
//xTimerStop(wifiReconnectTimer, 0);
|
||||
ConnectToMqtt();
|
||||
break;
|
||||
case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
|
||||
LOG_WARNING("WiFi Lost Connection! :()");
|
||||
xTimerStop(mqttReconnectTimer, 0); // ensure we don't reconnect to MQTT while reconnecting to Wi-Fi
|
||||
LOG_WARNING("WiFi Lost Connection! :(");
|
||||
xTimerStop(mqttReconnectTimer, 0);
|
||||
xTimerStart(wifiReconnectTimer, 0);
|
||||
break;
|
||||
|
||||
case ARDUINO_EVENT_ETH_START:
|
||||
LOG_DEBUG("ETH Started");
|
||||
ETH.setHostname(hostname);
|
||||
break;
|
||||
case ARDUINO_EVENT_ETH_CONNECTED:
|
||||
LOG_DEBUG("ETH Connected !");
|
||||
break;
|
||||
case ARDUINO_EVENT_ETH_GOT_IP:
|
||||
LOG_INFO("ETH Got IP: '%s'\n", esp_netif_get_desc(info.got_ip.esp_netif));
|
||||
WiFi.disconnect(true);
|
||||
ConnectToMqtt();
|
||||
break;
|
||||
case ARDUINO_EVENT_ETH_LOST_IP:
|
||||
LOG_WARNING("ETH Lost IP");
|
||||
break;
|
||||
case ARDUINO_EVENT_ETH_DISCONNECTED:
|
||||
LOG_WARNING("ETH Disconnected");
|
||||
xTimerStop(mqttReconnectTimer, 0);
|
||||
xTimerStart(wifiReconnectTimer, 0);
|
||||
break;
|
||||
case ARDUINO_EVENT_ETH_STOP:
|
||||
LOG_INFO("ETH Stopped");
|
||||
xTimerStop(mqttReconnectTimer, 0);
|
||||
xTimerStart(wifiReconnectTimer, 0);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void InitMqtt()
|
||||
{
|
||||
void InitMqtt() {
|
||||
mqttReconnectTimer = xTimerCreate("mqttTimer", pdMS_TO_TICKS(2000), pdFALSE, (void*)0, reinterpret_cast<TimerCallbackFunction_t>(ConnectToMqtt));
|
||||
wifiReconnectTimer = xTimerCreate("wifiTimer", pdMS_TO_TICKS(5000), pdFALSE, (void*)0, reinterpret_cast<TimerCallbackFunction_t>(ConnectWiFi_STA));
|
||||
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// Timed Function. Will Run ONCE per 10 sec
|
||||
void schedule_timer() {
|
||||
|
||||
// Tick the calendar to perform internal functions.
|
||||
// (Updates current time. Updates Daily Schedule if its Midnight)
|
||||
timekeeper.tick();
|
||||
timekeeper.checkAndRunSchedule(player);
|
||||
// This is here for debugging purposes only.
|
||||
timekeeper.printTimeNow();
|
||||
|
||||
}
|
||||
|
||||
// void dailyScheduleCheck(std::vector<ScheduleEntry>){
|
||||
|
||||
// }
|
||||
@@ -54,7 +54,6 @@ void relayControlTask(void *param) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void loop_playback(std::vector<uint16_t> &melody_steps) {
|
||||
|
||||
while(player.isPlaying && !player.isPaused){
|
||||
@@ -72,7 +71,6 @@ void loop_playback(std::vector<uint16_t> &melody_steps) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Function to activate relays for a specific note
|
||||
void itsHammerTime(uint16_t note) {
|
||||
uint64_t now = millis();
|
||||
|
||||
BIN
vesper/build/esp32.esp32.esp32/vesper.ino.bin
Normal file
BIN
vesper/build/esp32.esp32.esp32/vesper.ino.bin
Normal file
Binary file not shown.
BIN
vesper/build/esp32.esp32.esp32/vesper.ino.bootloader.bin
Normal file
BIN
vesper/build/esp32.esp32.esp32/vesper.ino.bootloader.bin
Normal file
Binary file not shown.
BIN
vesper/build/esp32.esp32.esp32/vesper.ino.elf
Normal file
BIN
vesper/build/esp32.esp32.esp32/vesper.ino.elf
Normal file
Binary file not shown.
137689
vesper/build/esp32.esp32.esp32/vesper.ino.map
Normal file
137689
vesper/build/esp32.esp32.esp32/vesper.ino.map
Normal file
File diff suppressed because one or more lines are too long
BIN
vesper/build/esp32.esp32.esp32/vesper.ino.merged.bin
Normal file
BIN
vesper/build/esp32.esp32.esp32/vesper.ino.merged.bin
Normal file
Binary file not shown.
BIN
vesper/build/esp32.esp32.esp32/vesper.ino.partitions.bin
Normal file
BIN
vesper/build/esp32.esp32.esp32/vesper.ino.partitions.bin
Normal file
Binary file not shown.
@@ -13,7 +13,7 @@ public:
|
||||
uint32_t loop_duration = 0; // Duration of the playback per segment
|
||||
uint32_t interval = 0; // Indicates the Duration of the Interval between finished segments, IF "inf" is true
|
||||
bool infinite_play = false; // Infinite Loop Indicator (If True the melody will loop forever or until stoped, with pauses of "interval" in between loops)
|
||||
bool isPlaying = false;; // Indicates if the Melody is actually Playing right now.
|
||||
bool isPlaying = false; // Indicates if the Melody is actually Playing right now.
|
||||
bool isPaused = false; // If playing, indicates if the Melody is Paused
|
||||
uint64_t startTime = 0; // The time-point the Melody started Playing
|
||||
uint64_t loopStartTime = 0; // The time-point the current segment started Playing
|
||||
@@ -51,19 +51,20 @@ public:
|
||||
}
|
||||
|
||||
// Handles Incoming Commands to PLAY or STOP
|
||||
void command(char * command){
|
||||
LOG_DEBUG("Incoming Command: %s",command);
|
||||
if (command[0] == '1') {
|
||||
void command(JsonVariant content){
|
||||
String action = content["action"];
|
||||
LOG_DEBUG("Incoming Command: %s", action);
|
||||
if (action == "play") {
|
||||
play();
|
||||
PublishMqtt("OK - PLAY");
|
||||
} else if (command[0] == '0') {
|
||||
} else if (action == "stop") {
|
||||
forceStop();
|
||||
PublishMqtt("OK - STOP");
|
||||
}
|
||||
}
|
||||
|
||||
// Sets incoming Attributes for the Melody, into the class' variables.
|
||||
void setMelodyAttributes(JsonDocument doc){
|
||||
void setMelodyAttributes(JsonVariant doc){
|
||||
|
||||
if (doc.containsKey("name")) {
|
||||
name = doc["name"].as<const char*>();
|
||||
@@ -98,229 +99,35 @@ public:
|
||||
);
|
||||
}
|
||||
|
||||
// Loads the Selected melody from a .bin file, into RAM
|
||||
|
||||
// Loads the Selected melody from a .bin file on SD into RAM
|
||||
void loadMelodyInRAM(std::vector<uint16_t> &melody_steps) {
|
||||
String filePath = "/melodies/" + String(name.c_str()) + ".bin";
|
||||
|
||||
LOG_INFO("Loading Melody.");
|
||||
std::string filePath = "/" + name + ".bin";
|
||||
|
||||
File bin_file = SPIFFS.open(filePath.c_str(), "r");
|
||||
File bin_file = SD.open(filePath.c_str(), FILE_READ);
|
||||
if (!bin_file) {
|
||||
LOG_ERROR("Failed to Open File");
|
||||
LOG_ERROR("Failed to open file: %s", filePath.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
size_t fileSize = bin_file.size();
|
||||
size_t steps = fileSize / 2;
|
||||
melody_steps.resize(steps);
|
||||
|
||||
LOG_DEBUG("Opened File, size: %zu - Steps: %zu",fileSize,steps)
|
||||
|
||||
for (size_t i=0; i<steps; i++){
|
||||
melody_steps[i] = bin_file.read() << 8 | bin_file.read();
|
||||
LOG_DEBUG("Current Step: %03d // HEX Value: 0x%04X", i, melody_steps[i]);
|
||||
}
|
||||
|
||||
LOG_DEBUG("Closing File");
|
||||
if (fileSize % 2 != 0) {
|
||||
LOG_ERROR("Invalid file size: %u (not a multiple of 2)", fileSize);
|
||||
bin_file.close();
|
||||
|
||||
// closing the file
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class TimeKeeper {
|
||||
private:
|
||||
|
||||
// holds the daily scheduled melodies
|
||||
struct ScheduleEntry {
|
||||
std::string mel;
|
||||
uint8_t hour;
|
||||
uint8_t minute;
|
||||
uint16_t speed;
|
||||
uint16_t duration;
|
||||
};
|
||||
|
||||
struct TimeNow {
|
||||
uint8_t hour;
|
||||
uint8_t minute;
|
||||
uint8_t second;
|
||||
};
|
||||
|
||||
std::vector<ScheduleEntry> dailySchedule;
|
||||
TimeNow now;
|
||||
|
||||
// update to current time
|
||||
void updateTime(){
|
||||
now.hour = getHour();
|
||||
now.minute = getMinute();
|
||||
now.second = getSecond();
|
||||
}
|
||||
|
||||
// Read the current Month's JSON file and add the daily entries to the dailySchedule
|
||||
void loadDailySchedule() {
|
||||
std::string filePath = "/" + getMonth() + ".json";
|
||||
File file = SPIFFS.open(filePath.c_str(), "r");
|
||||
if (!file) {
|
||||
LOG_ERROR("Failed to open file: %s\n", filePath.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_INFO("Opened daily schedule file");
|
||||
StaticJsonDocument<8192> doc; // Adjust size based on expected JSON complexity
|
||||
DeserializationError error = deserializeJson(doc, file);
|
||||
file.close();
|
||||
melody_steps.resize(fileSize / 2);
|
||||
|
||||
if (error) {
|
||||
LOG_ERROR("Failed to parse JSON: %s\n", error.c_str());
|
||||
return;
|
||||
for (size_t i = 0; i < melody_steps.size(); i++) {
|
||||
uint8_t high = bin_file.read();
|
||||
uint8_t low = bin_file.read();
|
||||
melody_steps[i] = (high << 8) | low;
|
||||
}
|
||||
|
||||
if (LOG_LEVEL_ENABLED(LOG_LEVEL_DEBUG)){
|
||||
String jsonString;
|
||||
serializeJsonPretty(doc, jsonString);
|
||||
LOG_DEBUG("Serialized JSON: /n%s",jsonString.c_str());
|
||||
}
|
||||
LOG_INFO("Melody Load Successful");
|
||||
|
||||
// Extract entries for the current day
|
||||
std::string currentDay = getDay();
|
||||
dailySchedule.clear(); // Clear previous day's schedule
|
||||
|
||||
LOG_DEBUG("Current Day: %s\n", currentDay.c_str());
|
||||
|
||||
if (doc.containsKey(currentDay.c_str())) {
|
||||
LOG_DEBUG("Doc contains key!");
|
||||
|
||||
// Check if the current day contains a single object or an array
|
||||
JsonVariant dayVariant = doc[currentDay.c_str()];
|
||||
|
||||
if (dayVariant.is<JsonArray>()) {
|
||||
// Handle case where the day contains an array of entries
|
||||
JsonArray dayEntries = dayVariant.as<JsonArray>();
|
||||
for (JsonObject entry : dayEntries) {
|
||||
ScheduleEntry schedule;
|
||||
schedule.mel = entry["name"].as<std::string>();
|
||||
schedule.hour = entry["hour"];
|
||||
schedule.minute = entry["minute"];
|
||||
schedule.speed = entry["speed"];
|
||||
schedule.duration = entry["duration"];
|
||||
dailySchedule.push_back(schedule);
|
||||
|
||||
LOG_INFO("Added Entry - Melody: %s, Hour: %d, Minute: %d, Speed: %d, Duration: %d\n",
|
||||
schedule.mel.c_str(), schedule.hour, schedule.minute, schedule.speed, schedule.duration);
|
||||
}
|
||||
} else if (dayVariant.is<JsonObject>()) {
|
||||
// Handle case where the day contains a single object
|
||||
JsonObject entry = dayVariant.as<JsonObject>();
|
||||
ScheduleEntry schedule;
|
||||
schedule.mel = entry["name"].as<std::string>();
|
||||
schedule.hour = entry["hour"];
|
||||
schedule.minute = entry["minute"];
|
||||
schedule.speed = entry["speed"];
|
||||
schedule.duration = entry["duration"];
|
||||
dailySchedule.push_back(schedule);
|
||||
|
||||
LOG_INFO("Added Single Entry - Melody: %s, Hour: %d, Minute: %d, Speed: %d, Duration: %d\n",
|
||||
schedule.mel.c_str(), schedule.hour, schedule.minute, schedule.speed, schedule.duration);
|
||||
} else {
|
||||
LOG_WARNING("Invalid data format for the current day.");
|
||||
}
|
||||
} else {
|
||||
LOG_INFO("No schedule found for today.");
|
||||
}
|
||||
}
|
||||
|
||||
// Calls "loadDailySchedule" and returns true ONCE per new day (00:00)
|
||||
bool newDayCheck() {
|
||||
|
||||
// Static variable to store whether we've already detected the new day
|
||||
static bool alreadyTriggered = false;
|
||||
// Check if it's midnight
|
||||
if (now.hour == 0 && now.minute == 0) {
|
||||
if (!alreadyTriggered) {
|
||||
// First time at midnight, trigger the event
|
||||
alreadyTriggered = true;
|
||||
LOG_DEBUG("New day detected, returning true.");
|
||||
loadDailySchedule();
|
||||
return true;
|
||||
} else {
|
||||
// It's still midnight, but we've already triggered
|
||||
LOG_DEBUG("Already triggered for today, returning false.");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// Reset the trigger after midnight has passed
|
||||
alreadyTriggered = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
// get date and time data from the RTC module
|
||||
uint16_t getYear() { return rtc.now().year(); }
|
||||
std::string getMonth() { return monthName(rtc.now().month()); }
|
||||
std::string getDay() { return std::to_string(rtc.now().day()); }
|
||||
uint8_t getHour() { return rtc.now().hour(); }
|
||||
uint8_t getMinute() { return rtc.now().minute(); }
|
||||
uint8_t getSecond() { return rtc.now().second(); }
|
||||
|
||||
// turn months from decimal to char*
|
||||
std::string monthName(uint8_t monthNumber) {
|
||||
const char* monthNames[] = {
|
||||
"jan", "feb", "mar", "apr", "may", "jun",
|
||||
"jul", "aug", "sep", "oct", "nov", "dec"
|
||||
};
|
||||
return monthNumber >= 1 && monthNumber <= 12 ? monthNames[monthNumber - 1] : "unknown";
|
||||
}
|
||||
|
||||
// Loads and updates the daily schedule
|
||||
void refreshDailySchedule() { loadDailySchedule(); }
|
||||
|
||||
// Returns the daily schedule in "ScheduleEntry" format
|
||||
const std::vector<ScheduleEntry>& getDailySchedule() const { return dailySchedule; }
|
||||
|
||||
// Prints the time, NOW.
|
||||
void printTimeNow(){
|
||||
LOG_INFO("Current Time: %s %s, %u - %02u:%02u:%02u\n",
|
||||
getMonth().c_str(), // Month as a string
|
||||
getDay().c_str(), // Day as a string
|
||||
getYear(), // Year as an integer
|
||||
getHour(), // Hour (24-hour format)
|
||||
getMinute(), // Minute
|
||||
getSecond()); // Second
|
||||
}
|
||||
|
||||
void tick(){
|
||||
updateTime();
|
||||
newDayCheck();
|
||||
}
|
||||
|
||||
void checkAndRunSchedule(Player & player) {
|
||||
LOG_DEBUG("Running daily schedule check");
|
||||
for (auto it = dailySchedule.begin(); it != dailySchedule.end(); ) {
|
||||
if (now.hour == it->hour && now.minute == it->minute) {
|
||||
LOG_DEBUG("Entry Exists, Calling program.");
|
||||
StaticJsonDocument<200> jsonDoc;
|
||||
jsonDoc["name"] = it->mel.c_str();
|
||||
jsonDoc["speed"] = it->speed;
|
||||
jsonDoc["duration"] = it->duration;
|
||||
jsonDoc["loop_dur"] = 0;
|
||||
jsonDoc["internal"] = 0;
|
||||
//LOG_INFO("Entry Found. Name: %s Speed: %d Duration: %d",it->mel.c_str(), it->speed, it->duration);
|
||||
if (!player.isPlaying){
|
||||
player.setMelodyAttributes(jsonDoc);
|
||||
player.loadMelodyInRAM(melody_steps);
|
||||
player.play();
|
||||
it = dailySchedule.erase(it);
|
||||
}
|
||||
}
|
||||
else {
|
||||
LOG_DEBUG("Entry's time doesn't match. Skipping.");
|
||||
++it;
|
||||
}
|
||||
}
|
||||
bin_file.close();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
62
vesper/commands.json
Normal file
62
vesper/commands.json
Normal file
@@ -0,0 +1,62 @@
|
||||
ADD MELODY:
|
||||
|
||||
{
|
||||
"cmd": "add_melody",
|
||||
"contents": {
|
||||
"url": "URL",
|
||||
"filename": "NAME.EXT"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
SET RELAY TIMERS:
|
||||
|
||||
{
|
||||
"cmd": "set_relay_timers",
|
||||
"contents": {"b1":100, "b2":200}
|
||||
}
|
||||
|
||||
|
||||
START PLAYBACK:
|
||||
|
||||
{
|
||||
"cmd": "playback",
|
||||
"contents": {
|
||||
"action": "play"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
STOP PLAYBACK:
|
||||
|
||||
{
|
||||
"cmd": "playback",
|
||||
"contents": {
|
||||
"action": "stop"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
SET MELODY:
|
||||
|
||||
{
|
||||
"cmd": "set_melody",
|
||||
"contents": {
|
||||
"name": "esperinos",
|
||||
"id": "10",
|
||||
"duration": 5000,
|
||||
"infinite": true,
|
||||
"interval": 1000,
|
||||
"speed": 200,
|
||||
"loop_dur": 2000
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
LIST MELODIES
|
||||
|
||||
{
|
||||
"cmd": "list_melodies",
|
||||
}
|
||||
|
||||
|
||||
@@ -1,17 +1,48 @@
|
||||
const char* ssid = "SmartNet";
|
||||
const char* password = "smartpass";
|
||||
#pragma once
|
||||
|
||||
#define DEV_ID "PC201504180001"
|
||||
|
||||
// Network Config
|
||||
const char* ssid = "SmartNet"; //Not used with WiFi Manager
|
||||
const char* password = "smartpass"; //Not used with WiFi Manager
|
||||
const char* hostname = "ESP32_mqtt_test";
|
||||
|
||||
const IPAddress MQTT_HOST(10,98,20,10);
|
||||
const int MQTT_PORT = 1883;
|
||||
|
||||
#define MQTT_USER "esp32_vesper"
|
||||
#define MQTT_PASS "vesper"
|
||||
|
||||
IPAddress ip(10, 98, 30, 150);
|
||||
IPAddress gateway(10, 98, 30, 1);
|
||||
IPAddress subnet(255, 255, 255, 0);
|
||||
String ap_ssid = String("BellSystems - ") + DEV_ID;
|
||||
String ap_pass = "password";
|
||||
|
||||
#define DEV_ID "id-96638646"
|
||||
// Version Controll Settings
|
||||
const char* versionUrl = "http://10.98.20.10:85/version.txt";
|
||||
const char* firmwareUrl = "http://10.98.20.10:85/firmware.bin";
|
||||
const float currentVersion = 1.1;
|
||||
|
||||
// NTP Config
|
||||
const char* ntpServer = "pool.ntp.org";
|
||||
const long gmtOffset_sec = 7200;
|
||||
const int daylightOffset_sec = 3600;
|
||||
|
||||
// MQTT Config
|
||||
const IPAddress MQTT_HOST(10,98,20,10);
|
||||
const int MQTT_PORT = 1883;
|
||||
#define MQTT_USER "esp32_vesper"
|
||||
#define MQTT_PASS "vesper"
|
||||
|
||||
// Hardware Configuration
|
||||
#define PCF8574_ADDR 0x24
|
||||
|
||||
|
||||
// SPI W5500 ETHERNET SETUP
|
||||
#define USE_TWO_ETH_PORTS 0
|
||||
|
||||
#ifndef ETH_PHY_CS
|
||||
#define ETH_PHY_TYPE ETH_PHY_W5500
|
||||
#define ETH_PHY_ADDR 1
|
||||
#define ETH_PHY_CS 5
|
||||
#define ETH_PHY_IRQ -1
|
||||
#define ETH_PHY_RST -1
|
||||
#endif
|
||||
// SPI pins
|
||||
#define ETH_SPI_SCK 18
|
||||
#define ETH_SPI_MISO 19
|
||||
#define ETH_SPI_MOSI 23
|
||||
|
||||
@@ -7,10 +7,8 @@ void saveRelayTimings();
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
|
||||
|
||||
// Sets Incoming Relay Durations to RAM and then call funtion to save them to file.
|
||||
void updateRelayTimings(JsonDocument doc) {
|
||||
void updateRelayTimings(JsonVariant doc) {
|
||||
// Iterate through the relays in the JSON payload
|
||||
for (uint8_t i = 0; i < 16; i++) {
|
||||
String key = String("b") + (i + 1); // Generate "b1", "b2", ...
|
||||
@@ -25,16 +23,33 @@ void updateRelayTimings(JsonDocument doc) {
|
||||
LOG_INFO("Updated Relay Timings.")
|
||||
}
|
||||
|
||||
// Save file "fileName" with data: "data"
|
||||
void savefile(const char* fileName, const char* data) {
|
||||
File file = SPIFFS.open(fileName, "w");
|
||||
if (!file) {
|
||||
LOG_ERROR("Failed to open file!");
|
||||
// Save file "filename" with data: "data" to the "dirPath" directory of the SD card
|
||||
void saveFileToSD(const char* dirPath, const char* filename, const char* data) {
|
||||
// Initialize SD (if not already done)
|
||||
if (!SD.begin(SD_CS)) {
|
||||
LOG_ERROR("SD Card not initialized!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure directory exists
|
||||
if (!SD.exists(dirPath)) {
|
||||
SD.mkdir(dirPath);
|
||||
}
|
||||
|
||||
// Build full path
|
||||
String fullPath = String(dirPath);
|
||||
if (!fullPath.endsWith("/")) fullPath += "/";
|
||||
fullPath += filename;
|
||||
|
||||
File file = SD.open(fullPath.c_str(), FILE_WRITE);
|
||||
if (!file) {
|
||||
LOG_ERROR("Failed to open file: %s", fullPath.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
file.print(data);
|
||||
file.close();
|
||||
LOG_INFO("File %s saved successfully.\n", fileName);
|
||||
LOG_INFO("File %s saved successfully.\n", fullPath.c_str());
|
||||
}
|
||||
|
||||
// Saves Relay Durations from RAM, into a file
|
||||
@@ -54,41 +69,30 @@ void saveRelayTimings() {
|
||||
return;
|
||||
}
|
||||
|
||||
const char * fileName = "/settings/relayTimings.json";
|
||||
savefile(fileName, buffer);
|
||||
|
||||
// // Open the file for writing
|
||||
// File file = SPIFFS.open("/settings/relayTimings.json", "w");
|
||||
// if (!file) {
|
||||
// Serial.println("Failed to open file for writing");
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// // Serialize JSON to the file
|
||||
// if (serializeJson(doc, file) == 0) {
|
||||
// Serial.println("Failed to write JSON to file");
|
||||
// } else {
|
||||
// Serial.println("Relay timings saved successfully");
|
||||
// }
|
||||
//
|
||||
// file.close();
|
||||
const char * path = "/settings";
|
||||
const char * filename = "relayTimings.json";
|
||||
saveFileToSD(path, filename, buffer);
|
||||
}
|
||||
|
||||
// Loads Relay Durations from file into RAM (called during boot)
|
||||
void loadRelayTimings() {
|
||||
// Open the file for reading
|
||||
File file = SPIFFS.open("/settings/relayTimings.json", "r");
|
||||
if (!file) {
|
||||
LOG_ERROR("Settings file not found. Using default relay timings.");
|
||||
|
||||
if (!SD.begin(SD_CS)) {
|
||||
LOG_ERROR("SD Card not initialized. Using default relay timings.");
|
||||
return;
|
||||
}
|
||||
|
||||
File file = SD.open("/settings/relayTimings.json", FILE_READ);
|
||||
if (!file) {
|
||||
LOG_ERROR("Settings file not found on SD. Using default relay timings.");
|
||||
return;
|
||||
}
|
||||
// Parse the JSON file
|
||||
StaticJsonDocument<512> doc; // Adjust size if needed
|
||||
DeserializationError error = deserializeJson(doc, file);
|
||||
if (error) {
|
||||
LOG_ERROR("Failed to parse settings file. Using default relay timings.");
|
||||
file.close();
|
||||
if (error) {
|
||||
LOG_ERROR("Failed to parse settings from SD. Using default relay timings.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -101,28 +105,343 @@ void loadRelayTimings() {
|
||||
}
|
||||
}
|
||||
|
||||
file.close();
|
||||
}
|
||||
|
||||
//
|
||||
void updateSchedule(JsonDocument doc) {
|
||||
const char* fileName = doc["file"];
|
||||
// Function to sync time with NTP server and update RTC
|
||||
void syncTimeWithNTP() {
|
||||
// Connect to Wi-Fi if not already connected
|
||||
if (WiFi.status() != WL_CONNECTED) {
|
||||
LOG_DEBUG("Connecting to Wi-Fi...");
|
||||
WiFi.begin(ssid, password);
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
delay(500);
|
||||
LOG_DEBUG(".");
|
||||
}
|
||||
LOG_DEBUG("\nWi-Fi connected!");
|
||||
}
|
||||
|
||||
// Ensure fileName exists and is valid
|
||||
if (!fileName || strlen(fileName) == 0) {
|
||||
LOG_ERROR("Invalid JSON payload: Missing or invalid 'file' field");
|
||||
// Configure NTP
|
||||
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
|
||||
|
||||
// Sync time from NTP server
|
||||
LOG_DEBUG("Syncing time with NTP server...");
|
||||
struct tm timeInfo;
|
||||
if (!getLocalTime(&timeInfo)) {
|
||||
LOG_DEBUG("Failed to obtain time from NTP server!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Serialize the "data" object into a string
|
||||
String dataString;
|
||||
serializeJson(doc["data"], dataString); // Converts the "data" object to a JSON string
|
||||
// Update RTC with synchronized time
|
||||
rtc.adjust(DateTime(timeInfo.tm_year + 1900, timeInfo.tm_mon + 1, timeInfo.tm_mday,
|
||||
timeInfo.tm_hour, timeInfo.tm_min, timeInfo.tm_sec));
|
||||
|
||||
if (dataString.length() > 0) {
|
||||
savefile(fileName, dataString.c_str());
|
||||
LOG_INFO("File '%s' updated successfully.\n", fileName);
|
||||
// Log synchronized time
|
||||
LOG_INFO("Time synced with NTP server.");
|
||||
LOG_DEBUG("Synced time: %02d:%02d:%02d, %02d/%02d/%04d",
|
||||
timeInfo.tm_hour, timeInfo.tm_min, timeInfo.tm_sec,
|
||||
timeInfo.tm_mday, timeInfo.tm_mon + 1, timeInfo.tm_year + 1900);
|
||||
}
|
||||
|
||||
// Call this function with the Firebase URL and desired local filename
|
||||
bool downloadFileToSD(const String& url, const String& directory, const String& filename) {
|
||||
LOG_INFO("HTTP Starting download...");
|
||||
|
||||
HTTPClient http;
|
||||
http.begin(url);
|
||||
int httpCode = http.GET();
|
||||
|
||||
if (httpCode != HTTP_CODE_OK) {
|
||||
LOG_ERROR("(HTTP) GET failed, error: %s\n", http.errorToString(httpCode).c_str());
|
||||
http.end();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!SD.begin(SD_CS)) {
|
||||
LOG_ERROR("SD Card init failed!");
|
||||
http.end();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ensure the directory ends with '/'
|
||||
String dirPath = directory;
|
||||
if (!dirPath.endsWith("/")) dirPath += "/";
|
||||
|
||||
// Create directory if it doesn't exist
|
||||
SD.mkdir(dirPath.c_str());
|
||||
|
||||
String fullPath = dirPath + filename;
|
||||
|
||||
File file = SD.open(fullPath.c_str(), FILE_WRITE);
|
||||
if (!file) {
|
||||
LOG_ERROR("SD Failed to open file for writing: %s", fullPath.c_str());
|
||||
http.end();
|
||||
return false;
|
||||
}
|
||||
|
||||
WiFiClient* stream = http.getStreamPtr();
|
||||
uint8_t buffer[1024];
|
||||
int bytesRead;
|
||||
|
||||
while (http.connected() && (bytesRead = stream->readBytes(buffer, sizeof(buffer))) > 0) {
|
||||
file.write(buffer, bytesRead);
|
||||
}
|
||||
|
||||
file.close();
|
||||
http.end();
|
||||
LOG_INFO("HTTP Download complete, file saved to: %s", fullPath.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Returns the list of melodies (the filenames) currently inside the SD Card.
|
||||
String listFilesAsJson(const char* dirPath) {
|
||||
if (!SD.begin(SD_CS)) {
|
||||
LOG_ERROR("SD init failed");
|
||||
return "{}";
|
||||
}
|
||||
|
||||
File dir = SD.open(dirPath);
|
||||
if (!dir || !dir.isDirectory()) {
|
||||
LOG_ERROR("Directory not found");
|
||||
return "{}";
|
||||
}
|
||||
|
||||
DynamicJsonDocument doc(1024); // Adjust size if needed
|
||||
JsonArray fileList = doc.createNestedArray("files");
|
||||
|
||||
File file = dir.openNextFile();
|
||||
while (file) {
|
||||
if (!file.isDirectory()) {
|
||||
fileList.add(file.name());
|
||||
}
|
||||
file = dir.openNextFile();
|
||||
}
|
||||
|
||||
String json;
|
||||
serializeJson(doc, json);
|
||||
return json;
|
||||
}
|
||||
|
||||
// Prints the Steps of a Melody from a file using its filename
|
||||
void printMelodyFile(const String& filename) {
|
||||
if (!SD.begin(SD_CS)) {
|
||||
LOG_ERROR("SD Card not initialized.");
|
||||
return;
|
||||
}
|
||||
|
||||
File file = SD.open("/melodies/" + filename, FILE_READ);
|
||||
if (!file) {
|
||||
Serial.println("Failed to open Melody file for reading");
|
||||
return;
|
||||
}
|
||||
|
||||
Serial.printf("---- Contents of %s ----\n", filename.c_str());
|
||||
|
||||
uint16_t step;
|
||||
int index = 0;
|
||||
|
||||
while (file.available() >= 2) {
|
||||
uint8_t low = file.read();
|
||||
uint8_t high = file.read();
|
||||
step = (high << 8) | low;
|
||||
Serial.printf("Step %5d: 0x%04X (%d)\n", index++, step, step);
|
||||
}
|
||||
|
||||
file.close();
|
||||
Serial.println("---- End of File ----");
|
||||
}
|
||||
|
||||
// Prints the contents of a Text file using its filename
|
||||
void printFileAsText(const String& path, const String& filename) {
|
||||
if (!SD.begin(SD_CS)) {
|
||||
LOG_ERROR("SD Card not initialized.");
|
||||
return;
|
||||
}
|
||||
|
||||
String fullPath = path;
|
||||
if (!fullPath.endsWith("/")) fullPath += "/";
|
||||
fullPath += filename;
|
||||
|
||||
File file = SD.open(fullPath, FILE_READ);
|
||||
if (!file) {
|
||||
Serial.println("Failed to open file for reading");
|
||||
return;
|
||||
}
|
||||
|
||||
Serial.printf("---- Contents of %s ----\n", filename.c_str());
|
||||
|
||||
while (file.available()) {
|
||||
String line = file.readStringUntil('\n');
|
||||
Serial.println(line);
|
||||
}
|
||||
|
||||
file.close();
|
||||
Serial.println("---- End of File ----");
|
||||
}
|
||||
|
||||
// Downloads a new melody from HTTP
|
||||
void addMelody(JsonVariant doc) {
|
||||
|
||||
LOG_INFO("Trying Saving...");
|
||||
const char* url = doc["url"];
|
||||
const char* filename = doc["filename"];
|
||||
downloadFileToSD(url, "/melodies", filename);
|
||||
|
||||
|
||||
}
|
||||
|
||||
// Checks the onboard SD Card for new firmware
|
||||
void checkFirmwareUpdate() {
|
||||
if (!SD.begin(SD_CS)) {
|
||||
Serial.println("SD init failed");
|
||||
return;
|
||||
}
|
||||
|
||||
File updateBin = SD.open("/firmware/update.bin");
|
||||
if (!updateBin) {
|
||||
Serial.println("No update.bin found");
|
||||
return;
|
||||
}
|
||||
|
||||
size_t updateSize = updateBin.size();
|
||||
if (updateSize == 0) {
|
||||
Serial.println("Empty update file");
|
||||
updateBin.close();
|
||||
return;
|
||||
}
|
||||
|
||||
Serial.println("Starting firmware update...");
|
||||
|
||||
if (Update.begin(updateSize)) {
|
||||
size_t written = Update.writeStream(updateBin);
|
||||
if (written == updateSize) {
|
||||
Serial.println("Update written successfully");
|
||||
} else {
|
||||
LOG_DEBUG("Invalid JSON payload: Unable to serialize 'data'");
|
||||
Serial.printf("Written only %d/%d bytes\n", written, updateSize);
|
||||
}
|
||||
timekeeper.refreshDailySchedule();
|
||||
|
||||
if (Update.end()) {
|
||||
Serial.println("Update finished!");
|
||||
if (Update.isFinished()) {
|
||||
Serial.println("Update complete. Rebooting...");
|
||||
updateBin.close();
|
||||
SD.remove("/firmware/update.bin"); // optional cleanup
|
||||
ESP.restart();
|
||||
} else {
|
||||
Serial.println("Update not complete");
|
||||
}
|
||||
} else {
|
||||
Serial.printf("Update error: %s\n", Update.errorString());
|
||||
}
|
||||
} else {
|
||||
Serial.println("Not enough space to begin update");
|
||||
}
|
||||
|
||||
updateBin.close();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// UNSUSED FUNCTIONS.
|
||||
|
||||
// void startConfigPortal() {
|
||||
// WiFi.mode(WIFI_AP);
|
||||
// WiFi.softAP("Device_Config", "12345678");
|
||||
// Serial.println("AP mode started. Connect to 'Device_Config'.");
|
||||
|
||||
// // Serve the configuration page
|
||||
// server.on("/", HTTP_GET, []() {
|
||||
// server.send(200, "text/html", generateConfigPageHTML());
|
||||
// });
|
||||
|
||||
// // Handle form submission
|
||||
// server.on("/save", HTTP_POST, []() {
|
||||
// ssid = server.arg("ssid");
|
||||
// password = server.arg("password");
|
||||
// mqttHost.fromString(server.arg("mqttHost"));
|
||||
// mqttUser = server.arg("mqttUser");
|
||||
// mqttPassword = server.arg("mqttPassword");
|
||||
|
||||
// saveSettings(); // Save new settings to SPIFFS
|
||||
// server.send(200, "text/plain", "Settings saved! Rebooting...");
|
||||
// delay(1000);
|
||||
// ESP.restart();
|
||||
// });
|
||||
|
||||
// server.begin();
|
||||
// }
|
||||
|
||||
// // Save settings to SPIFFS
|
||||
// void saveSettings() {
|
||||
// StaticJsonDocument<512> doc;
|
||||
// doc["ssid"] = ssid;
|
||||
// doc["password"] = password;
|
||||
// doc["mqttHost"] = mqttHost.toString();
|
||||
// doc["mqttUser"] = mqttUser;
|
||||
// doc["mqttPassword"] = mqttPassword;
|
||||
|
||||
// File configFile = SPIFFS.open(CONFIG_FILE, "w");
|
||||
// if (!configFile) {
|
||||
// Serial.println("Failed to open config file for writing.");
|
||||
// return;
|
||||
// }
|
||||
// serializeJson(doc, configFile);
|
||||
// configFile.close();
|
||||
// Serial.println("Settings saved to SPIFFS.");
|
||||
// }
|
||||
|
||||
// // Load settings from SPIFFS
|
||||
// void loadSettings() {
|
||||
// if (!SPIFFS.exists(CONFIG_FILE)) {
|
||||
// Serial.println("Config file not found. Using defaults.");
|
||||
// return;
|
||||
// }
|
||||
|
||||
// File configFile = SPIFFS.open(CONFIG_FILE, "r");
|
||||
// if (!configFile) {
|
||||
// Serial.println("Failed to open config file.");
|
||||
// return;
|
||||
// }
|
||||
|
||||
// StaticJsonDocument<512> doc;
|
||||
// DeserializationError error = deserializeJson(doc, configFile);
|
||||
// if (error) {
|
||||
// Serial.println("Failed to parse config file.");
|
||||
// return;
|
||||
// }
|
||||
|
||||
// ssid = doc["ssid"].as<String>();
|
||||
// password = doc["password"].as<String>();
|
||||
// mqttHost.fromString(doc["mqttHost"].as<String>());
|
||||
// mqttUser = doc["mqttUser"].as<String>();
|
||||
// mqttPassword = doc["mqttPassword"].as<String>();
|
||||
|
||||
// configFile.close();
|
||||
// Serial.println("Settings loaded from SPIFFS.");
|
||||
// }
|
||||
|
||||
// // Generate HTML page for configuration
|
||||
// String generateConfigPageHTML() {
|
||||
// String page = R"rawliteral(
|
||||
// <!DOCTYPE html>
|
||||
// <html>
|
||||
// <body>
|
||||
// <h2>Device Configuration</h2>
|
||||
// <form action="/save" method="POST">
|
||||
// WiFi SSID: <input type="text" name="ssid" value=")rawliteral" +
|
||||
// ssid + R"rawliteral("><br>
|
||||
// WiFi Password: <input type="password" name="password" value=")rawliteral" +
|
||||
// password + R"rawliteral("><br>
|
||||
// MQTT Host: <input type="text" name="mqttHost" value=")rawliteral" +
|
||||
// mqttHost.toString() + R"rawliteral("><br>
|
||||
// MQTT Username: <input type="text" name="mqttUser" value=")rawliteral" +
|
||||
// mqttUser + R"rawliteral("><br>
|
||||
// MQTT Password: <input type="password" name="mqttPassword" value=")rawliteral" +
|
||||
// mqttPassword + R"rawliteral("><br>
|
||||
// <input type="submit" value="Save">
|
||||
// </form>
|
||||
// </body>
|
||||
// </html>
|
||||
// )rawliteral";
|
||||
// return page;
|
||||
// }
|
||||
|
||||
65
vesper/ota.hpp
Normal file
65
vesper/ota.hpp
Normal file
@@ -0,0 +1,65 @@
|
||||
void checkForUpdates();
|
||||
void performOTA();
|
||||
|
||||
void checkForUpdates() {
|
||||
Serial.println("Checking for firmware updates...");
|
||||
|
||||
// Step 1: Check the current version on the server
|
||||
HTTPClient http;
|
||||
http.begin(versionUrl);
|
||||
int httpCode = http.GET();
|
||||
|
||||
if (httpCode == HTTP_CODE_OK) {
|
||||
String newVersionStr = http.getString();
|
||||
float newVersion = newVersionStr.toFloat();
|
||||
|
||||
Serial.printf("Current version: %.1f, Available version: %.1f\n", currentVersion, newVersion);
|
||||
|
||||
// Step 2: Compare the version
|
||||
if (newVersion > currentVersion) {
|
||||
Serial.println("New version available. Starting update...");
|
||||
performOTA(); // Perform the OTA update if a new version is found
|
||||
} else {
|
||||
Serial.println("No new version available.");
|
||||
}
|
||||
} else {
|
||||
Serial.printf("Failed to retrieve version. HTTP error code: %d\n", httpCode);
|
||||
}
|
||||
http.end();
|
||||
}
|
||||
|
||||
void performOTA() {
|
||||
HTTPClient http;
|
||||
http.begin(firmwareUrl);
|
||||
int httpCode = http.GET();
|
||||
|
||||
if (httpCode == HTTP_CODE_OK) {
|
||||
int contentLength = http.getSize();
|
||||
if (contentLength > 0) {
|
||||
bool canBegin = Update.begin(contentLength);
|
||||
if (canBegin) {
|
||||
Serial.println("Starting OTA update...");
|
||||
WiFiClient *client = http.getStreamPtr();
|
||||
size_t written = Update.writeStream(*client);
|
||||
if (written == contentLength) {
|
||||
Serial.println("Update complete");
|
||||
if (Update.end()) {
|
||||
Serial.println("Update successfully finished. Rebooting...");
|
||||
ESP.restart(); // Reboot to apply the update
|
||||
} else {
|
||||
Serial.printf("Update failed: %s\n", Update.errorString());
|
||||
}
|
||||
} else {
|
||||
Serial.println("Update failed: Written size mismatch.");
|
||||
}
|
||||
} else {
|
||||
Serial.println("Not enough space for update.");
|
||||
}
|
||||
} else {
|
||||
Serial.println("Firmware file is empty.");
|
||||
}
|
||||
} else {
|
||||
Serial.printf("Firmware HTTP error code: %d\n", httpCode);
|
||||
}
|
||||
http.end();
|
||||
}
|
||||
1
vesper/temp.hpp
Normal file
1
vesper/temp.hpp
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
@@ -1,52 +1,87 @@
|
||||
/*
|
||||
|
||||
TODO List:
|
||||
Done Features:
|
||||
|
||||
- Add OTA Updates
|
||||
- Add reset to Factory Defaults
|
||||
- Initiate General Structure
|
||||
- Add WiFi Support
|
||||
- Add MQTT Support both for Subscribing and Publishing
|
||||
- Add JSON support to handle MQTT messaging
|
||||
- Add File Handling
|
||||
- Add Melody Class with functions to Play/Pause/Stop etc.
|
||||
- Add Main BellEngine
|
||||
- Add custom Relay Timings (saved on-board)
|
||||
- Add RTC support
|
||||
- Add Timekeeper class, with functions to track time and call schedules
|
||||
- Add OTA Update Functionality
|
||||
- Add global logger with Mode Selection (None, Error, Warning, Info, Debug)
|
||||
- Add Captive Portal / WiFi HotSpot
|
||||
- Add manual Sync-Time
|
||||
- Add NTP Time Sync
|
||||
|
||||
ToDo Features:
|
||||
|
||||
- Add reset to Factory Defaults button
|
||||
- Add manual Sync-Time (for No-Connectivity Setups)
|
||||
- Add the ability to report the list of melodies
|
||||
- Add the ability to report a month's ScheduleEntry
|
||||
- Add the ability to report the free space in SPIFFS.
|
||||
- Add global logger
|
||||
- Add
|
||||
- Add Bluetooth support
|
||||
- Add WiFi Direct AP Support
|
||||
- Add PCB Temperature Sensor
|
||||
- Counters and Statistics:
|
||||
- Counter for each bell (counts total times the bell ringed)
|
||||
- Counter per bell, beats/minute for reliability and thermal protection. Warranty Void scenario.
|
||||
- Counter per playback, to figure out which melody is the most played.
|
||||
- Counter of items per Scheduler
|
||||
|
||||
|
||||
*/
|
||||
|
||||
#include "logging.hpp"
|
||||
|
||||
#include <SD.h>
|
||||
#include <FS.h>
|
||||
#include <ETH.h>
|
||||
#include <SPI.h>
|
||||
#include <Arduino.h>
|
||||
#include <WiFi.h>
|
||||
#include <HTTPClient.h>
|
||||
#include <Update.h>
|
||||
#include <AsyncMqttClient.h>
|
||||
#include <ArduinoJson.h>
|
||||
#include <FS.h>
|
||||
#include <SPIFFS.h>
|
||||
#include <string>
|
||||
#include <Wire.h>
|
||||
#include <Adafruit_PCF8574.h>
|
||||
#include "RTClib.h"
|
||||
#include <WebServer.h>
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include <WiFiManager.h>
|
||||
|
||||
|
||||
// Hardware Constructors:
|
||||
Adafruit_PCF8574 relays;
|
||||
RTC_DS1307 rtc;
|
||||
|
||||
// SD Card Chip Select:
|
||||
#define SD_CS 5
|
||||
|
||||
// Include Classes
|
||||
#include "classes.hpp"
|
||||
|
||||
// Class Constructors
|
||||
AsyncMqttClient mqttClient;
|
||||
Player player;
|
||||
TimeKeeper timekeeper;
|
||||
std::vector<uint16_t> melody_steps; // holds the steps of the melody. Should move into bell Engine.
|
||||
AsyncWebServer server(80);
|
||||
|
||||
AsyncWebSocket ws("/ws");
|
||||
|
||||
#include "config.h"
|
||||
#include "ota.hpp"
|
||||
#include "functions.hpp"
|
||||
#include "MQTT_Message_Handling.hpp"
|
||||
#include "MQTT_WiFi_Utilities.hpp"
|
||||
#include "PlaybackControls.hpp"
|
||||
#include "bellEngine.hpp"
|
||||
#include "Scheduler.hpp"
|
||||
|
||||
TaskHandle_t bellEngineHandle = NULL;
|
||||
TimerHandle_t schedulerTimer;
|
||||
@@ -57,64 +92,69 @@ void setup()
|
||||
{
|
||||
// Initialize Serial Communications & I2C Bus (for debugging)
|
||||
Serial.begin(115200);
|
||||
Wire.begin(4,15);
|
||||
SPI.begin(ETH_SPI_SCK, ETH_SPI_MISO, ETH_SPI_MOSI);
|
||||
delay(50);
|
||||
|
||||
// Initialize PCF8574
|
||||
Wire.begin(4,15);
|
||||
// Initialize PCF8574 and Relays
|
||||
relays.begin(PCF8574_ADDR, &Wire);
|
||||
// Initialize Relays
|
||||
for (uint8_t p=0; p<6; p++){
|
||||
relays.pinMode(p, OUTPUT);
|
||||
relays.digitalWrite(p, HIGH);
|
||||
}
|
||||
// Initialize SPIFFS
|
||||
if (!SPIFFS.begin(true)) { // 'true' means format SPIFFS if initialization fails
|
||||
LOG_ERROR("Failed to mount SPIFFS");
|
||||
while(true) delay(10);
|
||||
|
||||
|
||||
// Initialize SD Card
|
||||
if (!SD.begin(SD_CS)) {
|
||||
Serial.println("SD card not found. Using defaults.");
|
||||
} else {
|
||||
// do nothing
|
||||
}
|
||||
LOG_INFO("SPIFFS mounted successfully");
|
||||
delay(50);
|
||||
|
||||
// Initialize RTC
|
||||
if (!rtc.begin()) {
|
||||
Serial.println("Couldn't find RTC");
|
||||
LOG_ERROR("Couldn't find RTC");
|
||||
while(true) delay(10);
|
||||
}
|
||||
|
||||
if (! rtc.isrunning()) {
|
||||
LOG_INFO("RTC is NOT running, let's set the time!");
|
||||
// When time needs to be set on a new device, or after a power loss, the
|
||||
// following line sets the RTC to the date & time this sketch was compiled
|
||||
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
|
||||
// This line sets the RTC with an explicit date & time, for example to set
|
||||
// January 21, 2014 at 3am you would call:
|
||||
// rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
|
||||
// Initialize Networking and MQTT
|
||||
Network.onEvent(NetworkEvent);
|
||||
ETH.begin(ETH_PHY_TYPE, ETH_PHY_ADDR, ETH_PHY_CS, ETH_PHY_IRQ, ETH_PHY_RST, SPI);
|
||||
InitMqtt();
|
||||
WiFiManager wm;
|
||||
//wm.resetSettings(); // Only for Debugging.
|
||||
bool res;
|
||||
res = wm.autoConnect(ap_ssid.c_str(),ap_pass.c_str());
|
||||
if(!res) {
|
||||
LOG_ERROR("Failed to connect to WiFi");
|
||||
}
|
||||
else {
|
||||
LOG_INFO("Connected to WiFi");
|
||||
}
|
||||
|
||||
delay(100);
|
||||
|
||||
// Initialize WiFi and MQTT
|
||||
WiFi.onEvent(WiFiEvent);
|
||||
InitMqtt();
|
||||
ConnectWiFi_STA();
|
||||
delay(1000);
|
||||
checkForUpdates(); // checks for updates online
|
||||
syncTimeWithNTP(); // syncs time from NTP Server
|
||||
|
||||
delay(100);
|
||||
|
||||
// WebSocket setup
|
||||
ws.onEvent(onWebSocketEvent);
|
||||
server.addHandler(&ws);
|
||||
// Start the server
|
||||
server.begin();
|
||||
|
||||
// Tasks and Timers
|
||||
xTaskCreatePinnedToCore(bellEngine,"bellEngine", 8192, NULL, 1, &bellEngineHandle, 1);
|
||||
xTaskCreatePinnedToCore(durationTimer, "durationTimer", 8192, NULL, 2, NULL, 1);
|
||||
xTaskCreatePinnedToCore(relayControlTask, "Relay Control", 2048, NULL, 2, NULL, 1);
|
||||
|
||||
schedulerTimer = xTimerCreate("Timer",pdMS_TO_TICKS(10000),pdTRUE,(void*)0,reinterpret_cast<TimerCallbackFunction_t>(schedule_timer));
|
||||
|
||||
if (schedulerTimer != NULL) {
|
||||
xTimerStart(schedulerTimer, 0);
|
||||
} else {
|
||||
LOG_ERROR("Failed to create timer!");
|
||||
}
|
||||
|
||||
timekeeper.refreshDailySchedule();
|
||||
loadRelayTimings();
|
||||
|
||||
}
|
||||
|
||||
|
||||
void loop()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user