Added basic WebSocket Functionality
This commit is contained in:
Vendored
-17
@@ -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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Vendored
-21
@@ -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
|
|
||||||
}
|
|
||||||
Vendored
-4
@@ -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() {
|
|
||||||
}
|
|
||||||
Vendored
-17
@@ -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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Vendored
-101
@@ -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;
|
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.
|
// Subscribes to certain topics on the MQTT Server.
|
||||||
void SuscribeMqtt() {
|
void SuscribeMqtt() {
|
||||||
|
|
||||||
String topicPlayback = String("vesper/") + DEV_ID + "/control/playback";
|
String command = String("vesper/") + DEV_ID + "/control";
|
||||||
String topicSetMelody = String("vesper/") + DEV_ID + "/control/setMelody";
|
uint16_t command_id = mqttClient.subscribe(command.c_str(), 2);
|
||||||
String topicAddMelody = String("vesper/") + DEV_ID + "/control/addMelody";
|
LOG_INFO("Subscribing to Command topic, QoS 2, packetId: %d", command_id);
|
||||||
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);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,42 +62,37 @@ void SuscribeMqtt() {
|
|||||||
// Could move logic out of this into a dedicated function.
|
// 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) {
|
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 command = String("vesper/") + DEV_ID + "/control";
|
||||||
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";
|
|
||||||
|
|
||||||
// Don't know what this is. Check it out later.
|
// Don't know what this is. Check it out later.
|
||||||
//String payloadContent = String(payload).substring(0, len);
|
//String payloadContent = String(payload).substring(0, len);
|
||||||
|
|
||||||
if (String(topic) == topicPlayback){
|
if (String(topic) == command){
|
||||||
player.command(payload);
|
JsonDocument json = handleJSON(payload);
|
||||||
|
handleCommand(json);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
else if (String(topic) == topicSetMelody) {
|
|
||||||
player.setMelodyAttributes(handleJSON(payload));
|
|
||||||
player.loadMelodyInRAM(melody_steps);
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (String(topic) == topicAddMelody) {
|
// Handles incoming WebSocket messages on subscribed topics.
|
||||||
// Handle adding melody
|
// Could move logic out of this into a dedicated function.
|
||||||
LOG_INFO("Adding melody...");
|
void onWebSocketReceived(AsyncWebSocketClient *client, void *arg, uint8_t *data, size_t len) {
|
||||||
// You can call a function here to handle adding the melody
|
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);
|
||||||
|
|
||||||
else if (String(topic) == topicAddSchedule) {
|
JsonDocument json = handleJSON((char*)data);
|
||||||
updateSchedule(handleJSON(payload));
|
handleCommand(json, client);
|
||||||
}
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
else if (String(topic) == topicRelayTimers) {
|
|
||||||
updateRelayTimings(handleJSON(payload));
|
|
||||||
}
|
|
||||||
|
|
||||||
else {
|
void onWebSocketEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len) {
|
||||||
// Handle unknown topics
|
if (type == WS_EVT_DATA) {
|
||||||
LOG_WARNING("Unknown topic received.");
|
onWebSocketReceived(client, arg, data, len);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Publishes a message on the MQTT server. Message passed as an argument.
|
// Publishes a message on the MQTT server. Message passed as an argument.
|
||||||
|
|||||||
@@ -12,21 +12,17 @@ String GetPayloadContent(char * data, size_t len) {
|
|||||||
return content;
|
return content;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConnectToMqtt()
|
void ConnectToMqtt() {
|
||||||
{
|
|
||||||
LOG_INFO("Connecting to MQTT...");
|
LOG_INFO("Connecting to MQTT...");
|
||||||
mqttClient.connect();
|
mqttClient.connect();
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnMqttConnect(bool sessionPresent)
|
void OnMqttConnect(bool sessionPresent) {
|
||||||
{
|
|
||||||
LOG_INFO("Connected to MQTT.");
|
LOG_INFO("Connected to MQTT.");
|
||||||
//LOG_INFO("Session present: %s", sessionPresent ? "Yes":"No");
|
|
||||||
SuscribeMqtt();
|
SuscribeMqtt();
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnMqttDisconnect(AsyncMqttClientDisconnectReason reason)
|
void OnMqttDisconnect(AsyncMqttClientDisconnectReason reason) {
|
||||||
{
|
|
||||||
LOG_WARNING("Disconnected from MQTT.");
|
LOG_WARNING("Disconnected from MQTT.");
|
||||||
|
|
||||||
if(WiFi.isConnected())
|
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);
|
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);
|
LOG_INFO("Unsubscribe Acknowledged. PacketID: %d",packetId);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnMqttPublish(uint16_t packetId)
|
void OnMqttPublish(uint16_t packetId) {
|
||||||
{
|
|
||||||
LOG_INFO("Publish Acknowledged. PacketID: %d", packetId);
|
LOG_INFO("Publish Acknowledged. PacketID: %d", packetId);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConnectWiFi_STA(bool useStaticIP = false)
|
void ConnectWiFi_STA(bool useStaticIP = true) {
|
||||||
{
|
|
||||||
Serial.println("");
|
|
||||||
WiFi.mode(WIFI_STA);
|
WiFi.mode(WIFI_STA);
|
||||||
if(useStaticIP) {
|
if(useStaticIP) {
|
||||||
WiFi.config(ip, gateway, subnet);
|
WiFi.config(ip, gateway, subnet);
|
||||||
WiFi.setHostname(hostname);
|
WiFi.setHostname(hostname);
|
||||||
}
|
}
|
||||||
WiFi.begin(ssid, password);
|
//WiFi.begin(ssid, password);
|
||||||
|
WiFi.begin();
|
||||||
|
|
||||||
while (WiFi.status() != WL_CONNECTED) {
|
while (WiFi.status() != WL_CONNECTED) {
|
||||||
delay(100);
|
delay(10);
|
||||||
Serial.print('.');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (LOG_LEVEL_ENABLED(LOG_LEVEL_INFO)){
|
if (LOG_LEVEL_ENABLED(LOG_LEVEL_INFO)){
|
||||||
Serial.println("");
|
Serial.println("");
|
||||||
Serial.print("Initiating STA:\t");
|
Serial.print("NIGGA - Initiating STA:\t");
|
||||||
Serial.println(ssid);
|
Serial.println(ssid);
|
||||||
Serial.print("IP address:\t");
|
Serial.print("IP address:\t");
|
||||||
Serial.println(WiFi.localIP());
|
Serial.println(WiFi.localIP());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConnectWiFi_AP(bool useStaticIP = false)
|
void ConnectWiFi_AP(bool useStaticIP = false) {
|
||||||
{
|
|
||||||
Serial.println("");
|
Serial.println("");
|
||||||
WiFi.mode(WIFI_AP);
|
WiFi.mode(WIFI_AP);
|
||||||
while(!WiFi.softAP(ssid, password))
|
while(!WiFi.softAP(ssid, password)) {
|
||||||
{
|
|
||||||
Serial.println(".");
|
Serial.println(".");
|
||||||
delay(100);
|
delay(100);
|
||||||
}
|
}
|
||||||
@@ -95,26 +84,53 @@ void ConnectWiFi_AP(bool useStaticIP = false)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WiFiEvent(WiFiEvent_t event)
|
void NetworkEvent(arduino_event_id_t event, arduino_event_info_t info) {
|
||||||
{
|
LOG_INFO("(NET) event: %d\n", event);
|
||||||
LOG_INFO("[WiFi-event] event: %d\n", event);
|
IPAddress ip = WiFi.localIP();
|
||||||
switch(event)
|
switch(event)
|
||||||
{
|
{
|
||||||
case ARDUINO_EVENT_WIFI_STA_GOT_IP:
|
case ARDUINO_EVENT_WIFI_STA_GOT_IP:
|
||||||
Serial.print("[INFO] - WiFi connected. IP Address: ");
|
LOG_DEBUG("WiFi connected. IP Address: %d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
|
||||||
Serial.println(WiFi.localIP());
|
//xTimerStop(wifiReconnectTimer, 0);
|
||||||
ConnectToMqtt();
|
ConnectToMqtt();
|
||||||
break;
|
break;
|
||||||
case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
|
case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
|
||||||
LOG_WARNING("WiFi Lost Connection! :()");
|
LOG_WARNING("WiFi Lost Connection! :(");
|
||||||
xTimerStop(mqttReconnectTimer, 0); // ensure we don't reconnect to MQTT while reconnecting to Wi-Fi
|
xTimerStop(mqttReconnectTimer, 0);
|
||||||
xTimerStart(wifiReconnectTimer, 0);
|
xTimerStart(wifiReconnectTimer, 0);
|
||||||
break;
|
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));
|
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));
|
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) {
|
void loop_playback(std::vector<uint16_t> &melody_steps) {
|
||||||
|
|
||||||
while(player.isPlaying && !player.isPaused){
|
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
|
// Function to activate relays for a specific note
|
||||||
void itsHammerTime(uint16_t note) {
|
void itsHammerTime(uint16_t note) {
|
||||||
uint64_t now = millis();
|
uint64_t now = millis();
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
+70
-263
@@ -13,7 +13,7 @@ public:
|
|||||||
uint32_t loop_duration = 0; // Duration of the playback per segment
|
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
|
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 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
|
bool isPaused = false; // If playing, indicates if the Melody is Paused
|
||||||
uint64_t startTime = 0; // The time-point the Melody started Playing
|
uint64_t startTime = 0; // The time-point the Melody started Playing
|
||||||
uint64_t loopStartTime = 0; // The time-point the current segment started Playing
|
uint64_t loopStartTime = 0; // The time-point the current segment started Playing
|
||||||
@@ -51,276 +51,83 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handles Incoming Commands to PLAY or STOP
|
// Handles Incoming Commands to PLAY or STOP
|
||||||
void command(char * command){
|
void command(JsonVariant content){
|
||||||
LOG_DEBUG("Incoming Command: %s",command);
|
String action = content["action"];
|
||||||
if (command[0] == '1') {
|
LOG_DEBUG("Incoming Command: %s", action);
|
||||||
|
if (action == "play") {
|
||||||
play();
|
play();
|
||||||
PublishMqtt("OK - PLAY");
|
PublishMqtt("OK - PLAY");
|
||||||
} else if (command[0] == '0') {
|
} else if (action == "stop") {
|
||||||
forceStop();
|
forceStop();
|
||||||
PublishMqtt("OK - STOP");
|
PublishMqtt("OK - STOP");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sets incoming Attributes for the Melody, into the class' variables.
|
// Sets incoming Attributes for the Melody, into the class' variables.
|
||||||
void setMelodyAttributes(JsonDocument doc){
|
void setMelodyAttributes(JsonVariant doc){
|
||||||
|
|
||||||
if (doc.containsKey("name")) {
|
if (doc.containsKey("name")) {
|
||||||
name = doc["name"].as<const char*>();
|
name = doc["name"].as<const char*>();
|
||||||
}
|
}
|
||||||
if (doc.containsKey("id")) {
|
if (doc.containsKey("id")) {
|
||||||
id = doc["id"].as<uint16_t>();
|
id = doc["id"].as<uint16_t>();
|
||||||
}
|
}
|
||||||
if (doc.containsKey("duration")) {
|
if (doc.containsKey("duration")) {
|
||||||
duration = doc["duration"].as<uint32_t>();
|
duration = doc["duration"].as<uint32_t>();
|
||||||
}
|
}
|
||||||
if (doc.containsKey("infinite")) {
|
if (doc.containsKey("infinite")) {
|
||||||
infinite_play = doc["infinite"].as<bool>();
|
infinite_play = doc["infinite"].as<bool>();
|
||||||
}
|
}
|
||||||
if (doc.containsKey("interval")) {
|
if (doc.containsKey("interval")) {
|
||||||
interval = doc["interval"].as<uint32_t>();
|
interval = doc["interval"].as<uint32_t>();
|
||||||
}
|
}
|
||||||
if (doc.containsKey("speed")) {
|
if (doc.containsKey("speed")) {
|
||||||
speed = doc["speed"].as<uint16_t>();
|
speed = doc["speed"].as<uint16_t>();
|
||||||
}
|
}
|
||||||
if (doc.containsKey("loop_dur")) {
|
if (doc.containsKey("loop_dur")) {
|
||||||
loop_duration = doc["loop_dur"].as<uint32_t>();
|
loop_duration = doc["loop_dur"].as<uint32_t>();
|
||||||
}
|
}
|
||||||
// Print Just for Debugging Purposes
|
// Print Just for Debugging Purposes
|
||||||
LOG_DEBUG("Name: %s, ID: %d, Total Duration: %lu, Loop Duration: %lu, Interval: %d, Speed: %d, Inf: %s\n",
|
LOG_DEBUG("Name: %s, ID: %d, Total Duration: %lu, Loop Duration: %lu, Interval: %d, Speed: %d, Inf: %s\n",
|
||||||
name.c_str(),
|
name.c_str(),
|
||||||
id,
|
id,
|
||||||
duration,
|
duration,
|
||||||
loop_duration,
|
loop_duration,
|
||||||
interval,
|
interval,
|
||||||
speed,
|
speed,
|
||||||
infinite_play ? "true" : "false"
|
infinite_play ? "true" : "false"
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 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";
|
||||||
|
|
||||||
|
File bin_file = SD.open(filePath.c_str(), FILE_READ);
|
||||||
|
if (!bin_file) {
|
||||||
|
LOG_ERROR("Failed to open file: %s", filePath.c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t fileSize = bin_file.size();
|
||||||
|
if (fileSize % 2 != 0) {
|
||||||
|
LOG_ERROR("Invalid file size: %u (not a multiple of 2)", fileSize);
|
||||||
|
bin_file.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
melody_steps.resize(fileSize / 2);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_INFO("Melody Load Successful");
|
||||||
|
|
||||||
|
bin_file.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loads the Selected melody from a .bin file, into RAM
|
|
||||||
void loadMelodyInRAM(std::vector<uint16_t> &melody_steps) {
|
|
||||||
|
|
||||||
LOG_INFO("Loading Melody.");
|
|
||||||
std::string filePath = "/" + name + ".bin";
|
|
||||||
|
|
||||||
File bin_file = SPIFFS.open(filePath.c_str(), "r");
|
|
||||||
if (!bin_file) {
|
|
||||||
LOG_ERROR("Failed to Open File");
|
|
||||||
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");
|
|
||||||
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();
|
|
||||||
|
|
||||||
if (error) {
|
|
||||||
LOG_ERROR("Failed to parse JSON: %s\n", error.c_str());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (LOG_LEVEL_ENABLED(LOG_LEVEL_DEBUG)){
|
|
||||||
String jsonString;
|
|
||||||
serializeJsonPretty(doc, jsonString);
|
|
||||||
LOG_DEBUG("Serialized JSON: /n%s",jsonString.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
+42
-11
@@ -1,17 +1,48 @@
|
|||||||
const char* ssid = "SmartNet";
|
#pragma once
|
||||||
const char* password = "smartpass";
|
|
||||||
|
#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 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 ip(10, 98, 30, 150);
|
||||||
IPAddress gateway(10, 98, 30, 1);
|
IPAddress gateway(10, 98, 30, 1);
|
||||||
IPAddress subnet(255, 255, 255, 0);
|
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;
|
||||||
|
|
||||||
#define PCF8574_ADDR 0x24
|
// 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
|
||||||
|
|||||||
+369
-50
@@ -7,10 +7,8 @@ void saveRelayTimings();
|
|||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Sets Incoming Relay Durations to RAM and then call funtion to save them to file.
|
// 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
|
// Iterate through the relays in the JSON payload
|
||||||
for (uint8_t i = 0; i < 16; i++) {
|
for (uint8_t i = 0; i < 16; i++) {
|
||||||
String key = String("b") + (i + 1); // Generate "b1", "b2", ...
|
String key = String("b") + (i + 1); // Generate "b1", "b2", ...
|
||||||
@@ -25,16 +23,33 @@ void updateRelayTimings(JsonDocument doc) {
|
|||||||
LOG_INFO("Updated Relay Timings.")
|
LOG_INFO("Updated Relay Timings.")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save file "fileName" with data: "data"
|
// Save file "filename" with data: "data" to the "dirPath" directory of the SD card
|
||||||
void savefile(const char* fileName, const char* data) {
|
void saveFileToSD(const char* dirPath, const char* filename, const char* data) {
|
||||||
File file = SPIFFS.open(fileName, "w");
|
// Initialize SD (if not already done)
|
||||||
if (!file) {
|
if (!SD.begin(SD_CS)) {
|
||||||
LOG_ERROR("Failed to open file!");
|
LOG_ERROR("SD Card not initialized!");
|
||||||
return;
|
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.print(data);
|
||||||
file.close();
|
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
|
// Saves Relay Durations from RAM, into a file
|
||||||
@@ -54,41 +69,30 @@ void saveRelayTimings() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char * fileName = "/settings/relayTimings.json";
|
const char * path = "/settings";
|
||||||
savefile(fileName, buffer);
|
const char * filename = "relayTimings.json";
|
||||||
|
saveFileToSD(path, 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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loads Relay Durations from file into RAM (called during boot)
|
// Loads Relay Durations from file into RAM (called during boot)
|
||||||
void loadRelayTimings() {
|
void loadRelayTimings() {
|
||||||
// Open the file for reading
|
|
||||||
File file = SPIFFS.open("/settings/relayTimings.json", "r");
|
if (!SD.begin(SD_CS)) {
|
||||||
if (!file) {
|
LOG_ERROR("SD Card not initialized. Using default relay timings.");
|
||||||
LOG_ERROR("Settings file not found. Using default relay timings.");
|
|
||||||
return;
|
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
|
// Parse the JSON file
|
||||||
StaticJsonDocument<512> doc; // Adjust size if needed
|
StaticJsonDocument<512> doc; // Adjust size if needed
|
||||||
DeserializationError error = deserializeJson(doc, file);
|
DeserializationError error = deserializeJson(doc, file);
|
||||||
|
file.close();
|
||||||
if (error) {
|
if (error) {
|
||||||
LOG_ERROR("Failed to parse settings file. Using default relay timings.");
|
LOG_ERROR("Failed to parse settings from SD. Using default relay timings.");
|
||||||
file.close();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,28 +105,343 @@ void loadRelayTimings() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
file.close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
// Function to sync time with NTP server and update RTC
|
||||||
void updateSchedule(JsonDocument doc) {
|
void syncTimeWithNTP() {
|
||||||
const char* fileName = doc["file"];
|
// Connect to Wi-Fi if not already connected
|
||||||
|
if (WiFi.status() != WL_CONNECTED) {
|
||||||
// Ensure fileName exists and is valid
|
LOG_DEBUG("Connecting to Wi-Fi...");
|
||||||
if (!fileName || strlen(fileName) == 0) {
|
WiFi.begin(ssid, password);
|
||||||
LOG_ERROR("Invalid JSON payload: Missing or invalid 'file' field");
|
while (WiFi.status() != WL_CONNECTED) {
|
||||||
return;
|
delay(500);
|
||||||
|
LOG_DEBUG(".");
|
||||||
}
|
}
|
||||||
|
LOG_DEBUG("\nWi-Fi connected!");
|
||||||
|
}
|
||||||
|
|
||||||
// Serialize the "data" object into a string
|
// Configure NTP
|
||||||
String dataString;
|
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
|
||||||
serializeJson(doc["data"], dataString); // Converts the "data" object to a JSON string
|
|
||||||
|
|
||||||
if (dataString.length() > 0) {
|
// Sync time from NTP server
|
||||||
savefile(fileName, dataString.c_str());
|
LOG_DEBUG("Syncing time with NTP server...");
|
||||||
LOG_INFO("File '%s' updated successfully.\n", fileName);
|
struct tm timeInfo;
|
||||||
|
if (!getLocalTime(&timeInfo)) {
|
||||||
|
LOG_DEBUG("Failed to obtain time from NTP server!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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));
|
||||||
|
|
||||||
|
// 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 {
|
} 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;
|
||||||
|
// }
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
+85
-45
@@ -1,52 +1,87 @@
|
|||||||
/*
|
/*
|
||||||
|
|
||||||
TODO List:
|
Done Features:
|
||||||
|
|
||||||
- Add OTA Updates
|
- Initiate General Structure
|
||||||
- Add reset to Factory Defaults
|
- 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 Captive Portal / WiFi HotSpot
|
||||||
- Add manual Sync-Time
|
|
||||||
- Add NTP Time Sync
|
- 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 the list of melodies
|
||||||
- Add the ability to report a month's ScheduleEntry
|
- Add the ability to report a month's ScheduleEntry
|
||||||
- Add the ability to report the free space in SPIFFS.
|
- Add the ability to report the free space in SPIFFS.
|
||||||
- Add global logger
|
- Add Bluetooth support
|
||||||
- Add
|
- 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 "logging.hpp"
|
||||||
|
|
||||||
|
#include <SD.h>
|
||||||
|
#include <FS.h>
|
||||||
|
#include <ETH.h>
|
||||||
|
#include <SPI.h>
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <WiFi.h>
|
#include <WiFi.h>
|
||||||
|
#include <HTTPClient.h>
|
||||||
|
#include <Update.h>
|
||||||
#include <AsyncMqttClient.h>
|
#include <AsyncMqttClient.h>
|
||||||
#include <ArduinoJson.h>
|
#include <ArduinoJson.h>
|
||||||
#include <FS.h>
|
|
||||||
#include <SPIFFS.h>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <Wire.h>
|
#include <Wire.h>
|
||||||
#include <Adafruit_PCF8574.h>
|
#include <Adafruit_PCF8574.h>
|
||||||
#include "RTClib.h"
|
#include "RTClib.h"
|
||||||
|
#include <WebServer.h>
|
||||||
|
#include <ESPAsyncWebServer.h>
|
||||||
|
#include <WiFiManager.h>
|
||||||
|
|
||||||
|
|
||||||
// Hardware Constructors:
|
// Hardware Constructors:
|
||||||
Adafruit_PCF8574 relays;
|
Adafruit_PCF8574 relays;
|
||||||
RTC_DS1307 rtc;
|
RTC_DS1307 rtc;
|
||||||
|
|
||||||
|
// SD Card Chip Select:
|
||||||
|
#define SD_CS 5
|
||||||
|
|
||||||
// Include Classes
|
// Include Classes
|
||||||
#include "classes.hpp"
|
#include "classes.hpp"
|
||||||
|
|
||||||
// Class Constructors
|
// Class Constructors
|
||||||
AsyncMqttClient mqttClient;
|
AsyncMqttClient mqttClient;
|
||||||
Player player;
|
Player player;
|
||||||
TimeKeeper timekeeper;
|
|
||||||
std::vector<uint16_t> melody_steps; // holds the steps of the melody. Should move into bell Engine.
|
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 "config.h"
|
||||||
|
#include "ota.hpp"
|
||||||
#include "functions.hpp"
|
#include "functions.hpp"
|
||||||
#include "MQTT_Message_Handling.hpp"
|
#include "MQTT_Message_Handling.hpp"
|
||||||
#include "MQTT_WiFi_Utilities.hpp"
|
#include "MQTT_WiFi_Utilities.hpp"
|
||||||
#include "PlaybackControls.hpp"
|
#include "PlaybackControls.hpp"
|
||||||
#include "bellEngine.hpp"
|
#include "bellEngine.hpp"
|
||||||
#include "Scheduler.hpp"
|
|
||||||
|
|
||||||
TaskHandle_t bellEngineHandle = NULL;
|
TaskHandle_t bellEngineHandle = NULL;
|
||||||
TimerHandle_t schedulerTimer;
|
TimerHandle_t schedulerTimer;
|
||||||
@@ -57,64 +92,69 @@ void setup()
|
|||||||
{
|
{
|
||||||
// Initialize Serial Communications & I2C Bus (for debugging)
|
// Initialize Serial Communications & I2C Bus (for debugging)
|
||||||
Serial.begin(115200);
|
Serial.begin(115200);
|
||||||
|
Wire.begin(4,15);
|
||||||
|
SPI.begin(ETH_SPI_SCK, ETH_SPI_MISO, ETH_SPI_MOSI);
|
||||||
delay(50);
|
delay(50);
|
||||||
|
|
||||||
// Initialize PCF8574
|
// Initialize PCF8574 and Relays
|
||||||
Wire.begin(4,15);
|
|
||||||
relays.begin(PCF8574_ADDR, &Wire);
|
relays.begin(PCF8574_ADDR, &Wire);
|
||||||
// Initialize Relays
|
|
||||||
for (uint8_t p=0; p<6; p++){
|
for (uint8_t p=0; p<6; p++){
|
||||||
relays.pinMode(p, OUTPUT);
|
relays.pinMode(p, OUTPUT);
|
||||||
relays.digitalWrite(p, HIGH);
|
relays.digitalWrite(p, HIGH);
|
||||||
}
|
}
|
||||||
// Initialize SPIFFS
|
|
||||||
if (!SPIFFS.begin(true)) { // 'true' means format SPIFFS if initialization fails
|
|
||||||
LOG_ERROR("Failed to mount SPIFFS");
|
// Initialize SD Card
|
||||||
while(true) delay(10);
|
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
|
// Initialize RTC
|
||||||
if (!rtc.begin()) {
|
if (!rtc.begin()) {
|
||||||
Serial.println("Couldn't find RTC");
|
LOG_ERROR("Couldn't find RTC");
|
||||||
while(true) delay(10);
|
while(true) delay(10);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! rtc.isrunning()) {
|
// Initialize Networking and MQTT
|
||||||
LOG_INFO("RTC is NOT running, let's set the time!");
|
Network.onEvent(NetworkEvent);
|
||||||
// When time needs to be set on a new device, or after a power loss, the
|
ETH.begin(ETH_PHY_TYPE, ETH_PHY_ADDR, ETH_PHY_CS, ETH_PHY_IRQ, ETH_PHY_RST, SPI);
|
||||||
// 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 WiFi and MQTT
|
|
||||||
WiFi.onEvent(WiFiEvent);
|
|
||||||
InitMqtt();
|
InitMqtt();
|
||||||
ConnectWiFi_STA();
|
WiFiManager wm;
|
||||||
delay(1000);
|
//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);
|
||||||
|
|
||||||
|
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(bellEngine,"bellEngine", 8192, NULL, 1, &bellEngineHandle, 1);
|
||||||
xTaskCreatePinnedToCore(durationTimer, "durationTimer", 8192, NULL, 2, NULL, 1);
|
xTaskCreatePinnedToCore(durationTimer, "durationTimer", 8192, NULL, 2, NULL, 1);
|
||||||
xTaskCreatePinnedToCore(relayControlTask, "Relay Control", 2048, 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();
|
loadRelayTimings();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void loop()
|
void loop()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user