Compare commits
5 Commits
DEPLOYED
...
a79c4f8ca7
| Author | SHA1 | Date | |
|---|---|---|---|
| a79c4f8ca7 | |||
|
|
bbf604e1a8 | ||
|
|
e37aa58398 | ||
|
|
5bff567863 | ||
|
|
80fda62344 |
63
README.md
63
README.md
@@ -1,3 +1,64 @@
|
|||||||
# ETcontroller_PRO
|
# ETcontroller_PRO
|
||||||
|
|
||||||
Nuova versione di ETcontroller hardware (lo scatolo) basata su scheda ESP 32 Waveshare [https://www.waveshare.com/wiki/ESP32-S3-ETH-8DI-8RO#Resources]
|
### Nuova versione di ETcontroller hardware (lo scatolo) basata su scheda ESP32-S3 Waveshare
|
||||||
|
[https://www.waveshare.com/wiki/ESP32-S3-ETH-8DI-8RO#Resources]
|
||||||
|
|
||||||
|
## Tool necessari:
|
||||||
|
|
||||||
|
### Plugin VScode
|
||||||
|
* VScode
|
||||||
|
* PlatformIO Plugin per VScode
|
||||||
|
* GitGraph
|
||||||
|
* C/C++ Extension Pack
|
||||||
|
|
||||||
|
### Toolchain e Librerie
|
||||||
|
Dipendenze e toolchain vengono installate direttamente da PlatformIO.
|
||||||
|
Il firmware e' basato sul framework _Arduino_ e piattaforma _esp-idf_ di Espressif.
|
||||||
|
La versione della piattaforma inclusa in PlatformIO e' deprecata, quindi e' necessario scaicarne una indipendente da GitHub. La versione corrente e' listata nel file `platformio.ini` nella root directory, se fosse necessario aggiornarla si trova a: [https://github.com/pioarduino/platform-espressif32/releases]
|
||||||
|
Vale lo stesso per le librerie dipendenti, se non fossero disponibili tramite PlatformIO si possono cercare su GitHub e scaricare manualmente.
|
||||||
|
|
||||||
|
### Documentazione aggiuntiva
|
||||||
|
Nella cartella `docs` sono presenti i datasheet di tutti i device collegati.
|
||||||
|
I driver sono spesso scritti a manina usando quei documenti come reference.
|
||||||
|
|
||||||
|
## Configurazione Hardware e Build
|
||||||
|
|
||||||
|
### Definizione della board
|
||||||
|
Prima di poter compilare e' necessario copiare il file di descrizione della board `esp32-s3-waveshare8.json` che si trova in questa root directory nella cartella delle board di PlatformIO.
|
||||||
|
* Per Windows `%USERPROFILE%\.platformio\platforms\espressif32\boards`
|
||||||
|
* Per Linux `$HOME/.platformio/platforms/espressif32/boards`
|
||||||
|
* Per MAC `dovrebbe essere come linux`
|
||||||
|
|
||||||
|
### Cofigurazioni di build
|
||||||
|
Le configurazioni disponibili sono:
|
||||||
|
* **Release**: `esp32-s3-waveshare8`
|
||||||
|
Build adatta per il deploy, log su seriale ma non e' possibile il debug con PlatformIO
|
||||||
|
* **Debug**: `esp32-s3-waveshare8-debug`
|
||||||
|
Build per il debug del codice in circuit, attenzione che quando il debugger e' attivo tutti i procesi temporizzati dallo scheduler vanno in pausa per cui le funzioni di rete e comunicazione con le perriferche potrebbero non funzionare correttamente.
|
||||||
|
|
||||||
|
Il cambio di configurazione tra Release e Debug causa un rebuild completo del codice.
|
||||||
|
|
||||||
|
### Partizioni della flash
|
||||||
|
La flash dell'ESP32 e' partizionata secondo lo schema definito in `fatfs_partitions.csv`, che deve rimanere nella root del progetto.
|
||||||
|
Le partizioni sono come segue"
|
||||||
|
* **NVS + OTADATA**: Non toccare assoutamente, contengono il bootloader, se si toccano queste addio programmazione via USB, ci vuole il tool apposta.
|
||||||
|
* **APP_0 + APP_1**: contengono entrambe il firmware, quando avviene un aggiornamento via OTA una partizione e' in stby e riceve il firmware aggiornato. Se l'aggiornamento va a buon fine il boot successivo avviene dalla partizione aggiornata e cosi' via per i successivi.
|
||||||
|
* **FFAT** e' una partizione accessibile dal firmware per essere usata come memoria permanente. Montata dalla classe FSMount. E' di circa 9MB e si comporta come un filesystem FAT32.
|
||||||
|
**Attenzione che e' la flash integrata nel micro, evitare letture e scritture troppo frequenti per non bruciarla**
|
||||||
|
|
||||||
|
### Metodi di upload
|
||||||
|
La porta di upload e' configurata con `upload_protocol` nel file `platformio.ini`.
|
||||||
|
I valori possibili sono:
|
||||||
|
* **_esptool_** per upload USB
|
||||||
|
* **_espota_** per upload via Rete.
|
||||||
|
In questo caso il valore di _upload_port_ deve essere l'indirizzo IP della scheda, che sia settato statico o da DHCP.
|
||||||
|
E' possibile ce si debba permettere a VScode di aggiungere una regola al firewall del PC per permettere il collegamento "unsafe" via UDP
|
||||||
|
|
||||||
|
Il metodo di defaut e' tramite la porta USB, che ha un nome diverso a seconda del sistema operativo host e della porta a cui viene collegata.
|
||||||
|
Se si vuole utilizzare il metoto OTA via rete, questo va abilitato dalla scheda (per motivi di sicurezza).
|
||||||
|
Per abilitare OTA resettare la scheda e nel momento del boot tenere premuto il pulsante blu fino a che il buzzer smette di bippare e il led inizia a lampeggiare verde e giallo alternati: da quel momento e' possibile aggiornare via rete.
|
||||||
|
_Ogni aggiornamento causa il reboot della scheda._
|
||||||
|
|
||||||
|
### Logica del Firmware, come funziona?
|
||||||
|
|
||||||
|
[TODO]
|
||||||
42
include/pinlist.h
Normal file
42
include/pinlist.h
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
enum RO // relay output channels
|
||||||
|
{
|
||||||
|
P1 = 0,
|
||||||
|
P2 = 1,
|
||||||
|
P3 = 2,
|
||||||
|
P4 = 3,
|
||||||
|
RO_4 = 4,
|
||||||
|
FST_FLOOR = 5,
|
||||||
|
GND_FLOOR = 6,
|
||||||
|
PUMP_HT = 7,
|
||||||
|
PUMP_IRR = 8,
|
||||||
|
ZONE1 = 9,
|
||||||
|
ZONE2 = 10,
|
||||||
|
ZONE3 = 11,
|
||||||
|
DRIP = 12,
|
||||||
|
RETURN = 13,
|
||||||
|
RO_14 = 14,
|
||||||
|
RO_15 = 15,
|
||||||
|
RO_MAX = 16 // unused to detect invalid values
|
||||||
|
};
|
||||||
|
|
||||||
|
enum DI // digital input channels
|
||||||
|
{
|
||||||
|
CONFRESET = 0,
|
||||||
|
RESTART = 1,
|
||||||
|
DI_2 = 2,
|
||||||
|
DI_3 = 3,
|
||||||
|
DI_4 = 4,
|
||||||
|
DI_6 = 6,
|
||||||
|
OTAENABLE = 7,
|
||||||
|
PUMP_PRESSURE = 8,
|
||||||
|
RAIN = 9,
|
||||||
|
IRR_OVERRIDE = 10,
|
||||||
|
DI_11 = 11,
|
||||||
|
DI_12 = 12,
|
||||||
|
DI_13 = 13,
|
||||||
|
DI_14 = 14,
|
||||||
|
DI_15 = 15,
|
||||||
|
DI_MAX = 16
|
||||||
|
}; // unused to detect invalid values
|
||||||
@@ -12,6 +12,7 @@ namespace drivers
|
|||||||
pinMode(c_ledPin, OUTPUT);
|
pinMode(c_ledPin, OUTPUT);
|
||||||
m_blinkTask = NULL;
|
m_blinkTask = NULL;
|
||||||
m_flashTimer = NULL;
|
m_flashTimer = NULL;
|
||||||
|
m_enforce = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Led::~Led()
|
Led::~Led()
|
||||||
@@ -20,9 +21,16 @@ namespace drivers
|
|||||||
pinMode(c_ledPin, INPUT);
|
pinMode(c_ledPin, INPUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Led::setEnforce(const bool enf)
|
||||||
|
{
|
||||||
|
m_enforce = enf;
|
||||||
|
}
|
||||||
|
|
||||||
void Led::setColor(const color_t color)
|
void Led::setColor(const color_t color)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(m_ledMutex);
|
std::lock_guard<std::mutex> lock(m_ledMutex);
|
||||||
|
if (m_enforce)
|
||||||
|
return;
|
||||||
blinkStop();
|
blinkStop();
|
||||||
m_colorDefault = color;
|
m_colorDefault = color;
|
||||||
rgbLedWrite(c_ledPin, color.g, color.r, color.b);
|
rgbLedWrite(c_ledPin, color.g, color.r, color.b);
|
||||||
@@ -58,6 +66,8 @@ namespace drivers
|
|||||||
void Led::blinkColor(const uint16_t tOn, const uint16_t tOff, const color_t color)
|
void Led::blinkColor(const uint16_t tOn, const uint16_t tOff, const color_t color)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(m_ledMutex);
|
std::lock_guard<std::mutex> lock(m_ledMutex);
|
||||||
|
if (m_enforce)
|
||||||
|
return;
|
||||||
blinkStop();
|
blinkStop();
|
||||||
m_color1 = color;
|
m_color1 = color;
|
||||||
m_color2 = {0, 0, 0};
|
m_color2 = {0, 0, 0};
|
||||||
@@ -69,6 +79,8 @@ namespace drivers
|
|||||||
void Led::blinkAlternate(const uint16_t tOn, const uint16_t tOff, const color_t color1, const color_t color2)
|
void Led::blinkAlternate(const uint16_t tOn, const uint16_t tOff, const color_t color1, const color_t color2)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(m_ledMutex);
|
std::lock_guard<std::mutex> lock(m_ledMutex);
|
||||||
|
if (m_enforce)
|
||||||
|
return;
|
||||||
blinkStop();
|
blinkStop();
|
||||||
m_color1 = color1;
|
m_color1 = color1;
|
||||||
m_color2 = color2;
|
m_color2 = color2;
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ namespace drivers
|
|||||||
uint8_t b;
|
uint8_t b;
|
||||||
} color_t;
|
} color_t;
|
||||||
|
|
||||||
|
const color_t COLOR_OFF = {0, 0, 0};
|
||||||
const color_t COLOR_RED = {255, 0, 0};
|
const color_t COLOR_RED = {255, 0, 0};
|
||||||
const color_t COLOR_ORANGE = {255, 127, 0};
|
const color_t COLOR_ORANGE = {255, 127, 0};
|
||||||
const color_t COLOR_YELLOW = {255, 255, 0};
|
const color_t COLOR_YELLOW = {255, 255, 0};
|
||||||
@@ -36,6 +37,7 @@ namespace drivers
|
|||||||
Led();
|
Led();
|
||||||
~Led();
|
~Led();
|
||||||
|
|
||||||
|
void setEnforce(const bool enf);
|
||||||
void setColor(const color_t color);
|
void setColor(const color_t color);
|
||||||
void flashColor(const uint16_t tOn, const color_t color);
|
void flashColor(const uint16_t tOn, const color_t color);
|
||||||
void blinkColor(const uint16_t tOn, const uint16_t tOff, const color_t color);
|
void blinkColor(const uint16_t tOn, const uint16_t tOff, const color_t color);
|
||||||
@@ -60,6 +62,7 @@ namespace drivers
|
|||||||
TimerHandle_t m_flashTimer;
|
TimerHandle_t m_flashTimer;
|
||||||
|
|
||||||
bool m_flashing;
|
bool m_flashing;
|
||||||
|
bool m_enforce;
|
||||||
|
|
||||||
std::mutex m_ledMutex;
|
std::mutex m_ledMutex;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -23,17 +23,19 @@ build_type = release
|
|||||||
board_build.filesystem = ffat
|
board_build.filesystem = ffat
|
||||||
board_build.partitions = fatfs_partition.csv ; se stai usando uno custom
|
board_build.partitions = fatfs_partition.csv ; se stai usando uno custom
|
||||||
|
|
||||||
|
upload_protocol = espota
|
||||||
|
upload_port = 10.0.2.139
|
||||||
|
|
||||||
|
|
||||||
[env:esp32-s3-waveshare8-debug]
|
[env:esp32-s3-waveshare8-debug]
|
||||||
platform = ${env:esp32-s3-waveshare8.platform}
|
platform = ${env:esp32-s3-waveshare8.platform}
|
||||||
board = ${env:esp32-s3-waveshare8.board}
|
board = ${env:esp32-s3-waveshare8.board}
|
||||||
framework = ${env:esp32-s3-waveshare8.framework}
|
framework = ${env:esp32-s3-waveshare8.framework}
|
||||||
lib_deps =
|
lib_deps = ${env:esp32-s3-waveshare8.lib_deps}
|
||||||
bblanchon/ArduinoJson@^7.4.2
|
|
||||||
arduino-libraries/NTPClient@^3.2.1
|
board_build.filesystem = ffat
|
||||||
knolleary/PubSubClient@^2.8
|
board_build.partitions = fatfs_partition.csv ; se stai usando uno custom
|
||||||
robtillaart/CRC@^1.0.3
|
|
||||||
hideakitai/DebugLog@^0.8.4
|
|
||||||
build_type = debug
|
build_type = debug
|
||||||
build_flags =
|
build_flags =
|
||||||
-O0
|
-O0
|
||||||
@@ -44,5 +46,3 @@ build_flags =
|
|||||||
-fno-tree-sra
|
-fno-tree-sra
|
||||||
-fno-builtin
|
-fno-builtin
|
||||||
|
|
||||||
board_build.filesystem = ffat
|
|
||||||
board_build.partitions = fatfs_partition.csv ; se stai usando uno custom
|
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ namespace commands
|
|||||||
{
|
{
|
||||||
esp_restart();
|
esp_restart();
|
||||||
}
|
}
|
||||||
|
|
||||||
const ArduinoJson::JsonDocument Commands::setBuzz(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms)
|
const ArduinoJson::JsonDocument Commands::setBuzz(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms)
|
||||||
{
|
{
|
||||||
ArduinoJson::JsonDocument response;
|
ArduinoJson::JsonDocument response;
|
||||||
@@ -72,7 +71,6 @@ namespace commands
|
|||||||
response["values"]["status"] = "valid";
|
response["values"]["status"] = "valid";
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ArduinoJson::JsonDocument Commands::setCronJob(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms)
|
const ArduinoJson::JsonDocument Commands::setCronJob(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms)
|
||||||
{
|
{
|
||||||
ArduinoJson::JsonDocument response;
|
ArduinoJson::JsonDocument response;
|
||||||
@@ -168,7 +166,6 @@ namespace commands
|
|||||||
response["values"]["status"] = "valid";
|
response["values"]["status"] = "valid";
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ArduinoJson::JsonDocument Commands::storeCronJob(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms)
|
const ArduinoJson::JsonDocument Commands::storeCronJob(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms)
|
||||||
{
|
{
|
||||||
ArduinoJson::JsonDocument response;
|
ArduinoJson::JsonDocument response;
|
||||||
@@ -216,7 +213,6 @@ namespace commands
|
|||||||
response["values"]["status"] = "valid";
|
response["values"]["status"] = "valid";
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ArduinoJson::JsonDocument Commands::setHeating(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms)
|
const ArduinoJson::JsonDocument Commands::setHeating(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms)
|
||||||
{
|
{
|
||||||
ArduinoJson::JsonDocument response;
|
ArduinoJson::JsonDocument response;
|
||||||
@@ -250,7 +246,6 @@ namespace commands
|
|||||||
}
|
}
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
void resetZone(TimerHandle_t th)
|
void resetZone(TimerHandle_t th)
|
||||||
{
|
{
|
||||||
devices_t *dev = (devices_t *)pvTimerGetTimerID(th);
|
devices_t *dev = (devices_t *)pvTimerGetTimerID(th);
|
||||||
@@ -265,16 +260,14 @@ namespace commands
|
|||||||
c_irrigationTimerMap.at(timerName).second = NULL; // reset timer handle for this timer
|
c_irrigationTimerMap.at(timerName).second = NULL; // reset timer handle for this timer
|
||||||
xTimerDelete(th, 0); // delete the timer on expiry
|
xTimerDelete(th, 0); // delete the timer on expiry
|
||||||
}
|
}
|
||||||
|
|
||||||
void resetWaterPump(TimerHandle_t th)
|
void resetWaterPump(TimerHandle_t th)
|
||||||
{
|
{
|
||||||
devices_t *dev = (devices_t *)pvTimerGetTimerID(th);
|
devices_t *dev = (devices_t *)pvTimerGetTimerID(th);
|
||||||
LOG_INFO("setIrrigation shutdown pump");
|
LOG_INFO("setIrrigation shutdown pump");
|
||||||
dev->io.digitalOutWrite(RO::IRR_PUMP, false);
|
dev->io.digitalOutWrite(RO::PUMP_IRR, false);
|
||||||
s_irrigationPumpTimer = NULL;
|
s_irrigationPumpTimer = NULL;
|
||||||
xTimerDelete(th, 0); // delete the timer on expiry
|
xTimerDelete(th, 0); // delete the timer on expiry
|
||||||
}
|
}
|
||||||
|
|
||||||
const ArduinoJson::JsonDocument Commands::setIrrigation(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms)
|
const ArduinoJson::JsonDocument Commands::setIrrigation(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms)
|
||||||
{
|
{
|
||||||
ArduinoJson::JsonDocument response;
|
ArduinoJson::JsonDocument response;
|
||||||
@@ -347,7 +340,7 @@ namespace commands
|
|||||||
if (!s_irrigationPumpTimer) // Pump has not yet started
|
if (!s_irrigationPumpTimer) // Pump has not yet started
|
||||||
{
|
{
|
||||||
s_irrigationPumpTimer = xTimerCreate("pumpTimer", pdMS_TO_TICKS(pumpTime), false, (void *)&dev, resetWaterPump);
|
s_irrigationPumpTimer = xTimerCreate("pumpTimer", pdMS_TO_TICKS(pumpTime), false, (void *)&dev, resetWaterPump);
|
||||||
dev.io.digitalOutWrite(RO::IRR_PUMP, true);
|
dev.io.digitalOutWrite(RO::PUMP_IRR, true);
|
||||||
xTimerStart(s_irrigationPumpTimer, 0); // immediate start pump timer
|
xTimerStart(s_irrigationPumpTimer, 0); // immediate start pump timer
|
||||||
LOG_INFO("setIrrigation pump time", pumpTime);
|
LOG_INFO("setIrrigation pump time", pumpTime);
|
||||||
}
|
}
|
||||||
@@ -372,7 +365,6 @@ namespace commands
|
|||||||
}
|
}
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ArduinoJson::JsonDocument Commands::setTimeNTP(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms)
|
const ArduinoJson::JsonDocument Commands::setTimeNTP(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms)
|
||||||
{
|
{
|
||||||
ArduinoJson::JsonDocument response;
|
ArduinoJson::JsonDocument response;
|
||||||
@@ -415,56 +407,74 @@ namespace commands
|
|||||||
LOG_INFO("getHPpower -> power", pinfo.pAct, "current", pinfo.a, "voltage", pinfo.v, "energy", pinfo.whPar);
|
LOG_INFO("getHPpower -> power", pinfo.pAct, "current", pinfo.a, "voltage", pinfo.v, "energy", pinfo.whPar);
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ArduinoJson::JsonDocument Commands::getInputStatus(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms)
|
const ArduinoJson::JsonDocument Commands::getInputStatus(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms)
|
||||||
{
|
{
|
||||||
ArduinoJson::JsonDocument response;
|
ArduinoJson::JsonDocument response;
|
||||||
LOG_WARN("Comand not yet implemented");
|
response["cmd"] = "getInputStatus";
|
||||||
|
const std::vector<bool> inStatus(dev.io.digitalInReadPort());
|
||||||
|
if (inStatus.empty() || inStatus.size() != dev.io.getInNum())
|
||||||
|
{
|
||||||
|
response["values"] = "invalid";
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
uint8_t i(0);
|
||||||
|
for (auto s : inStatus)
|
||||||
|
{
|
||||||
|
const std::string k("DI" + std::to_string(i));
|
||||||
|
response["values"][k.c_str()] = s;
|
||||||
|
}
|
||||||
|
LOG_INFO("getInputStatus ->", printBoolVec(inStatus).c_str());
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ArduinoJson::JsonDocument Commands::getOutputStatus(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms)
|
const ArduinoJson::JsonDocument Commands::getOutputStatus(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms)
|
||||||
{
|
{
|
||||||
ArduinoJson::JsonDocument response;
|
ArduinoJson::JsonDocument response;
|
||||||
LOG_WARN("Comand not yet implemented");
|
response["cmd"] = "getOutputStatus";
|
||||||
|
const std::vector<bool> inStatus(dev.io.digitalOutReadPort());
|
||||||
|
if (inStatus.empty() || inStatus.size() != dev.io.getOutNum())
|
||||||
|
{
|
||||||
|
response["values"] = "invalid";
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
uint8_t i(0);
|
||||||
|
for (auto s : inStatus)
|
||||||
|
{
|
||||||
|
const std::string k("DO" + std::to_string(i));
|
||||||
|
response["values"][k.c_str()] = s;
|
||||||
|
}
|
||||||
|
LOG_INFO("getOutputStatus ->", printBoolVec(inStatus).c_str());
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ArduinoJson::JsonDocument Commands::getTemperatures(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms)
|
const ArduinoJson::JsonDocument Commands::getTemperatures(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms)
|
||||||
{
|
{
|
||||||
ArduinoJson::JsonDocument response;
|
ArduinoJson::JsonDocument response;
|
||||||
LOG_WARN("Comand not yet implemented");
|
LOG_WARN("Comand not yet implemented");
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ArduinoJson::JsonDocument Commands::getWaterInfo(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms)
|
const ArduinoJson::JsonDocument Commands::getWaterInfo(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms)
|
||||||
{
|
{
|
||||||
ArduinoJson::JsonDocument response;
|
ArduinoJson::JsonDocument response;
|
||||||
LOG_WARN("Comand not yet implemented");
|
LOG_WARN("Comand not yet implemented");
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ArduinoJson::JsonDocument Commands::getTankInfo(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms)
|
const ArduinoJson::JsonDocument Commands::getTankInfo(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms)
|
||||||
{
|
{
|
||||||
ArduinoJson::JsonDocument response;
|
ArduinoJson::JsonDocument response;
|
||||||
LOG_WARN("Comand not yet implemented");
|
LOG_WARN("Comand not yet implemented");
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ArduinoJson::JsonDocument Commands::getRainInfo(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms)
|
const ArduinoJson::JsonDocument Commands::getRainInfo(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms)
|
||||||
{
|
{
|
||||||
ArduinoJson::JsonDocument response;
|
ArduinoJson::JsonDocument response;
|
||||||
LOG_WARN("Comand not yet implemented");
|
LOG_WARN("Comand not yet implemented");
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ArduinoJson::JsonDocument Commands::getIrrigation(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms)
|
const ArduinoJson::JsonDocument Commands::getIrrigation(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms)
|
||||||
{
|
{
|
||||||
ArduinoJson::JsonDocument response;
|
ArduinoJson::JsonDocument response;
|
||||||
LOG_WARN("Comand not yet implemented");
|
LOG_WARN("Comand not yet implemented");
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ArduinoJson::JsonDocument Commands::getTimeDrift(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms)
|
const ArduinoJson::JsonDocument Commands::getTimeDrift(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms)
|
||||||
{
|
{
|
||||||
ArduinoJson::JsonDocument response;
|
ArduinoJson::JsonDocument response;
|
||||||
|
|||||||
@@ -8,45 +8,25 @@
|
|||||||
|
|
||||||
#include <config.h>
|
#include <config.h>
|
||||||
#include <devices.h>
|
#include <devices.h>
|
||||||
|
#include <pinlist.h>
|
||||||
|
|
||||||
namespace commands
|
namespace commands
|
||||||
{
|
{
|
||||||
enum RO // relay output channels
|
|
||||||
{
|
|
||||||
P1,
|
|
||||||
P2,
|
|
||||||
P3,
|
|
||||||
P4,
|
|
||||||
NC_1,
|
|
||||||
FST_FLOOR,
|
|
||||||
GND_FLOOR,
|
|
||||||
PUMP_HT,
|
|
||||||
IRR_PUMP,
|
|
||||||
Z1,
|
|
||||||
Z2,
|
|
||||||
Z3,
|
|
||||||
AUX,
|
|
||||||
RETURN,
|
|
||||||
NC_3,
|
|
||||||
NC_4,
|
|
||||||
RO_MAX // unused to detect invalid values
|
|
||||||
};
|
|
||||||
|
|
||||||
static const std::map<const std::string, uint8_t> c_hpLimitsMap = {{"P1", RO::P1},
|
static const std::map<const std::string, uint8_t> c_hpLimitsMap = {{"P1", RO::P1},
|
||||||
{"P2", RO::P2},
|
{"P2", RO::P2},
|
||||||
{"P3", RO::P3},
|
{"P3", RO::P3},
|
||||||
{"P4", RO::P4},
|
{"P4", RO::P4},
|
||||||
{"UNLIMITED", RO::P1}};
|
{"UNLIMITED", RO::RO_MAX}};
|
||||||
|
|
||||||
static const std::map<const std::string, uint8_t> c_heatingValveMap = {{"pump", RO::PUMP_HT},
|
static const std::map<const std::string, uint8_t> c_heatingValveMap = {{"pump", RO::PUMP_HT},
|
||||||
{"first", RO::FST_FLOOR},
|
{"first", RO::FST_FLOOR},
|
||||||
{"ground", RO::GND_FLOOR}};
|
{"ground", RO::GND_FLOOR}};
|
||||||
|
|
||||||
static const std::map<const std::string, uint8_t> c_irrigationValveMap = {{"ricircolo", RO::RETURN},
|
static const std::map<const std::string, uint8_t> c_irrigationValveMap = {{"ricircolo", RO::RETURN},
|
||||||
{"zone1", RO::Z1},
|
{"zone1", RO::ZONE1},
|
||||||
{"zone2", RO::Z2},
|
{"zone2", RO::ZONE2},
|
||||||
{"zone3", RO::Z3},
|
{"zone3", RO::ZONE3},
|
||||||
{"rubinetti", RO::AUX}};
|
{"rubinetti", RO::DRIP}};
|
||||||
|
|
||||||
static std::map<const std::string, std::pair<const char *, TimerHandle_t>> c_irrigationTimerMap = {{"ricircolo", {"ricircolo", NULL}},
|
static std::map<const std::string, std::pair<const char *, TimerHandle_t>> c_irrigationTimerMap = {{"ricircolo", {"ricircolo", NULL}},
|
||||||
{"zone1", {"zone1", NULL}},
|
{"zone1", {"zone1", NULL}},
|
||||||
@@ -97,25 +77,26 @@ namespace commands
|
|||||||
};
|
};
|
||||||
|
|
||||||
static const std::map<const std::string, Command> s_commandMap = {
|
static const std::map<const std::string, Command> s_commandMap = {
|
||||||
|
// TEST
|
||||||
{"setBuzz", Commands::setBuzz},
|
{"setBuzz", Commands::setBuzz},
|
||||||
|
// CONFIG
|
||||||
{"setConfig", Commands::setConfig},
|
{"setConfig", Commands::setConfig},
|
||||||
{"getConfig", Commands::getConfig},
|
{"getConfig", Commands::getConfig},
|
||||||
|
// CRONJOBS
|
||||||
{"loadCronJob", Commands::loadCronJob},
|
{"loadCronJob", Commands::loadCronJob},
|
||||||
{"setCronJob", Commands::setCronJob},
|
{"setCronJob", Commands::setCronJob},
|
||||||
{"getCronJob", Commands::getCronJob},
|
{"getCronJob", Commands::getCronJob},
|
||||||
{"delCronJob", Commands::delCronJob},
|
{"delCronJob", Commands::delCronJob},
|
||||||
{"storeCronJob", Commands::storeCronJob},
|
{"storeCronJob", Commands::storeCronJob},
|
||||||
|
// SETTERS
|
||||||
{"setHPlimit", Commands::setHPlimit},
|
{"setHPlimit", Commands::setHPlimit},
|
||||||
{"setHeating", Commands::setHeating},
|
{"setHeating", Commands::setHeating},
|
||||||
{"setIrrigation", Commands::setIrrigation},
|
{"setIrrigation", Commands::setIrrigation},
|
||||||
|
// GETTERS
|
||||||
{"getHPpower", Commands::getHPpower},
|
{"getHPpower", Commands::getHPpower},
|
||||||
{"setHeating", Commands::setHeating},
|
{"getInputStatus", Commands::getInputStatus},
|
||||||
|
{"getOutputStatus", Commands::getOutputStatus},
|
||||||
|
// NTP and Time
|
||||||
{"getTimeDrift", Commands::getTimeDrift},
|
{"getTimeDrift", Commands::getTimeDrift},
|
||||||
{"setTimeNTP", Commands::setTimeNTP},
|
{"setTimeNTP", Commands::setTimeNTP},
|
||||||
};
|
};
|
||||||
|
|||||||
28
src/main.cpp
28
src/main.cpp
@@ -8,9 +8,11 @@
|
|||||||
#include <commands.h>
|
#include <commands.h>
|
||||||
#include <cronjobs.h>
|
#include <cronjobs.h>
|
||||||
#include <mqtt.h>
|
#include <mqtt.h>
|
||||||
|
#include <ota.h>
|
||||||
|
|
||||||
#include <devices.h>
|
#include <devices.h>
|
||||||
#include "utils.h"
|
#include <utils.h>
|
||||||
|
#include <pinlist.h>
|
||||||
|
|
||||||
/////////////// GLOBALS ///////////////
|
/////////////// GLOBALS ///////////////
|
||||||
Config &conf = Config::getInstance();
|
Config &conf = Config::getInstance();
|
||||||
@@ -40,8 +42,10 @@ void loop()
|
|||||||
auto seneca = drivers::S50140(bus, conf.m_modbusSenecaAddr);
|
auto seneca = drivers::S50140(bus, conf.m_modbusSenecaAddr);
|
||||||
auto buzzer = drivers::Buzzer();
|
auto buzzer = drivers::Buzzer();
|
||||||
auto led = drivers::Led();
|
auto led = drivers::Led();
|
||||||
delay(500);
|
|
||||||
auto io = digitalIO(i2c, bus, {conf.m_modbusRelayAddr});
|
auto io = digitalIO(i2c, bus, {conf.m_modbusRelayAddr});
|
||||||
|
// Create device structure to pass all devices in the callbacks as needed
|
||||||
|
devices_t devices(eth, rtc, tmp, seneca, buzzer, led, io);
|
||||||
|
//
|
||||||
// get RTC time drift offset value
|
// get RTC time drift offset value
|
||||||
rtc.setOffset(conf.m_ntpRtcOffsetRegister);
|
rtc.setOffset(conf.m_ntpRtcOffsetRegister);
|
||||||
LOG_INFO("RTC offset register -> ", printHex(rtc.getOffset()).c_str());
|
LOG_INFO("RTC offset register -> ", printHex(rtc.getOffset()).c_str());
|
||||||
@@ -49,9 +53,8 @@ void loop()
|
|||||||
sensors = tmp.getNum();
|
sensors = tmp.getNum();
|
||||||
tmp.setCorrection(conf.m_tempCorrectionValues);
|
tmp.setCorrection(conf.m_tempCorrectionValues);
|
||||||
LOG_INFO("Temperature sensors connected ->", sensors);
|
LOG_INFO("Temperature sensors connected ->", sensors);
|
||||||
|
// Initialize OTA updater if needed
|
||||||
// Create device structure to pass all devices in the callbacks as needed
|
auto ota = OTA(devices);
|
||||||
devices_t devices(eth, rtc, tmp, seneca, buzzer, led, io);
|
|
||||||
//////////////// DEVICES ////////////////
|
//////////////// DEVICES ////////////////
|
||||||
|
|
||||||
//////////////// MQTT ////////////////
|
//////////////// MQTT ////////////////
|
||||||
@@ -123,6 +126,17 @@ void loop()
|
|||||||
led.setColor(led.COLOR_RED);
|
led.setColor(led.COLOR_RED);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (io.digitalInRead(DI::OTAENABLE)) // Initialize OTA, BLUE
|
||||||
|
{
|
||||||
|
buzzer.beepRepeat(25, 25, NOTE_A);
|
||||||
|
delay(1000);
|
||||||
|
if (io.digitalInRead(DI::OTAENABLE))
|
||||||
|
{ // maintain keyPress for 1s
|
||||||
|
ota.begin();
|
||||||
|
}
|
||||||
|
buzzer.beep(100, NOTE_G);
|
||||||
|
delay(100);
|
||||||
|
}
|
||||||
// Get RTC time at ethernet connection
|
// Get RTC time at ethernet connection
|
||||||
time_t ntpTime;
|
time_t ntpTime;
|
||||||
uint8_t timeRetries(0);
|
uint8_t timeRetries(0);
|
||||||
@@ -187,7 +201,7 @@ void loop()
|
|||||||
mqtt.publish(conf.m_mqttPublish["temperatures"], ti);
|
mqtt.publish(conf.m_mqttPublish["temperatures"], ti);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (io.digitalInRead(0)) // ROSSO - Config Reset
|
if (io.digitalInRead(DI::CONFRESET)) // ROSSO - Config Reset
|
||||||
{
|
{
|
||||||
LOG_WARN("Config RESET!");
|
LOG_WARN("Config RESET!");
|
||||||
buzzer.beep(450, NOTE_E);
|
buzzer.beep(450, NOTE_E);
|
||||||
@@ -195,7 +209,7 @@ void loop()
|
|||||||
conf.resetConfig();
|
conf.resetConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (io.digitalInRead(1)) // GIALLO - Restart
|
if (io.digitalInRead(DI::RESTART)) // GIALLO - Restart
|
||||||
{
|
{
|
||||||
LOG_WARN("RESTART!");
|
LOG_WARN("RESTART!");
|
||||||
buzzer.beep(450, NOTE_D);
|
buzzer.beep(450, NOTE_D);
|
||||||
|
|||||||
129
src/ota.cpp
Normal file
129
src/ota.cpp
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
#include <ota.h>
|
||||||
|
|
||||||
|
#define STACK_DEPTH 4096
|
||||||
|
#define TASK_PRIORITY 2
|
||||||
|
|
||||||
|
OTA::OTA(const devices_t &dev) : m_dev(dev), m_taskHandle(NULL), m_updating(false), m_prevPercent(0)
|
||||||
|
{
|
||||||
|
LOG_WARN("OTA begin, waiting for connection on [", dev.eth.localIP().toString().c_str(), "]");
|
||||||
|
}
|
||||||
|
|
||||||
|
OTA::~OTA()
|
||||||
|
{
|
||||||
|
end();
|
||||||
|
LOG_WARN("OTA end");
|
||||||
|
}
|
||||||
|
|
||||||
|
void OTA::begin()
|
||||||
|
{
|
||||||
|
if (m_taskHandle)
|
||||||
|
{
|
||||||
|
LOG_ERROR("OTA already started");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ArduinoOTA.setRebootOnSuccess(true);
|
||||||
|
ArduinoOTA.onStart(s_onStart);
|
||||||
|
ArduinoOTA.onEnd(s_onEnd);
|
||||||
|
ArduinoOTA.onProgress(s_onProgress);
|
||||||
|
ArduinoOTA.onError(s_onError);
|
||||||
|
if (xTaskCreate(handle, "otaUpdate", STACK_DEPTH, this, TASK_PRIORITY, &m_taskHandle) != pdPASS)
|
||||||
|
{
|
||||||
|
m_taskHandle = NULL;
|
||||||
|
LOG_ERROR("OTA failed to create handle task");
|
||||||
|
}
|
||||||
|
ArduinoOTA.begin(); // start the OTA server
|
||||||
|
m_dev.led.blinkAlternate(100, 100, m_dev.led.COLOR_ORANGE, m_dev.led.COLOR_SKYBLUE);
|
||||||
|
m_dev.led.setEnforce(true); // take unique control of the LED
|
||||||
|
m_active = true;
|
||||||
|
LOG_WARN("OTA started");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OTA::end()
|
||||||
|
{
|
||||||
|
if (m_updating)
|
||||||
|
{
|
||||||
|
LOG_WARN("OTA cannot cancel update while running");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (m_taskHandle)
|
||||||
|
{
|
||||||
|
vTaskDelete(m_taskHandle);
|
||||||
|
m_taskHandle = NULL;
|
||||||
|
m_updating = false;
|
||||||
|
ArduinoOTA.end();
|
||||||
|
m_active = false;
|
||||||
|
m_dev.led.setColor(m_dev.led.COLOR_GREEN);
|
||||||
|
m_dev.led.setEnforce(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OTA::isActive()
|
||||||
|
{
|
||||||
|
return m_active;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OTA::onProgress(const uint32_t progress, const uint32_t total)
|
||||||
|
{
|
||||||
|
float percent = (progress * 100.0f) / total;
|
||||||
|
if (percent > m_prevPercent + 5.0f)
|
||||||
|
{
|
||||||
|
LOG_INFO("OTA progress [", percent, "]%");
|
||||||
|
m_prevPercent = percent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void OTA::onStart()
|
||||||
|
{
|
||||||
|
LOG_WARN("OTA update started");
|
||||||
|
m_updating = true;
|
||||||
|
m_dev.led.setEnforce(false);
|
||||||
|
m_dev.led.blinkAlternate(25, 50, m_dev.led.COLOR_BLUE, m_dev.led.COLOR_OFF);
|
||||||
|
m_dev.led.setEnforce(true);
|
||||||
|
m_prevPercent = 0;
|
||||||
|
}
|
||||||
|
void OTA::onEnd()
|
||||||
|
{
|
||||||
|
LOG_WARN("OTA update end");
|
||||||
|
m_updating = false;
|
||||||
|
m_dev.led.setEnforce(false);
|
||||||
|
m_dev.led.blinkAlternate(50, 50, m_dev.led.COLOR_GREEN, m_dev.led.COLOR_YELLOW);
|
||||||
|
m_dev.led.setEnforce(true);
|
||||||
|
}
|
||||||
|
void OTA::onError(const ota_error_t err)
|
||||||
|
{
|
||||||
|
LOG_ERROR("OTA Error [", err, "]");
|
||||||
|
switch (err)
|
||||||
|
{
|
||||||
|
case OTA_AUTH_ERROR:
|
||||||
|
LOG_ERROR("OTA authentication error");
|
||||||
|
break;
|
||||||
|
case OTA_BEGIN_ERROR:
|
||||||
|
LOG_ERROR("OTA begin errror");
|
||||||
|
break;
|
||||||
|
case OTA_CONNECT_ERROR:
|
||||||
|
LOG_ERROR("OTA connection error");
|
||||||
|
break;
|
||||||
|
case OTA_RECEIVE_ERROR:
|
||||||
|
LOG_ERROR("OTA receive error");
|
||||||
|
break;
|
||||||
|
case OTA_END_ERROR:
|
||||||
|
LOG_ERROR("OTA end error");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOG_ERROR("OTA unknown error");
|
||||||
|
};
|
||||||
|
m_updating = false;
|
||||||
|
end(); // end ota on error
|
||||||
|
}
|
||||||
|
|
||||||
|
void OTA::handle(void *params)
|
||||||
|
{
|
||||||
|
OTA *ota = (OTA *)params;
|
||||||
|
while (true)
|
||||||
|
{ // task never returns
|
||||||
|
ArduinoOTA.handle();
|
||||||
|
delay(50);
|
||||||
|
}
|
||||||
|
vTaskDelete(ota->m_taskHandle);
|
||||||
|
ota->m_taskHandle = NULL;
|
||||||
|
}
|
||||||
52
src/ota.h
Normal file
52
src/ota.h
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define DEBUGLOG_DEFAULT_LOG_LEVEL_INFO
|
||||||
|
|
||||||
|
#include <DebugLog.h>
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <ArduinoOTA.h>
|
||||||
|
#include <devices.h>
|
||||||
|
|
||||||
|
class OTA
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
OTA(const devices_t &dev);
|
||||||
|
~OTA();
|
||||||
|
|
||||||
|
void begin();
|
||||||
|
void end();
|
||||||
|
bool isActive();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void onProgress(const uint32_t progress, const uint32_t total);
|
||||||
|
void onStart();
|
||||||
|
void onEnd();
|
||||||
|
void onError(const ota_error_t err);
|
||||||
|
static void handle(void *params);
|
||||||
|
|
||||||
|
private:
|
||||||
|
const devices_t &m_dev;
|
||||||
|
TaskHandle_t m_taskHandle;
|
||||||
|
bool m_updating;
|
||||||
|
bool m_active;
|
||||||
|
float m_prevPercent;
|
||||||
|
|
||||||
|
private: // callbacks, do not init in code
|
||||||
|
ArduinoOTAClass::THandlerFunction s_onStart = [this]()
|
||||||
|
{
|
||||||
|
this->onStart();
|
||||||
|
};
|
||||||
|
ArduinoOTAClass::THandlerFunction s_onEnd = [this]()
|
||||||
|
{
|
||||||
|
this->onEnd();
|
||||||
|
};
|
||||||
|
ArduinoOTAClass::THandlerFunction_Progress s_onProgress = [this](const uint32_t progress, const uint32_t total)
|
||||||
|
{
|
||||||
|
this->onProgress(progress, total);
|
||||||
|
};
|
||||||
|
ArduinoOTAClass::THandlerFunction_Error s_onError = [this](const ota_error_t err)
|
||||||
|
{
|
||||||
|
this->onError(err);
|
||||||
|
};
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user