Refactor globals/mqtt (#521)

This commit is contained in:
Darrell 2022-07-04 08:29:04 -04:00 committed by GitHub
parent 5f4cf7fa3c
commit cec2c719c3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 710 additions and 754 deletions

View File

@ -1,5 +1,6 @@
#include <regex>
#include <string_utils.h>
#include <SPIFFS.h>
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();
}

View File

@ -1,5 +1,4 @@
#pragma once
#pragma once
#include <Arduino.h>
#include <WString.h>
#include <string>
@ -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);

146
src/BH1750.cpp Normal file
View File

@ -0,0 +1,146 @@
#ifdef SENSORS
#include "BH1750.h"
#include "globals.h"
#include "mqtt.h"
#include "defaults.h"
#include <WiFiSettings.h>
#include <AsyncMqttClient.h>
#include "string_utils.h"
#include <hp_BH1750.h>
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

17
src/BH1750.h Normal file
View File

@ -0,0 +1,17 @@
#pragma once
#ifdef SENSORS
#include <ArduinoJson.h>
// Forward declares
class AsyncMqttClient;
namespace BH1750
{
void Setup();
void ConnectToWifi();
void SerialReport();
void Loop();
bool SendDiscovery();
}
#endif

View File

@ -1,24 +1,13 @@
#ifdef SENSORS
#include "defaults.h"
#include "BME280Sensor.h"
#include <WiFiSettings.h>
#include <AsyncMqttClient.h>
#include <Adafruit_BME280.h>
// for #define ESPMAC
#include "globals.h"
#include "mqtt.h"
#include "defaults.h"
#include <WiFiSettings.h>
#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 <Adafruit_BME280.h>
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");
}
}

View File

@ -1,19 +1,14 @@
#ifdef SENSORS
#pragma once
#include <ArduinoJson.h>
// Forward declares
class AsyncMqttClient;
namespace BME280
{
void Setup();
void ConnectToWifi();
void SerialReport();
void Loop(AsyncMqttClient& mqttClient);
bool SendDiscovery(DynamicJsonDocument& doc);
}
#endif
#pragma once
#ifdef SENSORS
#include <ArduinoJson.h>
namespace BME280
{
void Setup();
void ConnectToWifi();
void SerialReport();
void Loop();
bool SendDiscovery();
}
#endif

159
src/DHT.cpp Normal file
View File

@ -0,0 +1,159 @@
#ifdef SENSORS
#include "DHT.h"
#include "globals.h"
#include "mqtt.h"
#include "defaults.h"
#include <WiFiSettings.h>
#include <AsyncMqttClient.h>
#include "string_utils.h"
#include <DHTesp.h>
#include <Ticker.h>
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

15
src/DHT.h Normal file
View File

@ -0,0 +1,15 @@
#pragma once
#ifdef SENSORS
#include <ArduinoJson.h>
namespace DHT
{
void Setup();
void ConnectToWifi();
void SerialReport();
void Loop();
bool SendDiscovery();
bool SendOnline();
bool Command(String& command, String& pay);
}
#endif

View File

@ -1,20 +1,13 @@
#ifdef SENSORS
#include "defaults.h"
#include "HX711.h"
#include "globals.h"
#include "mqtt.h"
#include "defaults.h"
#include <WiFiSettings.h>
#include <AsyncMqttClient.h>
// 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", "", "", "");
}
}

View File

@ -1,26 +1,14 @@
#pragma once
#ifdef SENSORS
#pragma once
#include <ArduinoJson.h>
// 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

View File

@ -1,21 +1,12 @@
#include "MotionSensors.h"
#include <WiFiSettings.h>
#include <AsyncMqttClient.h>
// 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());

View File

@ -1,17 +1,13 @@
#pragma once
#include <ArduinoJson.h>
// Forward declares
class AsyncMqttClient;
#pragma once
#include <Arduino.h>
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);
}

View File

@ -1,31 +1,21 @@
#ifdef SENSORS
#include "globals.h"
#include "defaults.h"
#include "mqtt.h"
#include "TSL2561Sensor.h"
#include <WiFiSettings.h>
#include <AsyncMqttClient.h>
#include <Adafruit_TSL2561_U.h>
// 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");
}
}

View File

@ -1,19 +1,14 @@
#ifdef SENSORS
#pragma once
#include <ArduinoJson.h>
// Forward declares
class AsyncMqttClient;
namespace TSL2561
{
void Setup();
void ConnectToWifi();
void SerialReport();
void Loop(AsyncMqttClient& mqttClient);
bool SendDiscovery(DynamicJsonDocument& doc);
}
#endif
#pragma once
#ifdef SENSORS
#include <ArduinoJson.h>
namespace TSL2561
{
void Setup();
void ConnectToWifi();
void SerialReport();
void Loop();
bool SendDiscovery();
}
#endif

41
src/globals.h Normal file
View File

@ -0,0 +1,41 @@
#pragma once
#include <Arduino.h>
#include <AsyncMqttClient.h>
#include <ArduinoJson.h>
/*----------------------------------------------------------------------------
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;

View File

@ -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 <a href='https://espresense.com/configuration/settings#i2c-settings' target='_blank'></a>", false);
@ -219,14 +214,9 @@ void setupNetwork()
WiFiSettings.heading("I2C Sensors <a href='https://espresense.com/configuration/settings#i2c-sensors' target='_blank'></a>", 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();

View File

@ -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 <Arduino.h>
#include <ArduinoJson.h>
#include <ArduinoOTA.h>
#include <AsyncMqttClient.h>
#include <AsyncTCP.h>
#include <HTTPClient.h>
#include <HTTPUpdate.h>
#include <NimBLEDevice.h>
#include <SPIFFS.h>
#include <Ticker.h>
#include <WebServer.h>
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <WiFiSettings.h>
#include <freertos/FreeRTOS.h>
#include <freertos/timers.h>
#include <rom/rtc.h>
#include "Network.h"
#include "MotionSensors.h"
#ifdef SENSORS
#include <DHTesp.h>
#include <Wire.h>
// 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.h>
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;

173
src/mqtt.cpp Normal file
View File

@ -0,0 +1,173 @@
#include "globals.h"
#include "string_utils.h"
#include <WiFi.h>
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, "");
}

17
src/mqtt.h Normal file
View File

@ -0,0 +1,17 @@
#pragma once
#include <Arduino.h>
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);