Files
OpenMQTT-Gateway/main/ZactuatorONOFF.ino
T
FlorianandFlorian 3187ed6154 [SERIAL] Enable SERIAL to X communication (#2068)
Add Serial as a communication layer so as to transmit data to/from serial
Enable discovery of BT sensors through serial
Add serial heartbeat

Change discovery principle for interval and intervalacts, keep them always discovered and deactivate adaptivescan when changed. Instead of removing their discovery when adaptive scan is activated

Increase JSON_MSG_BUFFER_MAX as we are now using const char* and it requires more memory for Arduino Json Deserialization

Co-authored-by: Florian <1technophile@users.noreply.github.com>
2024-09-26 06:55:26 -05:00

329 lines
12 KiB
Arduino

/*
Theengs OpenMQTTGateway - We Unite Sensors in One Open-Source Interface
Act as a gateway between your 433mhz, infrared IR, BLE, LoRa signal and one interface like an MQTT broker
Send and receiving command by MQTT
Output GPIO defined to High or Low
Copyright: (c)Florian ROBERT
Contributors:
- 1technophile
This file is part of OpenMQTTGateway.
OpenMQTTGateway is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OpenMQTTGateway is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "User_config.h"
#ifdef ZactuatorONOFF
# ifdef ESP32
// Global struct to store live ONOFF configuration data
ONOFFConfig_s ONOFFConfig;
void ONOFFConfig_init() {
ONOFFConfig.ONOFFState = !ACTUATOR_ON;
ONOFFConfig.useLastStateOnStart = USE_LAST_STATE_ON_RESTART;
}
void ONOFFConfig_fromJson(JsonObject& ONOFFdata) {
Config_update(ONOFFdata, "uselaststate", ONOFFConfig.useLastStateOnStart);
Config_update(ONOFFdata, "cmd", ONOFFConfig.ONOFFState);
if (ONOFFdata.containsKey("erase") && ONOFFdata["erase"].as<bool>()) {
// Erase config from NVS (non-volatile storage)
preferences.begin(Gateway_Short_Name, false);
if (preferences.isKey("ONOFFConfig")) {
int result = preferences.remove("ONOFFConfig");
Log.notice(F("ONOFF config erase result: %d" CR), result);
preferences.end();
return; // Erase prevails on save, so skipping save
} else {
Log.notice(F("ONOFF config not found" CR));
preferences.end();
}
}
if (ONOFFdata.containsKey("save") && ONOFFdata["save"].as<bool>()) {
StaticJsonDocument<JSON_MSG_BUFFER> jsonBuffer;
JsonObject jo = jsonBuffer.to<JsonObject>();
jo["uselaststate"] = ONOFFConfig.useLastStateOnStart;
jo["cmd"] = ONOFFConfig.ONOFFState;
// Save config into NVS (non-volatile storage)
String conf = "";
serializeJson(jsonBuffer, conf);
preferences.begin(Gateway_Short_Name, false);
int result = preferences.putString("ONOFFConfig", conf);
preferences.end();
Log.notice(F("ONOFF Config_save: %s, result: %d" CR), conf.c_str(), result);
}
}
void ONOFFConfig_load() {
StaticJsonDocument<JSON_MSG_BUFFER> jsonBuffer;
preferences.begin(Gateway_Short_Name, true);
if (preferences.isKey("ONOFFConfig")) {
auto error = deserializeJson(jsonBuffer, preferences.getString("ONOFFConfig", "{}"));
preferences.end();
if (error) {
Log.error(F("ONOFF config deserialization failed: %s, buffer capacity: %u" CR), error.c_str(), jsonBuffer.capacity());
return;
}
if (jsonBuffer.isNull()) {
Log.warning(F("ONOFF config is null" CR));
return;
}
JsonObject jo = jsonBuffer.as<JsonObject>();
ONOFFConfig_fromJson(jo);
Log.notice(F("ONOFF config loaded" CR));
} else {
preferences.end();
Log.notice(F("ONOFF config not found" CR));
}
}
# else
void ONOFFConfig_init(){};
void ONOFFConfig_fromJson(JsonObject& ONOFFdata){};
void ONOFFConfig_load(){};
# endif
void updatePowerIndicator() {
# ifdef LED_ACTUATOR_ONOFF
if (digitalRead(ACTUATOR_ONOFF_GPIO) == ACTUATOR_ON) {
ledManager.setMode(LED_ACTUATOR_ONOFF, 0, LEDManager::Mode::STATIC, LED_ACTUATOR_ONOFF_COLOR);
} else {
ledManager.setMode(LED_ACTUATOR_ONOFF, 0, LEDManager::Mode::STATIC, LED_COLOR_BLACK);
}
# endif
}
void setupONOFF() {
# ifdef MAX_TEMP_ACTUATOR
xTaskCreate(overLimitTemp, "overLimitTemp", 4000, NULL, 10, NULL);
# endif
# ifdef ESP32
ONOFFConfig_init();
ONOFFConfig_load();
Log.notice(F("Target state on restart: %T" CR), ONOFFConfig.ONOFFState);
Log.notice(F("Use last state on restart: %T" CR), ONOFFConfig.useLastStateOnStart);
# endif
pinMode(ACTUATOR_ONOFF_GPIO, OUTPUT);
# ifdef ACTUATOR_ONOFF_DEFAULT
digitalWrite(ACTUATOR_ONOFF_GPIO, ACTUATOR_ONOFF_DEFAULT);
# elif defined(ESP32)
if (ONOFFConfig.useLastStateOnStart) {
digitalWrite(ACTUATOR_ONOFF_GPIO, ONOFFConfig.ONOFFState);
} else {
digitalWrite(ACTUATOR_ONOFF_GPIO, !ACTUATOR_ON);
}
# endif
updatePowerIndicator();
Log.trace(F("ZactuatorONOFF setup done" CR));
}
# if jsonReceiving
void XtoONOFF(const char* topicOri, JsonObject& ONOFFdata) {
if (cmpToMainTopic(topicOri, subjectMQTTtoONOFF)) {
Log.trace(F("MQTTtoONOFF json data analysis" CR));
int boolSWITCHTYPE = ONOFFdata["cmd"] | 99;
int gpio = ONOFFdata["gpio"] | ACTUATOR_ONOFF_GPIO;
if (boolSWITCHTYPE != 99) {
Log.notice(F("MQTTtoONOFF boolSWITCHTYPE ok: %d" CR), boolSWITCHTYPE);
Log.notice(F("GPIO number: %d" CR), gpio);
pinMode(gpio, OUTPUT);
digitalWrite(gpio, boolSWITCHTYPE);
# ifdef LED_ACTUATOR_ONOFF
if (boolSWITCHTYPE == ACTUATOR_ON) {
ledManager.setMode(LED_ACTUATOR_ONOFF, 0, LEDManager::Mode::STATIC, LED_ACTUATOR_ONOFF_COLOR);
} else {
ledManager.setMode(LED_ACTUATOR_ONOFF, 0, LEDManager::Mode::STATIC, LED_COLOR_BLACK);
}
# endif
# ifdef ESP32
if (ONOFFConfig.useLastStateOnStart) {
ONOFFdata["save"] = true;
ONOFFConfig_fromJson(ONOFFdata);
}
# endif
// we acknowledge the sending by publishing the value to an acknowledgement topic
stateONOFFMeasures();
} else {
if (ONOFFdata["cmd"] == "high_pulse") {
Log.notice(F("MQTTtoONOFF high_pulse ok" CR));
Log.notice(F("GPIO number: %d" CR), gpio);
int pulselength = ONOFFdata["pulse_length"] | 500;
Log.notice(F("Pulse length: %d ms" CR), pulselength);
pinMode(gpio, OUTPUT);
digitalWrite(gpio, HIGH);
delay(pulselength);
digitalWrite(gpio, LOW);
} else if (ONOFFdata["cmd"] == "low_pulse") {
Log.notice(F("MQTTtoONOFF low_pulse ok" CR));
Log.notice(F("GPIO number: %d" CR), gpio);
int pulselength = ONOFFdata["pulse_length"] | 500;
Log.notice(F("Pulse length: %d ms" CR), pulselength);
pinMode(gpio, OUTPUT);
digitalWrite(gpio, LOW);
delay(pulselength);
digitalWrite(gpio, HIGH);
} else {
Log.error(F("MQTTtoONOFF failed json read" CR));
}
}
}
if (cmpToMainTopic(topicOri, subjectMQTTtoONOFFset)) {
Log.trace(F("MQTTtoONOFF json set" CR));
/*
* Configuration modifications priorities:
* First `init=true` and `load=true` commands are executed (if both are present, INIT prevails on LOAD)
* Then parameters included in json are taken in account
* Finally `erase=true` and `save=true` commands are executed (if both are present, ERASE prevails on SAVE)
*/
if (ONOFFdata.containsKey("init") && ONOFFdata["init"].as<bool>()) {
// Restore the default (initial) configuration
ONOFFConfig_init();
} else if (ONOFFdata.containsKey("load") && ONOFFdata["load"].as<bool>()) {
// Load the saved configuration, if not initialised
ONOFFConfig_load();
}
// Load config from json if available
ONOFFConfig_fromJson(ONOFFdata);
stateONOFFMeasures();
}
}
# endif
# if simpleReceiving
void XtoONOFF(const char* topicOri, const char* datacallback) {
if ((cmpToMainTopic(topicOri, subjectMQTTtoONOFF))) {
Log.trace(F("MQTTtoONOFF" CR));
char* endptr = NULL;
long gpio = strtol(datacallback, &endptr, 10);
if (datacallback == endptr)
gpio = ACTUATOR_ONOFF_GPIO;
Log.notice(F("GPIO number: %d" CR), gpio);
pinMode(gpio, OUTPUT);
bool ON = false;
if (strstr(topicOri, ONKey) != NULL)
ON = true;
if (strstr(topicOri, OFFKey) != NULL)
ON = false;
digitalWrite(gpio, ON);
# ifdef LED_ACTUATOR_ONOFF
if (ON == ACTUATOR_ON) {
ledManager.setMode(LED_ACTUATOR_ONOFF, 0, LEDManager::Mode::STATIC, LED_ACTUATOR_ONOFF_COLOR);
} else {
ledManager.setMode(LED_ACTUATOR_ONOFF, 0, LEDManager::Mode::STATIC, LED_COLOR_BLACK);
}
# endif
// we acknowledge the sending by publishing the value to an acknowledgement topic
char b = ON;
pub(subjectGTWONOFFtoMQTT, &b);
}
}
# endif
//Check regularly temperature of the ESP32 board and switch OFF the relay if temperature is more than MAX_TEMP_ACTUATOR
# ifdef MAX_TEMP_ACTUATOR
void overLimitTemp(void* pvParameters) {
# if defined(ESP32) && !defined(NO_INT_TEMP_READING)
for (;;) {
static float previousInternalTempc = 0;
float internalTempc = intTemperatureRead();
Log.trace(F("Internal temperature of the ESP32 %F" CR), internalTempc);
// We switch OFF the actuator if the temperature of the ESP32 is more than MAX_TEMP_ACTUATOR two consecutive times, so as to avoid false single readings to trigger the relay OFF.
if (internalTempc > MAX_TEMP_ACTUATOR && previousInternalTempc > MAX_TEMP_ACTUATOR) {
if (digitalRead(ACTUATOR_ONOFF_GPIO) == ACTUATOR_ON) { // This could be with the previous condition, but it is better to trigger the digitalRead only if the previous condition is met to avoid the digitalRead
Log.error(F("[ActuatorONOFF] OverTemperature detected ( %F > %F ) switching OFF Actuator" CR), internalTempc, MAX_TEMP_ACTUATOR);
ActuatorTrigger();
# ifdef LED_ACTUATOR_ONOFF
ledManager.setMode(LED_ACTUATOR_ONOFF, 0, LEDManager::Mode::STATIC, LED_ERROR_COLOR, -1);
# endif
}
}
previousInternalTempc = internalTempc;
vTaskDelay(TimeBetweenReadingIntTemp);
}
# endif
}
# endif
// Check regularly current the relay and switch it OFF if the current is more than MAX_CURRENT_ACTUATOR
# ifdef MAX_CURRENT_ACTUATOR
void overLimitCurrent(float RN8209current) {
static float RN8209previousCurrent = 0;
Log.trace(F("RN8209 Current %F" CR), RN8209current);
// We switch OFF the actuator if the current of the RN8209 is more than MAX_CURRENT_ACTUATOR.
if (RN8209current > MAX_CURRENT_ACTUATOR && RN8209previousCurrent > MAX_CURRENT_ACTUATOR) {
if (digitalRead(ACTUATOR_ONOFF_GPIO) == ACTUATOR_ON) { // This could be with the previous condition, but it is better to trigger the digitalRead only if the previous condition is met to avoid the digitalRead
Log.error(F("[ActuatorONOFF] OverCurrent detected ( %F > %F ) switching OFF Actuator" CR), RN8209current, MAX_CURRENT_ACTUATOR);
ActuatorTrigger();
# ifdef LED_ACTUATOR_ONOFF
ledManager.setMode(LED_ACTUATOR_ONOFF, 0, LEDManager::Mode::STATIC, LED_ERROR_COLOR, -1);
# endif
}
}
RN8209previousCurrent = RN8209current;
}
# else
void overLimitCurrent(float RN8209current) {}
# endif
/*
Handling of actuator control following the cases below:
-Button press, if the button goes to ACTUATOR_BUTTON_TRIGGER_LEVEL we change the Actuator level
-Status less switch state change (a switch without ON OFF labels), an action of this type of switch will trigger a change of the actuator state independently from the switch position
*/
void ActuatorTrigger() {
uint8_t level = !digitalRead(ACTUATOR_ONOFF_GPIO);
Log.trace(F("Actuator triggered %d" CR), level);
digitalWrite(ACTUATOR_ONOFF_GPIO, level);
# ifdef LED_ACTUATOR_ONOFF
if (level == ACTUATOR_ON) {
ledManager.setMode(LED_ACTUATOR_ONOFF, 0, LEDManager::Mode::STATIC, LED_ACTUATOR_ONOFF_COLOR);
} else {
ledManager.setMode(LED_ACTUATOR_ONOFF, 0, LEDManager::Mode::STATIC, LED_COLOR_BLACK);
}
# endif
# ifdef ESP32
if (ONOFFConfig.useLastStateOnStart) {
StaticJsonDocument<64> jsonBuffer;
JsonObject ONOFFdata = jsonBuffer.to<JsonObject>();
ONOFFdata["cmd"] = (int)level;
ONOFFdata["save"] = true;
ONOFFConfig_fromJson(ONOFFdata);
}
# endif
stateONOFFMeasures();
}
void stateONOFFMeasures() {
//Publish actuator state
StaticJsonDocument<128> jsonBuffer;
JsonObject ONOFFdata = jsonBuffer.to<JsonObject>();
ONOFFdata["cmd"] = (int)digitalRead(ACTUATOR_ONOFF_GPIO);
# ifdef ESP32
ONOFFdata["uselaststate"] = ONOFFConfig.useLastStateOnStart;
# endif
ONOFFdata["origin"] = subjectGTWONOFFtoMQTT;
enqueueJsonObject(ONOFFdata, QueueSemaphoreTimeOutTask);
}
#endif