diff --git a/lib/BleFingerprint/string_utils.cpp b/lib/BleFingerprint/string_utils.cpp index 667841c..88fcdc1 100644 --- a/lib/BleFingerprint/string_utils.cpp +++ b/lib/BleFingerprint/string_utils.cpp @@ -1,5 +1,6 @@ #include #include +#include std::string ltrim(const std::string &s, char toTrim) { @@ -106,3 +107,12 @@ bool prefixExists(const String &prefixes, const String &s) auto sub = prefixes.substring(start); return !sub.isEmpty() && s.indexOf(sub) != -1; } + +bool spurt(const String &fn, const String &content) +{ + File f = SPIFFS.open(fn, "w"); + if (!f) return false; + auto w = f.print(content); + f.close(); + return w == content.length(); +} diff --git a/lib/BleFingerprint/string_utils.h b/lib/BleFingerprint/string_utils.h index 50b7670..6bc3ece 100644 --- a/lib/BleFingerprint/string_utils.h +++ b/lib/BleFingerprint/string_utils.h @@ -1,5 +1,4 @@ -#pragma once - +#pragma once #include #include #include @@ -26,3 +25,4 @@ std::string hexStrRev(const uint8_t *data, int len); std::string hexStrRev(const char *data, int len); std::string hexStrRev(const std::string &s); bool prefixExists(const String& prefixes, const String& s); +bool spurt(const String &fn, const String &content); diff --git a/src/BH1750.cpp b/src/BH1750.cpp new file mode 100644 index 0000000..1272a55 --- /dev/null +++ b/src/BH1750.cpp @@ -0,0 +1,146 @@ +#ifdef SENSORS +#include "BH1750.h" + +#include "globals.h" +#include "mqtt.h" +#include "defaults.h" +#include +#include +#include "string_utils.h" + +#include + +namespace BH1750 +{ + hp_BH1750 BH1750; + unsigned long ms_BH1750; + float lux_BH1750; + int lux_BH1750_MQTT; + String BH1750_I2c; + int BH1750_I2c_Bus; + bool initialized = false; + + void Setup() + { + if (!I2C_Bus_1_Enabled && !I2C_Bus_2_Enabled) return; + if (BH1750_I2c != "0x23" && BH1750_I2c != "0x5C") return; + + int rc; + unsigned long m; // milli for calibration + bool state = false; + + if (BH1750_I2c == "0x23" && BH1750_I2c_Bus == 1) + { + state = BH1750.begin(BH1750_TO_GROUND, &Wire); + } + else if (BH1750_I2c == "0x5C" && BH1750_I2c_Bus == 1) + { + state = BH1750.begin(BH1750_TO_VCC, &Wire); + } + else if (BH1750_I2c == "0x23" && BH1750_I2c_Bus == 2) + { + state = BH1750.begin(BH1750_TO_GROUND, &Wire1); + } + else if (BH1750_I2c == "0x5C" && BH1750_I2c_Bus == 2) + { + state = BH1750.begin(BH1750_TO_VCC, &Wire1); + } + + if (!state) + { + Serial.println("Error on initialisation BH1750 GY-302"); + } + else + { + Serial.println("initialisation BH1750 GY-302 success"); + m = millis(); + rc = BH1750.calibrateTiming(); + m = millis() - m; + Serial.print("Lux Sensor BH1750 GY-302 calibrated (Time: "); + Serial.print(m); + Serial.print(" ms)"); + if (rc != 0) + { + Serial.print(" - Lux Sensor Error code "); + Serial.print(rc); + } + Serial.println(); + + // start first measure and timecount + lux_BH1750 = -1; // nothing to compare + BH1750.start(BH1750_QUALITY_HIGH, 1); + ms_BH1750 = millis(); + initialized = true; + } + } + + void ConnectToWifi() + { + WiFiSettings.html("h4", "BH1750 - Ambient Light Sensor:"); + BH1750_I2c_Bus = WiFiSettings.integer("BH1750_I2c_Bus", 1, 2, DEFAULT_I2C_BUS, "I2C Bus"); + BH1750_I2c = WiFiSettings.string("BH1750_I2c", "", "I2C address (0x23 or 0x5C)"); + } + + void SerialReport() + { + Serial.print("BH1750_I2c Sensor: "); + Serial.println(BH1750_I2c + " on bus " + BH1750_I2c_Bus); + } + + void Loop() + { + if (!I2C_Bus_1_Enabled && !I2C_Bus_2_Enabled) return; + if (!initialized) return; + + if (BH1750_I2c == "0x23" || BH1750_I2c == "0x5C") + { + float lux; + int lux_mqtt; + + if (BH1750.hasValue()) + { + ms_BH1750 = millis() - ms_BH1750; + if (!BH1750.saturated()) + { + lux = BH1750.getLux(); + lux_mqtt = int(lux); + + if (lux != lux_BH1750) + { + lux_BH1750 = lux; + // Serial.print("BH1750 ("); + // Serial.print(ms_BH1750); + // Serial.print(" ms): "); + // Serial.print(lux); + // Serial.println(" lx"); + } + + //convert lx to integer to reduce mqtt traffic, send only if lx changed + if (lux_mqtt != lux_BH1750_MQTT) + { + lux_BH1750_MQTT = lux_mqtt; + Serial.print("BH1750 ("); + Serial.print(ms_BH1750); + Serial.print(" ms): "); + Serial.print(lux_mqtt); + Serial.println(" lx"); + mqttClient.publish((roomsTopic + "/lux").c_str(), 0, 1, String(lux_mqtt).c_str()); + } + } + + BH1750.adjustSettings(90); + BH1750.start(); + ms_BH1750 = millis(); + } + } + } + + bool SendDiscovery() + { + if (BH1750_I2c.isEmpty()) return true; + + return sendSensorDiscovery("Lux", "", "illuminance", "lx"); + } +} + +#endif diff --git a/src/BH1750.h b/src/BH1750.h new file mode 100644 index 0000000..4ccbd25 --- /dev/null +++ b/src/BH1750.h @@ -0,0 +1,17 @@ +#pragma once +#ifdef SENSORS +#include + +// Forward declares +class AsyncMqttClient; + +namespace BH1750 +{ + void Setup(); + void ConnectToWifi(); + void SerialReport(); + void Loop(); + bool SendDiscovery(); +} + +#endif diff --git a/src/BME280Sensor.cpp b/src/BME280Sensor.cpp index 59e64bb..d0594f0 100644 --- a/src/BME280Sensor.cpp +++ b/src/BME280Sensor.cpp @@ -1,24 +1,13 @@ #ifdef SENSORS - -#include "defaults.h" #include "BME280Sensor.h" -#include -#include -#include -// for #define ESPMAC +#include "globals.h" +#include "mqtt.h" +#include "defaults.h" +#include #include "string_utils.h" -// TODO: Not a fan of externs, but this helps refactoring for now -extern bool I2C_Bus_1_Enabled; -extern bool I2C_Bus_2_Enabled; -extern unsigned long sensorInterval; - -extern char buffer[2048]; -extern String room; -extern String roomsTopic; -extern void commonDiscovery(); -extern bool pub(const char *topic, uint8_t qos, bool retain, const char *payload, size_t length = 0, bool dup = false, uint16_t message_id = 0); +#include namespace BME280 { @@ -27,10 +16,38 @@ namespace BME280 String BME280_I2c; int BME280_I2c_Bus; unsigned long bme280PreviousMillis = 0; + int sensorInterval = 60000; + bool initialized = false; void Setup() { + if (!I2C_Bus_1_Enabled && !I2C_Bus_2_Enabled) return; + if (BME280_I2c == "0x76" && BME280_I2c_Bus == 1) { + BME280_status = BME280.begin(0x76, &Wire); + } else if (BME280_I2c == "0x77" && BME280_I2c_Bus == 1) { + BME280_status = BME280.begin(0x77, &Wire); + } else if (BME280_I2c == "0x76" && BME280_I2c_Bus == 2) { + BME280_status = BME280.begin(0x76, &Wire1); + } else if (BME280_I2c == "0x77" && BME280_I2c_Bus == 2) { + BME280_status = BME280.begin(0x77, &Wire1); + } else { + return; + } + + if (!BME280_status) { + Serial.println("[BME280] Couldn't find a sensor, check your wiring and I2C address!"); + } else { + initialized = true; + } + + BME280.setSampling( + Adafruit_BME280::MODE_FORCED, + Adafruit_BME280::SAMPLING_X1, // Temperature + Adafruit_BME280::SAMPLING_X1, // Pressure + Adafruit_BME280::SAMPLING_X1, // Humidity + Adafruit_BME280::FILTER_OFF + ); } void ConnectToWifi() @@ -46,35 +63,10 @@ namespace BME280 Serial.println(BME280_I2c + " on bus " + BME280_I2c_Bus); } - void Loop(AsyncMqttClient& mqttClient) + void Loop() { if (!I2C_Bus_1_Enabled && !I2C_Bus_2_Enabled) return; - - // TODO: This should move to setup - if (BME280_I2c == "0x76" && BME280_I2c_Bus == 1) { - BME280_status = BME280.begin(0x76, &Wire); - } else if (BME280_I2c == "0x77" && BME280_I2c_Bus == 1) { - BME280_status = BME280.begin(0x77, &Wire); - } else if (BME280_I2c == "0x76" && BME280_I2c_Bus == 2) { - BME280_status = BME280.begin(0x76, &Wire1); - } else if (BME280_I2c == "0x77" && BME280_I2c_Bus == 2) { - BME280_status = BME280.begin(0x77, &Wire1); - } else { - return; - } - - if (!BME280_status) { - Serial.println("[BME280] Couldn't find a sensor, check your wiring and I2C address!"); - } - - BME280.setSampling( - Adafruit_BME280::MODE_FORCED, - Adafruit_BME280::SAMPLING_X1, // Temperature - Adafruit_BME280::SAMPLING_X1, // Pressure - Adafruit_BME280::SAMPLING_X1, // Humidity - //Adafruit_BME280::FILTER_X16, - Adafruit_BME280::FILTER_OFF - ); + if (!initialized) return; float temperature = BME280.readTemperature(); float humidity = BME280.readHumidity(); @@ -90,65 +82,13 @@ namespace BME280 } } - static bool SendTemperature(DynamicJsonDocument& doc) - { - commonDiscovery(); - doc["~"] = roomsTopic; - doc["name"] = "ESPresense " + room + " BME280 Temperature"; - doc["uniq_id"] = Sprintf("espresense_%06lx_bme280_temperature", CHIPID); - doc["avty_t"] = "~/status"; - doc["stat_t"] = "~/bme280_temperature"; - doc["dev_cla"] = "temperature"; - doc["unit_of_meas"] = "°C"; - doc["frc_upd"] = true; - - serializeJson(doc, buffer); - String discoveryTopic = "homeassistant/sensor/espresense_" + ESPMAC + "/bme280_temperature/config"; - - return pub(discoveryTopic.c_str(), 0, true, buffer); - } - - static bool SendHumidity(DynamicJsonDocument& doc) - { - commonDiscovery(); - doc["~"] = roomsTopic; - doc["name"] = "ESPresense " + room + " BME280 Humidity"; - doc["uniq_id"] = Sprintf("espresense_%06lx_bme280_humidity", CHIPID); - doc["avty_t"] = "~/status"; - doc["stat_t"] = "~/bme280_humidity"; - doc["dev_cla"] = "humidity"; - doc["unit_of_meas"] = "%"; - doc["frc_upd"] = true; - - serializeJson(doc, buffer); - String discoveryTopic = "homeassistant/sensor/espresense_" + ESPMAC + "/bme280_humidity/config"; - - return pub(discoveryTopic.c_str(), 0, true, buffer); - } - - static bool SendPressure(DynamicJsonDocument& doc) - { - commonDiscovery(); - doc["~"] = roomsTopic; - doc["name"] = "ESPresense " + room + " BME280 Pressure"; - doc["uniq_id"] = Sprintf("espresense_%06lx_bme280_pressure", CHIPID); - doc["avty_t"] = "~/status"; - doc["stat_t"] = "~/bme280_pressure"; - doc["dev_cla"] = "pressure"; - doc["unit_of_meas"] = "hPa"; - doc["frc_upd"] = true; - - serializeJson(doc, buffer); - String discoveryTopic = "homeassistant/sensor/espresense_" + ESPMAC + "/bme280_pressure/config"; - - return pub(discoveryTopic.c_str(), 0, true, buffer); - } - - bool SendDiscovery(DynamicJsonDocument& doc) + bool SendDiscovery() { if (BME280_I2c.isEmpty()) return true; - return SendTemperature(doc) && SendHumidity(doc) && SendPressure(doc); + return sendSensorDiscovery("BME Temperature", "", "temperature", "°C") + && sendSensorDiscovery("BME Humidity", "", "humidity", "%") + && sendSensorDiscovery("BME Pressure", "", "pressure", "hPa"); } } diff --git a/src/BME280Sensor.h b/src/BME280Sensor.h index 5d0aa29..6851608 100644 --- a/src/BME280Sensor.h +++ b/src/BME280Sensor.h @@ -1,19 +1,14 @@ -#ifdef SENSORS - -#pragma once - -#include - -// Forward declares -class AsyncMqttClient; - -namespace BME280 -{ - void Setup(); - void ConnectToWifi(); - void SerialReport(); - void Loop(AsyncMqttClient& mqttClient); - bool SendDiscovery(DynamicJsonDocument& doc); -} - -#endif \ No newline at end of file +#pragma once +#ifdef SENSORS +#include + +namespace BME280 +{ + void Setup(); + void ConnectToWifi(); + void SerialReport(); + void Loop(); + bool SendDiscovery(); +} + +#endif diff --git a/src/DHT.cpp b/src/DHT.cpp new file mode 100644 index 0000000..7c681b0 --- /dev/null +++ b/src/DHT.cpp @@ -0,0 +1,159 @@ +#ifdef SENSORS +#include "DHT.h" + +#include "globals.h" +#include "mqtt.h" +#include "defaults.h" +#include +#include +#include "string_utils.h" + +#include +#include + +namespace DHT +{ + uint8_t dht11Pin; + uint8_t dht22Pin; + float dhtTempOffset; + + /** Initialize DHT sensor 1 */ + DHTesp dhtSensor; + + /** Task handle for the light value read task */ + TaskHandle_t dhtTempTaskHandle = NULL; + + /** Ticker for temperature reading */ + Ticker tempTicker; + + /** Flags for temperature readings finished */ + bool gotNewTemperature = false; + + /** Data from dht sensor 1 */ + TempAndHumidity dhtSensorData; + + /* Flag if main loop is running */ + bool dhtTasksEnabled = false; + + /* update time */ + int dhtUpdateTime = 10; //ToDo: maybe make this a user choise via settings menu + + /** + * Task to reads temperature from DHT11 sensor + * @param pvParameters + * pointer to task parameters + */ + void tempTask(void *pvParameters) + { + Serial.println("tempTask loop started"); + while (1) // tempTask loop + { + if (dhtTasksEnabled && !gotNewTemperature) + { + // Read temperature only if old data was processed already + // Reading temperature for humidity takes about 250 milliseconds! + // Sensor readings may also be up to 2 seconds 'old' (it's a very slow sensor) + dhtSensorData = dhtSensor.getTempAndHumidity(); // Read values from sensor 1 + gotNewTemperature = true; + } + vTaskSuspend(NULL); + } + } + + /** + * triggerGetTemp + * Sets flag dhtUpdated to true for handling in loop() + * called by Ticker tempTicker + */ + void triggerGetTemp() + { + if (dhtTempTaskHandle != NULL) + { + xTaskResumeFromISR(dhtTempTaskHandle); + } + } + + void Setup() + { + if (dht11Pin) dhtSensor.setup(dht11Pin, DHTesp::DHT11); + if (dht22Pin) dhtSensor.setup(dht22Pin, DHTesp::DHT22); //(AM2302) + + if (dht11Pin || dht22Pin) + { + // Start task to get temperature + xTaskCreatePinnedToCore( + tempTask, /* Function to implement the task */ + "tempTask ", /* Name of the task */ + 4000, /* Stack size in words */ + NULL, /* Task input parameter */ + 5, /* Priority of the task */ + &dhtTempTaskHandle, /* Task handle. */ + 1); /* Core where the task should run */ + + if (dhtTempTaskHandle == NULL) + { + Serial.println("[ERROR] Failed to start task for temperature update"); + } + else + { + // Start update of environment data every 10 seconds + tempTicker.attach(dhtUpdateTime, triggerGetTemp); + } + + // Signal end of setup() to tasks + dhtTasksEnabled = true; + } + } + + void ConnectToWifi() + { + dht11Pin = WiFiSettings.integer("dht11_pin", 0, "DHT11 sensor pin (0 for disable)"); + dht22Pin = WiFiSettings.integer("dht22_pin", 0, "DHT22 sensor pin (0 for disable)"); + dhtTempOffset = WiFiSettings.floating("dhtTemp_offset", -40, 125, 0.0, "DHT temperature offset"); + } + + void SerialReport() + { + Serial.print("DHT11 Sensor: "); + Serial.println(dht11Pin ? "enabled" : "disabled"); + Serial.print("DHT22 Sensor: "); + Serial.println(dht22Pin ? "enabled" : "disabled"); + Serial.print("DHT Temp Offset: "); + Serial.println(dhtTempOffset ? "enabled" : "disabled"); + } + + void Loop() + { + if (!dht11Pin && !dht22Pin) return; + + if (gotNewTemperature) + { + float humidity = dhtSensorData.humidity; + float temperature = dhtSensorData.temperature + dhtTempOffset; + Serial.println("Temp: " + String(temperature, 1) + "'C Humidity: " + String(humidity, 1) + "%"); + + mqttClient.publish((roomsTopic + "/humidity").c_str(), 0, 1, String(humidity, 1).c_str()); + mqttClient.publish((roomsTopic + "/temperature").c_str(), 0, 1, String(temperature, 1).c_str()); + + gotNewTemperature = false; + } + } + + bool SendDiscovery() + { + if (!dht11Pin && !dht22Pin) return true; + + return sendSensorDiscovery("Temperature", "", "temperature", "°C") && sendSensorDiscovery("Humidity", "", "humidity", "%"); + } + + bool Command(String& command, String& pay) + { + return false; + } + + bool SendOnline() + { + return true; + } +} +#endif diff --git a/src/DHT.h b/src/DHT.h new file mode 100644 index 0000000..eae4af0 --- /dev/null +++ b/src/DHT.h @@ -0,0 +1,15 @@ +#pragma once +#ifdef SENSORS +#include + +namespace DHT +{ + void Setup(); + void ConnectToWifi(); + void SerialReport(); + void Loop(); + bool SendDiscovery(); + bool SendOnline(); + bool Command(String& command, String& pay); +} +#endif diff --git a/src/HX711.cpp b/src/HX711.cpp index 867e555..f553f33 100644 --- a/src/HX711.cpp +++ b/src/HX711.cpp @@ -1,20 +1,13 @@ #ifdef SENSORS - -#include "defaults.h" #include "HX711.h" + +#include "globals.h" +#include "mqtt.h" +#include "defaults.h" #include #include - -// for #define ESPMAC #include "string_utils.h" -// TODO: Not a fan of externs, but this helps refactoring for now -extern char buffer[2048]; -extern String room; -extern String roomsTopic; -extern void commonDiscovery(); -extern bool pub(const char *topic, uint8_t qos, bool retain, const char *payload, size_t length = 0, bool dup = false, uint16_t message_id = 0); - namespace HX711 { int sckPin = 0; @@ -45,7 +38,7 @@ namespace HX711 Serial.println(String(sckPin) + "/" + String(doutPin)); } - void Loop(AsyncMqttClient& mqttClient) + void Loop() { if (!sckPin && !doutPin) return; if (millis() - lastMillis < sensorInterval) return; @@ -83,26 +76,10 @@ namespace HX711 pub((roomsTopic + "/raw_weight").c_str(), 0, true, String(data).c_str()); } - static bool SendWeight(DynamicJsonDocument& doc) - { - commonDiscovery(); - doc["~"] = roomsTopic; - doc["name"] = "ESPresense " + room + " Raw Weight"; - doc["uniq_id"] = Sprintf("espresense_%06lx_raw_weight", CHIPID); - doc["avty_t"] = "~/status"; - doc["stat_t"] = "~/raw_weight"; - doc["frc_upd"] = true; - - serializeJson(doc, buffer); - String discoveryTopic = "homeassistant/sensor/espresense_" + ESPMAC + "/raw_weight/config"; - - return pub(discoveryTopic.c_str(), 0, true, buffer); - } - - bool SendDiscovery(DynamicJsonDocument& doc) + bool SendDiscovery() { if (!sckPin && !doutPin) return true; - return SendWeight(doc); + return sendSensorDiscovery("Raw Weight", "", "", ""); } } diff --git a/src/HX711.h b/src/HX711.h index 43a57b6..022f33a 100644 --- a/src/HX711.h +++ b/src/HX711.h @@ -1,26 +1,14 @@ +#pragma once #ifdef SENSORS - -#pragma once - #include -// Forward declares -class AsyncMqttClient; - namespace HX711 { - - enum HX711Gain { - HX711_GAIN_128 = 1, - HX711_GAIN_32 = 2, - HX711_GAIN_64 = 3, - }; - void Setup(); void ConnectToWifi(); void SerialReport(); - void Loop(AsyncMqttClient& mqttClient); - bool SendDiscovery(DynamicJsonDocument& doc); + void Loop(); + bool SendDiscovery(); } #endif diff --git a/src/MotionSensors.cpp b/src/MotionSensors.cpp index 640148d..aa76b02 100644 --- a/src/MotionSensors.cpp +++ b/src/MotionSensors.cpp @@ -1,21 +1,12 @@ #include "MotionSensors.h" #include #include - -// for #define ESPMAC +#include "globals.h" +#include "mqtt.h" #include "string_utils.h" #include "defaults.h" #include "GUI.h" -// TODO: Not a fan of externs, but this helps refactoring for now -extern char buffer[2048]; -extern String room; -extern String roomsTopic; -extern void commonDiscovery(); -extern bool sendNumberDiscovery(const String& name, const String& entityCategory); -extern bool pub(const char *topic, uint8_t qos, bool retain, const char *payload, size_t length = 0, bool dup = false, uint16_t message_id = 0); -extern bool spurt(const String& fn, const String& content); - namespace Motion { int lastMotionValue = -1; @@ -54,7 +45,7 @@ namespace Motion Serial.println(radarPin ? "enabled" : "disabled"); } - static void PirLoop(AsyncMqttClient& mqttClient) + static void pirLoop() { if (!pirPin) return; bool detected = digitalRead(pirPin) == HIGH; @@ -68,7 +59,7 @@ namespace Motion lastPirValue = pirValue; } - static void RadarLoop(AsyncMqttClient& mqttClient) + static void radarLoop() { if (!radarPin) return; bool detected = digitalRead(radarPin) == HIGH; @@ -82,34 +73,23 @@ namespace Motion lastRadarValue = radarValue; } - void Loop(AsyncMqttClient& mqttClient) + void Loop() { - PirLoop(mqttClient); - RadarLoop(mqttClient); + pirLoop(); + radarLoop(); int motionValue = (lastRadarValue == HIGH || lastPirValue == HIGH) ? HIGH : LOW; if (lastMotionValue == motionValue) return; mqttClient.publish((roomsTopic + "/motion").c_str(), 0, true, motionValue == HIGH ? "ON" : "OFF"); lastMotionValue = motionValue; } - bool SendDiscovery(DynamicJsonDocument& doc) + bool SendDiscovery() { - if (!pirPin && !radarPin) - return true; + if (!pirPin && !radarPin) return true; - commonDiscovery(); - doc["~"] = roomsTopic; - doc["name"] = "ESPresense " + room + " Motion"; - doc["uniq_id"] = Sprintf("espresense_%06lx_motion", CHIPID); - doc["avty_t"] = "~/status"; - doc["stat_t"] = "~/motion"; - doc["dev_cla"] = "motion"; - - serializeJson(doc, buffer); - String discoveryTopic = "homeassistant/binary_sensor/espresense_" + ESPMAC + "/motion/config"; - - if (!pub(discoveryTopic.c_str(), 0, true, buffer)) return false; - return (!pirPin || sendNumberDiscovery("Pir Timeout", EC_CONFIG)) && (!radarPin || sendNumberDiscovery("Radar Timeout", EC_CONFIG)); + if (pirPin && !sendNumberDiscovery("Pir Timeout", EC_CONFIG)) return false; + if (radarPin && !sendNumberDiscovery("Radar Timeout", EC_CONFIG)) return false; + return sendSensorDiscovery("Motion", "", "", "motion"); } bool Command(String& command, String& pay) @@ -128,7 +108,7 @@ namespace Motion return true; } - bool SendOnline(DynamicJsonDocument& doc) + bool SendOnline() { return pub((roomsTopic + "/pir_timeout").c_str(), 0, true, String(pirTimeout).c_str()) && pub((roomsTopic + "/radar_timeout").c_str(), 0, true, String(radarTimeout).c_str()); diff --git a/src/MotionSensors.h b/src/MotionSensors.h index 45ee77f..a7994b6 100644 --- a/src/MotionSensors.h +++ b/src/MotionSensors.h @@ -1,17 +1,13 @@ -#pragma once - -#include - -// Forward declares -class AsyncMqttClient; +#pragma once +#include namespace Motion { void Setup(); void ConnectToWifi(); void SerialReport(); - void Loop(AsyncMqttClient& mqttClient); - bool SendDiscovery(DynamicJsonDocument& doc); - bool SendOnline(DynamicJsonDocument& doc); + void Loop(); + bool SendDiscovery(); + bool SendOnline(); bool Command(String& command, String& pay); } diff --git a/src/TSL2561Sensor.cpp b/src/TSL2561Sensor.cpp index 9996061..f0fcb09 100644 --- a/src/TSL2561Sensor.cpp +++ b/src/TSL2561Sensor.cpp @@ -1,31 +1,21 @@ #ifdef SENSORS +#include "globals.h" #include "defaults.h" +#include "mqtt.h" #include "TSL2561Sensor.h" #include #include #include - -// for #define ESPMAC #include "string_utils.h" -// TODO: Not a fan of externs, but this helps refactoring for now -extern bool I2C_Bus_1_Enabled; -extern bool I2C_Bus_2_Enabled; -extern unsigned long sensorInterval; - -extern char buffer[2048]; -extern String room; -extern String roomsTopic; -extern void commonDiscovery(); -extern bool pub(const char *topic, uint8_t qos, bool retain, const char *payload, size_t length = 0, bool dup = false, uint16_t message_id = 0); - namespace TSL2561 { String TSL2561_I2c; int TSL2561_I2c_Bus; String TSL2561_I2c_Gain; unsigned long tsl2561PreviousMillis = 0; + int sensorInterval = 60000; void Setup() { @@ -45,7 +35,7 @@ namespace TSL2561 Serial.println(TSL2561_I2c + " on bus " + TSL2561_I2c_Bus); } - void Loop(AsyncMqttClient& mqttClient) + void Loop() { if (!I2C_Bus_1_Enabled && !I2C_Bus_2_Enabled) return; @@ -97,24 +87,11 @@ namespace TSL2561 } } - bool SendDiscovery(DynamicJsonDocument& doc) + bool SendDiscovery() { if (TSL2561_I2c.isEmpty()) return true; - commonDiscovery(); - doc["~"] = roomsTopic; - doc["name"] = "ESPresense " + room + " TSL2561 Lux"; - doc["uniq_id"] = Sprintf("espresense_%06lx_tsl2561_lux", CHIPID); - doc["avty_t"] = "~/status"; - doc["stat_t"] = "~/tsl2561_lux"; - doc["dev_cla"] = "illuminance"; - doc["unit_of_meas"] = "lx"; - doc["frc_upd"] = true; - - serializeJson(doc, buffer); - String discoveryTopic = "homeassistant/sensor/espresense_" + ESPMAC + "/tsl2561_lux/config"; - - return pub(discoveryTopic.c_str(), 0, true, buffer); + return sendSensorDiscovery("TSL2561 Lux", "", "illuminance", "lx"); } } diff --git a/src/TSL2561Sensor.h b/src/TSL2561Sensor.h index 2bddcd5..ec6bf68 100644 --- a/src/TSL2561Sensor.h +++ b/src/TSL2561Sensor.h @@ -1,19 +1,14 @@ -#ifdef SENSORS - -#pragma once - -#include - -// Forward declares -class AsyncMqttClient; - -namespace TSL2561 -{ - void Setup(); - void ConnectToWifi(); - void SerialReport(); - void Loop(AsyncMqttClient& mqttClient); - bool SendDiscovery(DynamicJsonDocument& doc); -} - -#endif \ No newline at end of file +#pragma once +#ifdef SENSORS +#include + +namespace TSL2561 +{ + void Setup(); + void ConnectToWifi(); + void SerialReport(); + void Loop(); + bool SendDiscovery(); +} + +#endif diff --git a/src/globals.h b/src/globals.h new file mode 100644 index 0000000..efa4778 --- /dev/null +++ b/src/globals.h @@ -0,0 +1,41 @@ +#pragma once +#include +#include +#include + +/*---------------------------------------------------------------------------- +globals.h + +Note: #define VAR_DECLS 1 before including this file to DECLARE and INITIALIZE +global variables. Include this file without defining VAR_DECLS to extern +these variables. +----------------------------------------------------------------------------*/ + +/*---------------------------------------------- +Setup variable declaration macros. +----------------------------------------------*/ +#ifndef VAR_DECLS +# define _DECL extern +# define _INIT(x) +# define _INIT_N(x) +#else +# define _DECL +# define _INIT(x) = x +# define UNPACK( ... ) __VA_ARGS__ +# define _INIT_N(x) UNPACK x +#endif + +_DECL char buffer[2048]; +_DECL String room, id, statusTopic, teleTopic, roomsTopic, setTopic; +_DECL AsyncMqttClient mqttClient; +_DECL DynamicJsonDocument doc _INIT_N(((2048))); +_DECL String localIp; + +// I2C +_DECL int I2C_Bus_1_SDA; +_DECL int I2C_Bus_1_SCL; +_DECL int I2C_Bus_2_SDA; +_DECL int I2C_Bus_2_SCL; +_DECL bool I2CDebug; +_DECL bool I2C_Bus_1_Enabled; +_DECL bool I2C_Bus_2_Enabled; diff --git a/src/main.cpp b/src/main.cpp index 6d6b098..b5dcfb7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,14 +1,12 @@ +#define VAR_DECLS #include "main.h" -#include "Network.h" -#include "MotionSensors.h" - bool sendTelemetry(int totalSeen, int totalFpSeen, int totalFpQueried, int totalFpReported, int count) { if (!online) { if (pub(statusTopic.c_str(), 0, true, "online") && pub((roomsTopic + "/max_distance").c_str(), 0, true, String(BleFingerprintCollection::maxDistance).c_str()) && pub((roomsTopic + "/absorption").c_str(), 0, true, String(BleFingerprintCollection::absorption).c_str()) && pub((roomsTopic + "/query").c_str(), 0, true, BleFingerprintCollection::query.c_str()) && pub((roomsTopic + "/include").c_str(), 0, true, BleFingerprintCollection::include.c_str()) && pub((roomsTopic + "/exclude").c_str(), 0, true, BleFingerprintCollection::exclude.c_str()) && pub((roomsTopic + "/known_macs").c_str(), 0, true, BleFingerprintCollection::knownMacs.c_str()) && pub((roomsTopic + "/count_ids").c_str(), 0, true, BleFingerprintCollection::countIds.c_str()) && pub((roomsTopic + "/status_led").c_str(), 0, true, String(GUI::statusLed ? "ON" : "OFF").c_str()) && pub((roomsTopic + "/arduino_ota").c_str(), 0, true, String(arduinoOta ? "ON" : "OFF").c_str()) && - pub((roomsTopic + "/auto_update").c_str(), 0, true, String(autoUpdate ? "ON" : "OFF").c_str()) && pub((roomsTopic + "/prerelease").c_str(), 0, true, String(prerelease ? "ON" : "OFF").c_str()) && pub((roomsTopic + "/active_scan").c_str(), 0, true, String(activeScan ? "ON" : "OFF").c_str()) && Motion::SendOnline(doc)) + pub((roomsTopic + "/auto_update").c_str(), 0, true, String(autoUpdate ? "ON" : "OFF").c_str()) && pub((roomsTopic + "/prerelease").c_str(), 0, true, String(prerelease ? "ON" : "OFF").c_str()) && pub((roomsTopic + "/active_scan").c_str(), 0, true, String(activeScan ? "ON" : "OFF").c_str()) && Motion::SendOnline()) { online = true; reconnectTries = 0; @@ -21,7 +19,7 @@ bool sendTelemetry(int totalSeen, int totalFpSeen, int totalFpQueried, int total if (discovery && !sentDiscovery) { - if (sendDiscoveryConnectivity() + if (sendConnectivityDiscovery() && sendTeleSensorDiscovery("Uptime", EC_DIAGNOSTIC, "{{ value_json.uptime }}", "s") && sendTeleSensorDiscovery("Free Mem", EC_DIAGNOSTIC, "{{ value_json.freeHeap }}", "bytes") && (BleFingerprintCollection::countIds.isEmpty() ? sendDeleteDiscovery("sensor", "Count") : sendTeleSensorDiscovery("Count", "", "{{ value_json.count }}", "")) @@ -33,19 +31,17 @@ bool sendTelemetry(int totalSeen, int totalFpSeen, int totalFpQueried, int total && sendSwitchDiscovery("Auto Update", EC_CONFIG) && sendSwitchDiscovery("Arduino OTA", EC_CONFIG) && sendSwitchDiscovery("Prerelease", EC_CONFIG) - && sendDeleteDiscovery("switch", "OTA Update") - && Motion::SendDiscovery(doc) + && Motion::SendDiscovery() #ifdef MACCHINA_A0 && sendTeleSensorDiscovery("Battery", "", "{{ value_json.batt }}", "%", "battery") && sendTeleBinarySensorDiscovery("Charging", "", "{{ value_json.charging }}", "battery_charging") #endif #ifdef SENSORS - && sendDiscoveryHumidity() - && sendDiscoveryTemperature() - && sendDiscoveryLux() - && BME280::SendDiscovery(doc) - && TSL2561::SendDiscovery(doc) - && HX711::SendDiscovery(doc) + && DHT::SendDiscovery() + && BH1750::SendDiscovery() + && BME280::SendDiscovery() + && TSL2561::SendDiscovery() + && HX711::SendDiscovery() #endif ) { @@ -199,10 +195,9 @@ void setupNetwork() GUI::statusLed = WiFiSettings.checkbox("status_led", true, "Status LED"); Motion::ConnectToWifi(); + #ifdef SENSORS - dht11Pin = WiFiSettings.integer("dht11_pin", 0, "DHT11 sensor pin (0 for disable)"); - dht22Pin = WiFiSettings.integer("dht22_pin", 0, "DHT22 sensor pin (0 for disable)"); - dhtTempOffset = WiFiSettings.floating("dhtTemp_offset", -40, 125, 0.0, "DHT temperature offset"); + DHT::ConnectToWifi(); WiFiSettings.heading("I2C Settings ℹ️", false); @@ -219,14 +214,9 @@ void setupNetwork() WiFiSettings.heading("I2C Sensors ℹ️", false); - WiFiSettings.html("h4", "BH1750 - Ambient Light Sensor:"); - BH1750_I2c_Bus = WiFiSettings.integer("BH1750_I2c_Bus", 1, 2, DEFAULT_I2C_BUS, "I2C Bus"); - BH1750_I2c = WiFiSettings.string("BH1750_I2c", "", "I2C address (0x23 or 0x5C)"); - + BH1750::ConnectToWifi(); BME280::ConnectToWifi(); - TSL2561::ConnectToWifi(); - HX711::ConnectToWifi(); #endif @@ -257,14 +247,8 @@ void setupNetwork() Serial.printf("Max Distance: %.2f\n", BleFingerprintCollection::maxDistance); Motion::SerialReport(); #ifdef SENSORS - Serial.print("DHT11 Sensor: "); - Serial.println(dht11Pin ? "enabled" : "disabled"); - Serial.print("DHT22 Sensor: "); - Serial.println(dht22Pin ? "enabled" : "disabled"); - Serial.print("DHT Temp Offset: "); - Serial.println(dhtTempOffset ? "enabled" : "disabled"); - Serial.print("BH1750_I2c Sensor: "); - Serial.println(BH1750_I2c + " on bus " + BH1750_I2c_Bus); + DHT::SerialReport(); + BH1750::SerialReport(); BME280::SerialReport(); TSL2561::SerialReport(); HX711::SerialReport(); @@ -546,40 +530,7 @@ void scanTask(void *parameter) } #ifdef SENSORS -/** - * Task to reads temperature from DHT11 sensor - * @param pvParameters - * pointer to task parameters - */ -void tempTask(void *pvParameters) -{ - Serial.println("tempTask loop started"); - while (1) // tempTask loop - { - if (dhtTasksEnabled && !gotNewTemperature) - { - // Read temperature only if old data was processed already - // Reading temperature for humidity takes about 250 milliseconds! - // Sensor readings may also be up to 2 seconds 'old' (it's a very slow sensor) - dhtSensorData = dhtSensor.getTempAndHumidity(); // Read values from sensor 1 - gotNewTemperature = true; - } - vTaskSuspend(NULL); - } -} -/** - * triggerGetTemp - * Sets flag dhtUpdated to true for handling in loop() - * called by Ticker tempTicker - */ -void triggerGetTemp() -{ - if (dhtTempTaskHandle != NULL) - { - xTaskResumeFromISR(dhtTempTaskHandle); - } -} #endif void setup() @@ -609,34 +560,7 @@ void setup() pinMode(GPIO_NUM_35, INPUT); #endif #ifdef SENSORS - if (dht11Pin) dhtSensor.setup(dht11Pin, DHTesp::DHT11); - if (dht22Pin) dhtSensor.setup(dht22Pin, DHTesp::DHT22); //(AM2302) - - if (dht11Pin || dht22Pin) - { - // Start task to get temperature - xTaskCreatePinnedToCore( - tempTask, /* Function to implement the task */ - "tempTask ", /* Name of the task */ - 4000, /* Stack size in words */ - NULL, /* Task input parameter */ - 5, /* Priority of the task */ - &dhtTempTaskHandle, /* Task handle. */ - 1); /* Core where the task should run */ - - if (dhtTempTaskHandle == NULL) - { - Serial.println("[ERROR] Failed to start task for temperature update"); - } - else - { - // Start update of environment data every 10 seconds - tempTicker.attach(dhtUpdateTime, triggerGetTemp); - } - - // Signal end of setup() to tasks - dhtTasksEnabled = true; - } + DHT::Setup(); if (I2C_Bus_1_SDA != 0 && I2C_Bus_1_SDA != 0) { Wire.begin(I2C_Bus_1_SDA, I2C_Bus_1_SCL); @@ -655,64 +579,7 @@ void setup() Serial.println("\nI2C Scanner"); } - - if (I2C_Bus_1_Enabled || I2C_Bus_2_Enabled) { - // BH1750_I2c - // BH1750_updateFr - if (BH1750_I2c == "0x23" || BH1750_I2c == "0x5C") - { - // Init BH1750 (witch default l2c adres) - int rc; // Returncode - unsigned long m; // milli for calibration - bool state = false; - - // if (! BH1750.begin(BH1750_TO_GROUND)) - if (BH1750_I2c == "0x23" && BH1750_I2c_Bus == 1) - { - state = BH1750.begin(BH1750_TO_GROUND, &Wire); - } - else if (BH1750_I2c == "0x5C" && BH1750_I2c_Bus == 1) - { - state = BH1750.begin(BH1750_TO_VCC, &Wire); - } - else if (BH1750_I2c == "0x23" && BH1750_I2c_Bus == 2) - { - state = BH1750.begin(BH1750_TO_GROUND, &Wire1); - } - else if (BH1750_I2c == "0x5C" && BH1750_I2c_Bus == 2) - { - state = BH1750.begin(BH1750_TO_VCC, &Wire1); - } - - if (!state) - { - Serial.println("Error on initialisation BH1750 GY-302"); - } - else - { - sendDiscoveryLux(); - Serial.println("initialisation BH1750 GY-302 success"); - m = millis(); - rc = BH1750.calibrateTiming(); - m = millis() - m; - Serial.print("Lux Sensor BH1750 GY-302 calibrated (Time: "); - Serial.print(m); - Serial.print(" ms)"); - if (rc != 0) - { - Serial.print(" - Lux Sensor Error code "); - Serial.print(rc); - } - Serial.println(); - - // start first measure and timecount - lux_BH1750 = -1; // nothing to compare - BH1750.start(BH1750_QUALITY_HIGH, 1); - ms_BH1750 = millis(); - } - } - } - + BH1750::Setup(); //BME280::Setup(); //TSL2561::Setup(); HX711::Setup(); @@ -723,72 +590,9 @@ void setup() } #ifdef SENSORS -void dhtLoop() -{ - if (!dht11Pin && !dht22Pin) return; - if (gotNewTemperature) - { - float humidity = dhtSensorData.humidity; - float temperature = dhtSensorData.temperature + dhtTempOffset; - Serial.println("Temp: " + String(temperature, 1) + "'C Humidity: " + String(humidity, 1) + "%"); - - mqttClient.publish((roomsTopic + "/humidity").c_str(), 0, 1, String(humidity, 1).c_str()); - mqttClient.publish((roomsTopic + "/temperature").c_str(), 0, 1, String(temperature, 1).c_str()); - - gotNewTemperature = false; - } -} //non blocking ambient sensor -void luxLoop() -{ - if (I2C_Bus_1_Enabled || I2C_Bus_2_Enabled) { - - if (BH1750_I2c == "0x23" || BH1750_I2c == "0x5C") - { - - float lux; - int lux_mqtt; - - if (BH1750.hasValue()) - { - ms_BH1750 = millis() - ms_BH1750; - if (!BH1750.saturated()) - { - lux = BH1750.getLux(); - lux_mqtt = int(lux); - - if (lux != lux_BH1750) - { - lux_BH1750 = lux; - // Serial.print("BH1750 ("); - // Serial.print(ms_BH1750); - // Serial.print(" ms): "); - // Serial.print(lux); - // Serial.println(" lx"); - } - - //convert lx to integer to reduce mqtt traffic, send only if lx changed - if (lux_mqtt != lux_BH1750_MQTT) - { - lux_BH1750_MQTT = lux_mqtt; - Serial.print("BH1750 ("); - Serial.print(ms_BH1750); - Serial.print(" ms): "); - Serial.print(lux_mqtt); - Serial.println(" lx"); - mqttClient.publish((roomsTopic + "/lux").c_str(), 0, 1, String(lux_mqtt).c_str()); - } - } - - BH1750.adjustSettings(90); - BH1750.start(); - ms_BH1750 = millis(); - } - } - } -} void l2cScanner() { @@ -876,13 +680,13 @@ void loop() ArduinoOTA.handle(); if (freeHeap < 10000) Serial.printf("Low memory: %u bytes free", freeHeap); firmwareUpdate(); - Motion::Loop(mqttClient); + Motion::Loop(); #ifdef SENSORS - dhtLoop(); - luxLoop(); - BME280::Loop(mqttClient); - TSL2561::Loop(mqttClient); - HX711::Loop(mqttClient); + DHT::Loop(); + BH1750::Loop(); + BME280::Loop(); + TSL2561::Loop(); + HX711::Loop(); l2cScanner(); #endif WiFiSettings.httpLoop(); diff --git a/src/main.h b/src/main.h index ab23dd4..e011b5f 100644 --- a/src/main.h +++ b/src/main.h @@ -1,70 +1,44 @@ #include "BleFingerprint.h" #include "BleFingerprintCollection.h" +#include "globals.h" +#include "mqtt.h" #include "GUI.h" #include "defaults.h" #include "string_utils.h" #include -#include #include -#include #include - #include #include #include #include -#include #include + #include #include #include #include #include #include +#include "Network.h" +#include "MotionSensors.h" #ifdef SENSORS -#include #include -// I2C -int I2C_Bus_1_SDA; -int I2C_Bus_1_SCL; -int I2C_Bus_2_SDA; -int I2C_Bus_2_SCL; -bool I2CDebug; -bool I2C_Bus_1_Enabled; -bool I2C_Bus_2_Enabled; -unsigned long sensorInterval = 60000; - -//GY-302 lux sensor -#include -hp_BH1750 BH1750; -unsigned long ms_BH1750; -float lux_BH1750; -int lux_BH1750_MQTT; -String BH1750_I2c; -int BH1750_I2c_Bus; - -//I2C BME280 sensor #include "BME280Sensor.h" - -//I2C TSL2561 sensor #include "TSL2561Sensor.h" - #include "HX711.h" +#include "DHT.h" +#include "BH1750.h" #endif -AsyncMqttClient mqttClient; TimerHandle_t reconnectTimer; TaskHandle_t scanTaskHandle, reportTaskHandle; -DynamicJsonDocument doc(2048); -char buffer[2048]; - bool updateInProgress = false; -String localIp; unsigned long lastTeleMillis; int reconnectTries = 0; int teleFails = 0; @@ -75,38 +49,10 @@ String offline = "offline"; int ethernetType = 0; String mqttHost, mqttUser, mqttPass; uint16_t mqttPort; -String room, id, statusTopic, teleTopic, roomsTopic, setTopic; bool autoUpdate, arduinoOta, prerelease; bool discovery, activeScan, publishTele, publishRooms, publishDevices; -#ifdef SENSORS - -uint8_t dht11Pin; -uint8_t dht22Pin; -float dhtTempOffset; - -/** Initialize DHT sensor 1 */ -DHTesp dhtSensor; - -/** Task handle for the light value read task */ -TaskHandle_t dhtTempTaskHandle = NULL; - -/** Ticker for temperature reading */ -Ticker tempTicker; - -/** Flags for temperature readings finished */ -bool gotNewTemperature = false; - -/** Data from dht sensor 1 */ -TempAndHumidity dhtSensorData; - -/* Flag if main loop is running */ -bool dhtTasksEnabled = false; - -/* update time */ -int dhtUpdateTime = 10; //ToDo: maybe make this a user choise via settings menu -#endif BleFingerprintCollection fingerprints; String resetReason(RESET_REASON reason) @@ -314,226 +260,6 @@ void spiffsInit() SPIFFS.begin(true); } -bool pub(const char *topic, uint8_t qos, bool retain, const char *payload, size_t length = 0, bool dup = false, uint16_t message_id = 0) -{ - for (int i = 0; i < 10; i++) - { - if (mqttClient.publish(topic, qos, retain, payload, length, dup, message_id)) - return true; - delay(50); - } - return false; -} - -void commonDiscovery() -{ - doc.clear(); - auto identifiers = doc["dev"].createNestedArray("ids"); - identifiers.add(Sprintf("espresense_%06" PRIx64, CHIPID)); - auto connections = doc["dev"].createNestedArray("cns"); - auto mac = connections.createNestedArray(); - mac.add("mac"); - mac.add(WiFi.macAddress()); - doc["dev"]["name"] = "ESPresense " + room; - doc["dev"]["sa"] = room; -#ifdef VERSION - doc["dev"]["sw"] = VERSION; -#endif -#ifdef FIRMWARE - doc["dev"]["mf"] = "ESPresense (" FIRMWARE ")"; -#endif - doc["dev"]["cu"] = "http://" + localIp; - doc["dev"]["mdl"] = String(ESP.getChipModel()); -} - -bool sendDiscoveryConnectivity() -{ - commonDiscovery(); - doc["~"] = roomsTopic; - doc["name"] = "ESPresense " + room; - doc["uniq_id"] = Sprintf("espresense_%06lx_connectivity", CHIPID); - doc["json_attr_t"] = "~/telemetry"; - doc["stat_t"] = "~/status"; - doc["dev_cla"] = "connectivity"; - doc["pl_on"] = "online"; - doc["pl_off"] = "offline"; - - serializeJson(doc, buffer); - String discoveryTopic = "homeassistant/binary_sensor/espresense_" + ESPMAC + "/connectivity/config"; - - return pub(discoveryTopic.c_str(), 0, true, buffer); -} - -bool sendTeleBinarySensorDiscovery(const String &name, const String &entityCategory, const String &temp, const String &devClass) -{ - auto slug = slugify(name); - - commonDiscovery(); - doc["~"] = roomsTopic; - doc["name"] = Sprintf("ESPresense %s %s", room.c_str(), name.c_str()); - doc["uniq_id"] = Sprintf("espresense_%06lx_%s", CHIPID, slug.c_str()); - doc["avty_t"] = "~/status"; - doc["stat_t"] = "~/telemetry"; - if (!entityCategory.isEmpty()) doc["entity_category"] = entityCategory; - doc["value_template"] = temp; - if (!devClass.isEmpty()) doc["dev_cla"] = devClass; - serializeJson(doc, buffer); - String discoveryTopic = "homeassistant/binary_sensor/espresense_" + ESPMAC + "/" + slug + "/config"; - - return pub(discoveryTopic.c_str(), 0, true, buffer); -} - -bool sendTeleSensorDiscovery(const String &name, const String &entityCategory, const String &temp, const String &units, const String &devClass = "") -{ - auto slug = slugify(name); - - commonDiscovery(); - doc["~"] = roomsTopic; - doc["name"] = Sprintf("ESPresense %s %s", room.c_str(), name.c_str()); - doc["uniq_id"] = Sprintf("espresense_%06lx_%s", CHIPID, slug.c_str()); - doc["avty_t"] = "~/status"; - doc["stat_t"] = "~/telemetry"; - if (!entityCategory.isEmpty()) doc["entity_category"] = entityCategory; - doc["value_template"] = temp; - if (!units.isEmpty()) doc["unit_of_measurement"] = units; - if (!devClass.isEmpty()) doc["dev_cla"] = devClass; - serializeJson(doc, buffer); - String discoveryTopic = "homeassistant/sensor/espresense_" + ESPMAC + "/" + slug + "/config"; - - return pub(discoveryTopic.c_str(), 0, true, buffer); -} - -#ifdef SENSORS -bool sendDiscoveryTemperature() -{ - if (!dht11Pin && !dht22Pin) return true; - - commonDiscovery(); - doc["~"] = roomsTopic; - doc["name"] = "ESPresense " + room + " Temperature"; - doc["uniq_id"] = Sprintf("espresense_%06lx_temperature", CHIPID); - doc["avty_t"] = "~/status"; - doc["stat_t"] = "~/temperature"; - doc["dev_cla"] = "temperature"; - doc["unit_of_meas"] = "°C"; - doc["frc_upd"] = true; - - serializeJson(doc, buffer); - String discoveryTopic = "homeassistant/sensor/espresense_" + ESPMAC + "/temperature/config"; - return pub(discoveryTopic.c_str(), 0, true, buffer); -} - -bool sendDiscoveryHumidity() -{ - if (!dht11Pin && !dht22Pin) return true; - - commonDiscovery(); - doc["~"] = roomsTopic; - doc["name"] = "ESPresense " + room + " Humidity"; - doc["uniq_id"] = Sprintf("espresense_%06lx_humidity", CHIPID); - doc["avty_t"] = "~/status"; - doc["stat_t"] = "~/humidity"; - doc["dev_cla"] = "humidity"; - doc["unit_of_meas"] = "%"; - doc["frc_upd"] = true; - - serializeJson(doc, buffer); - String discoveryTopic = "homeassistant/sensor/espresense_" + ESPMAC + "/humidity/config"; - return pub(discoveryTopic.c_str(), 0, true, buffer); -} - -bool sendDiscoveryLux() -{ - if (BH1750_I2c.isEmpty()) return true; - - commonDiscovery(); - doc["~"] = roomsTopic; - doc["name"] = "ESPresense " + room + " Lux"; - doc["uniq_id"] = Sprintf("espresense_%06lx_lux", CHIPID); - doc["avty_t"] = "~/status"; - doc["stat_t"] = "~/lux"; - doc["dev_cla"] = "illuminance"; - doc["unit_of_meas"] = "lx"; - doc["frc_upd"] = true; - - char buffer[1200]; - serializeJson(doc, buffer); - String discoveryTopic = "homeassistant/sensor/espresense_" + ESPMAC + "/lux/config"; - return pub(discoveryTopic.c_str(), 0, true, buffer); -} -#endif - -bool sendButtonDiscovery(const String &name, const String &entityCategory) -{ - auto slug = slugify(name); - - commonDiscovery(); - doc["~"] = roomsTopic; - doc["name"] = Sprintf("ESPresense %s %s", room.c_str(), name.c_str()); - doc["uniq_id"] = Sprintf("espresense_%06lx_%s", CHIPID, slug.c_str()); - doc["avty_t"] = "~/status"; - doc["stat_t"] = "~/" + slug; - doc["cmd_t"] = "~/" + slug + "/set"; - doc["entity_category"] = entityCategory; - - serializeJson(doc, buffer); - String discoveryTopic = "homeassistant/button/espresense_" + ESPMAC + "/" + slug + "/config"; - return pub(discoveryTopic.c_str(), 0, true, buffer); -} - -bool sendSwitchDiscovery(const String &name, const String &entityCategory) -{ - auto slug = slugify(name); - - commonDiscovery(); - doc["~"] = roomsTopic; - doc["name"] = Sprintf("ESPresense %s %s", room.c_str(), name.c_str()); - doc["uniq_id"] = Sprintf("espresense_%06lx_%s", CHIPID, slug.c_str()); - doc["avty_t"] = "~/status"; - doc["stat_t"] = "~/" + slug; - doc["cmd_t"] = "~/" + slug + "/set"; - doc["entity_category"] = entityCategory; - - serializeJson(doc, buffer); - String discoveryTopic = "homeassistant/switch/espresense_" + ESPMAC + "/" + slug + "/config"; - return pub(discoveryTopic.c_str(), 0, true, buffer); -} - -bool sendDeleteDiscovery(const String &domain, const String &name) -{ - auto slug = slugify(name); - String discoveryTopic = "homeassistant/" + domain + "/espresense_" + ESPMAC + "/" + slug + "/config"; - return pub(discoveryTopic.c_str(), 0, false, ""); -} - -bool sendNumberDiscovery(const String &name, const String &entityCategory) -{ - auto slug = slugify(name); - - commonDiscovery(); - doc["~"] = roomsTopic; - doc["name"] = Sprintf("ESPresense %s %s", room.c_str(), name.c_str()); - doc["uniq_id"] = Sprintf("espresense_%06lx_%s", CHIPID, slug.c_str()); - doc["avty_t"] = "~/status"; - doc["stat_t"] = "~/" + slug; - doc["cmd_t"] = "~/" + slug + "/set"; - doc["step"] = "0.1"; - doc["entity_category"] = entityCategory; - - serializeJson(doc, buffer); - String discoveryTopic = "homeassistant/number/espresense_" + ESPMAC + "/" + slug + "/config"; - return pub(discoveryTopic.c_str(), 0, true, buffer); -} - -bool spurt(const String &fn, const String &content) -{ - File f = SPIFFS.open(fn, "w"); - if (!f) return false; - auto w = f.print(content); - f.close(); - return w == content.length(); -} - #ifdef MACCHINA_A0 int smoothMilliVolts; diff --git a/src/mqtt.cpp b/src/mqtt.cpp new file mode 100644 index 0000000..b62acd8 --- /dev/null +++ b/src/mqtt.cpp @@ -0,0 +1,173 @@ +#include "globals.h" +#include "string_utils.h" +#include + +bool pub(const char *topic, uint8_t qos, bool retain, const char *payload, size_t length = 0, bool dup = false, uint16_t message_id = 0) +{ + for (int i = 0; i < 10; i++) + { + if (mqttClient.publish(topic, qos, retain, payload, length, dup, message_id)) + return true; + delay(50); + } + return false; +} + +void commonDiscovery() +{ + doc.clear(); + auto identifiers = doc["dev"].createNestedArray("ids"); + identifiers.add(Sprintf("espresense_%06lx", CHIPID)); + auto connections = doc["dev"].createNestedArray("cns"); + auto mac = connections.createNestedArray(); + mac.add("mac"); + mac.add(WiFi.macAddress()); + doc["dev"]["name"] = "ESPresense " + room; + doc["dev"]["sa"] = room; +#ifdef VERSION + doc["dev"]["sw"] = VERSION; +#endif +#ifdef FIRMWARE + doc["dev"]["mf"] = "ESPresense (" FIRMWARE ")"; +#endif + doc["dev"]["cu"] = "http://" + localIp; + doc["dev"]["mdl"] = String(ESP.getChipModel()); +} + +bool sendConnectivityDiscovery() +{ + commonDiscovery(); + doc["~"] = roomsTopic; + doc["name"] = "ESPresense " + room; + doc["uniq_id"] = Sprintf("espresense_%06lx_connectivity", CHIPID); + doc["json_attr_t"] = "~/telemetry"; + doc["stat_t"] = "~/status"; + doc["dev_cla"] = "connectivity"; + doc["pl_on"] = "online"; + doc["pl_off"] = "offline"; + + serializeJson(doc, buffer); + String discoveryTopic = Sprintf("homeassistant/binary_sensor/espresense_%06lx/connectivity/config", CHIPID); + return pub(discoveryTopic.c_str(), 0, true, buffer); +} + +bool sendTeleBinarySensorDiscovery(const String &name, const String &entityCategory, const String &temp, const String &devClass) +{ + auto slug = slugify(name); + + commonDiscovery(); + doc["~"] = roomsTopic; + doc["name"] = Sprintf("ESPresense %s %s", room.c_str(), name.c_str()); + doc["uniq_id"] = Sprintf("espresense_%06lx_%s", CHIPID, slug.c_str()); + doc["avty_t"] = "~/status"; + doc["stat_t"] = "~/telemetry"; + doc["value_template"] = temp; + if (!entityCategory.isEmpty()) doc["entity_category"] = entityCategory; + if (!devClass.isEmpty()) doc["dev_cla"] = devClass; + + serializeJson(doc, buffer); + String discoveryTopic = Sprintf("homeassistant/binary_sensor/espresense_%06lx/%s/config", CHIPID, slug.c_str()); + return pub(discoveryTopic.c_str(), 0, true, buffer); +} + +bool sendTeleSensorDiscovery(const String &name, const String &entityCategory, const String &temp, const String &units, const String &devClass) +{ + auto slug = slugify(name); + + commonDiscovery(); + doc["~"] = roomsTopic; + doc["name"] = Sprintf("ESPresense %s %s", room.c_str(), name.c_str()); + doc["uniq_id"] = Sprintf("espresense_%06lx_%s", CHIPID, slug.c_str()); + doc["avty_t"] = "~/status"; + doc["stat_t"] = "~/telemetry"; + doc["value_template"] = temp; + if (!entityCategory.isEmpty()) doc["entity_category"] = entityCategory; + if (!units.isEmpty()) doc["unit_of_meas"] = units; + if (!devClass.isEmpty()) doc["dev_cla"] = devClass; + + serializeJson(doc, buffer); + String discoveryTopic = Sprintf("homeassistant/sensor/espresense_%06lx/%s/config", CHIPID, slug.c_str()); + return pub(discoveryTopic.c_str(), 0, true, buffer); +} + +bool sendSensorDiscovery(const String &name, const String &entityCategory, const String &units, const String &devClass) +{ + auto slug = slugify(name); + + commonDiscovery(); + doc["~"] = roomsTopic; + doc["name"] = Sprintf("ESPresense %s %s", room.c_str(), name.c_str()); + doc["uniq_id"] = Sprintf("espresense_%06lx_%s", CHIPID, slug.c_str()); + doc["avty_t"] = "~/status"; + doc["stat_t"] = "~/" + slug; + if (!entityCategory.isEmpty()) doc["entity_category"] = entityCategory; + if (!units.isEmpty()) doc["unit_of_meas"] = units; + if (!devClass.isEmpty()) doc["dev_cla"] = devClass; + doc["frc_upd"] = true; + + serializeJson(doc, buffer); + String discoveryTopic = Sprintf("homeassistant/sensor/espresense_%06lx/%s/config", CHIPID, slug.c_str()); + return pub(discoveryTopic.c_str(), 0, true, buffer); +} + +bool sendButtonDiscovery(const String &name, const String &entityCategory) +{ + auto slug = slugify(name); + + commonDiscovery(); + doc["~"] = roomsTopic; + doc["name"] = Sprintf("ESPresense %s %s", room.c_str(), name.c_str()); + doc["uniq_id"] = Sprintf("espresense_%06lx_%s", CHIPID, slug.c_str()); + doc["avty_t"] = "~/status"; + doc["stat_t"] = "~/" + slug; + doc["cmd_t"] = "~/" + slug + "/set"; + doc["entity_category"] = entityCategory; + + serializeJson(doc, buffer); + String discoveryTopic = Sprintf("homeassistant/button/espresense_%06lx/%s/config", CHIPID, slug.c_str()); + return pub(discoveryTopic.c_str(), 0, true, buffer); +} + +bool sendSwitchDiscovery(const String &name, const String &entityCategory) +{ + auto slug = slugify(name); + + commonDiscovery(); + doc["~"] = roomsTopic; + doc["name"] = Sprintf("ESPresense %s %s", room.c_str(), name.c_str()); + doc["uniq_id"] = Sprintf("espresense_%06lx_%s", CHIPID, slug.c_str()); + doc["avty_t"] = "~/status"; + doc["stat_t"] = "~/" + slug; + doc["cmd_t"] = "~/" + slug + "/set"; + doc["entity_category"] = entityCategory; + + serializeJson(doc, buffer); + String discoveryTopic = Sprintf("homeassistant/switch/espresense_%06lx/%s/config", CHIPID, slug.c_str()); + return pub(discoveryTopic.c_str(), 0, true, buffer, 0); +} + +bool sendNumberDiscovery(const String &name, const String &entityCategory) +{ + auto slug = slugify(name); + + commonDiscovery(); + doc["~"] = roomsTopic; + doc["name"] = Sprintf("ESPresense %s %s", room.c_str(), name.c_str()); + doc["uniq_id"] = Sprintf("espresense_%06lx_%s", CHIPID, slug.c_str()); + doc["avty_t"] = "~/status"; + doc["stat_t"] = "~/" + slug; + doc["cmd_t"] = "~/" + slug + "/set"; + doc["step"] = "0.1"; + doc["entity_category"] = entityCategory; + + serializeJson(doc, buffer); + String discoveryTopic = Sprintf("homeassistant/number/espresense_%06lx/%s/config", CHIPID, slug.c_str()); + return pub(discoveryTopic.c_str(), 0, true, buffer); +} + +bool sendDeleteDiscovery(const String &domain, const String &name) +{ + auto slug = slugify(name); + String discoveryTopic = Sprintf("homeassistant/%s/espresense_%06lx/%s/config", domain, CHIPID, slug.c_str()); + return pub(discoveryTopic.c_str(), 0, false, ""); +} diff --git a/src/mqtt.h b/src/mqtt.h new file mode 100644 index 0000000..0b7b572 --- /dev/null +++ b/src/mqtt.h @@ -0,0 +1,17 @@ +#pragma once +#include + +bool pub(const char *topic, uint8_t qos, bool retain, const char *payload, size_t length = 0, bool dup = false, uint16_t message_id = 0); +void commonDiscovery(); + +bool sendConnectivityDiscovery(); + +bool sendTeleBinarySensorDiscovery(const String &name, const String &entityCategory, const String &temp, const String &devClass); +bool sendTeleSensorDiscovery(const String &name, const String &entityCategory, const String &temp, const String &units, const String &devClass = ""); + +bool sendSensorDiscovery(const String &name, const String &entityCategory, const String &units, const String &devClass); +bool sendButtonDiscovery(const String &name, const String &entityCategory); +bool sendSwitchDiscovery(const String &name, const String &entityCategory); +bool sendNumberDiscovery(const String &name, const String &entityCategory); + +bool sendDeleteDiscovery(const String &domain, const String &name);