Add support for BLE temp sensors

This commit is contained in:
Menno de Graaf
2023-12-08 10:32:58 +01:00
parent dbcca514b0
commit 5ecbddc929
6 changed files with 93 additions and 6 deletions

View File

@@ -50,6 +50,7 @@ platform = espressif32
lib_deps = lib_deps =
${env.lib_deps} ${env.lib_deps}
laxilef/ESP32Scheduler@^1.0.1 laxilef/ESP32Scheduler@^1.0.1
h2zero/NimBLE-Arduino@^1.4.1
lib_ignore = lib_ignore =
extra_scripts = extra_scripts =
post:tools/esp32.py post:tools/esp32.py
@@ -174,6 +175,7 @@ board = wemos_d1_mini32
lib_deps = ${esp32_defaults.lib_deps} lib_deps = ${esp32_defaults.lib_deps}
lib_ignore = ${esp32_defaults.lib_ignore} lib_ignore = ${esp32_defaults.lib_ignore}
extra_scripts = ${esp32_defaults.extra_scripts} extra_scripts = ${esp32_defaults.extra_scripts}
;board_build.partitions = huge_app.csv
build_flags = build_flags =
${esp32_defaults.build_flags} ${esp32_defaults.build_flags}
-D OT_IN_PIN_DEFAULT=21 -D OT_IN_PIN_DEFAULT=21

View File

@@ -29,16 +29,17 @@ public:
bool publishSelectIndoorSensorType(bool enabledByDefault = true) { bool publishSelectIndoorSensorType(bool enabledByDefault = true) {
StaticJsonDocument<1536> doc; StaticJsonDocument<1536> doc;
doc[FPSTR(HA_COMMAND_TOPIC)] = devicePrefix + F("/settings/set"); doc[FPSTR(HA_COMMAND_TOPIC)] = devicePrefix + F("/settings/set");
doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"sensors\": {\"indoor\": {\"type\": {% if value == 'Manual' %}1{% elif value == 'External' %}2{% endif %}}}}"); doc[FPSTR(HA_COMMAND_TEMPLATE)] = F("{\"sensors\": {\"indoor\": {\"type\": {% if value == 'Manual' %}1{% elif value == 'External' %}2{% elif value == 'Bluetooth' %}3{% endif %}}}}");
doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault; doc[FPSTR(HA_ENABLED_BY_DEFAULT)] = enabledByDefault;
doc[FPSTR(HA_UNIQUE_ID)] = devicePrefix + F("_indoor_sensor_type"); doc[FPSTR(HA_UNIQUE_ID)] = devicePrefix + F("_indoor_sensor_type");
doc[FPSTR(HA_OBJECT_ID)] = devicePrefix + F("_indoor_sensor_type"); doc[FPSTR(HA_OBJECT_ID)] = devicePrefix + F("_indoor_sensor_type");
doc[FPSTR(HA_ENTITY_CATEGORY)] = F("config"); doc[FPSTR(HA_ENTITY_CATEGORY)] = F("config");
doc[FPSTR(HA_NAME)] = F("Indoor temperature source"); doc[FPSTR(HA_NAME)] = F("Indoor temperature source");
doc[FPSTR(HA_STATE_TOPIC)] = devicePrefix + F("/settings"); doc[FPSTR(HA_STATE_TOPIC)] = devicePrefix + F("/settings");
doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{% if value_json.sensors.indoor.type == 1 %}Manual{% elif value_json.sensors.indoor.type == 2 %}External{% endif %}"); doc[FPSTR(HA_VALUE_TEMPLATE)] = F("{% if value_json.sensors.indoor.type == 1 %}Manual{% elif value_json.sensors.indoor.type == 2 %}External{% elif value_json.sensors.indoor.type == 3 %}Bluetooth{% endif %}");
doc[FPSTR(HA_OPTIONS)][0] = F("Manual"); doc[FPSTR(HA_OPTIONS)][0] = F("Manual");
doc[FPSTR(HA_OPTIONS)][1] = F("External"); doc[FPSTR(HA_OPTIONS)][1] = F("External");
doc[FPSTR(HA_OPTIONS)][2] = F("Bluetooth");
return publish(getTopic("select", "indoor_sensor_type").c_str(), doc); return publish(getTopic("select", "indoor_sensor_type").c_str(), doc);
} }

View File

@@ -290,7 +290,7 @@ protected:
} }
if (!doc["sensors"]["indoor"]["type"].isNull() && doc["sensors"]["indoor"]["type"].is<unsigned char>()) { if (!doc["sensors"]["indoor"]["type"].isNull() && doc["sensors"]["indoor"]["type"].is<unsigned char>()) {
if (doc["sensors"]["indoor"]["type"].as<unsigned char>() >= 1 && doc["sensors"]["indoor"]["type"].as<unsigned char>() <= 2) { if (doc["sensors"]["indoor"]["type"].as<unsigned char>() >= 1 && doc["sensors"]["indoor"]["type"].as<unsigned char>() <= 3) {
settings.sensors.indoor.type = doc["sensors"]["indoor"]["type"].as<unsigned char>(); settings.sensors.indoor.type = doc["sensors"]["indoor"]["type"].as<unsigned char>();
flag = true; flag = true;
} }

View File

@@ -1,8 +1,20 @@
#include <OneWire.h> #include <OneWire.h>
#include <DallasTemperature.h> #include <DallasTemperature.h>
#if defined(ESP32)
#include <NimBLEDevice.h>
// BLE services and characterstics that we are interested in
const uint16_t bleUuidServiceBattery = 0x180F;
const uint16_t bleUuidServiceEnvironment = 0x181AU;
const uint16_t bleUuidCharacteristicBatteryLevel = 0x2A19;
const uint16_t bleUuidCharacteristicTemperature = 0x2A6E;
const uint16_t bleUuidCharacteristicHumidity = 0x2A6F;
#endif
const char S_SENSORS_OUTDOOR[] PROGMEM = "SENSORS.OUTDOOR"; const char S_SENSORS_OUTDOOR[] PROGMEM = "SENSORS.OUTDOOR";
const char S_SENSORS_INDOOR[] PROGMEM = "SENSORS.INDOOR"; const char S_SENSORS_INDOOR[] PROGMEM = "SENSORS.INDOOR";
const char S_SENSORS_BLE[] PROGMEM = "SENSORS.BLE";
class SensorsTask : public LeanTask { class SensorsTask : public LeanTask {
public: public:
@@ -25,6 +37,12 @@ protected:
float filteredIndoorTemp = 0; float filteredIndoorTemp = 0;
bool emptyIndoorTemp = true; bool emptyIndoorTemp = true;
#if defined(ESP32)
BLEClient* pBleClient = nullptr;
BLERemoteService* pBleServiceBattery = nullptr;
BLERemoteService* pBleServiceEnvironment = nullptr;
bool initBleSensor = false;
#endif
const char* getTaskName() { const char* getTaskName() {
return "Sensors"; return "Sensors";
@@ -39,15 +57,69 @@ protected:
} }
void loop() { void loop() {
if (settings.sensors.outdoor.type == 2) { if (settings.sensors.outdoor.type == 2 && settings.sensors.outdoor.pin) {
outdoorTemperatureSensor(); outdoorTemperatureSensor();
} }
if (settings.sensors.indoor.type == 2) { if (settings.sensors.indoor.type == 2 && settings.sensors.indoor.pin) {
indoorTemperatureSensor(); indoorTemperatureSensor();
} }
#if defined(ESP32)
else if (settings.sensors.indoor.type == 3 && strlen(settings.sensors.indoor.bleAddresss)) {
bluetoothSensor();
}
} }
void bluetoothSensor() {
if (!initBleSensor && millis() > 5000) {
Log.sinfoln(FPSTR(S_SENSORS_BLE), "Init BLE. Free heap %u bytes", ESP.getFreeHeap());
BLEDevice::init("");
pBleClient = BLEDevice::createClient();
// Connect to the remote BLE Server.
BLEAddress bleServerAddress(std::string(settings.sensors.indoor.bleAddresss));
if (pBleClient->connect(bleServerAddress)) {
Log.sinfoln(FPSTR(S_SENSORS_BLE), "Connected to BLE device at %s", bleServerAddress.toString().c_str());
// Obtain a reference to the services we are interested in
pBleServiceBattery = pBleClient->getService(BLEUUID(bleUuidServiceBattery));
if (pBleServiceBattery == nullptr) {
Log.sinfoln(FPSTR(S_SENSORS_BLE), "Failed to find battery service");
}
pBleServiceEnvironment = pBleClient->getService(BLEUUID(bleUuidServiceEnvironment));
if (pBleServiceEnvironment == nullptr) {
Log.sinfoln(FPSTR(S_SENSORS_BLE), "Failed to find environmental service");
}
}
else {
Log.swarningln(FPSTR(S_SENSORS_BLE), "Error connecting to BLE device at %s", bleServerAddress.toString().c_str());
}
initBleSensor = true;
}
if (pBleClient && pBleClient->isConnected()) {
Log.straceln(FPSTR(S_SENSORS_BLE), "Connected. Free heap %u bytes", ESP.getFreeHeap());
if (pBleServiceBattery) {
uint8_t batteryLevel = *reinterpret_cast<const uint8_t *>(pBleServiceBattery->getValue(bleUuidCharacteristicBatteryLevel).data());
Log.straceln(FPSTR(S_SENSORS_BLE), "Battery: %d", batteryLevel);
}
if (pBleServiceEnvironment) {
float temperature = *reinterpret_cast<const int16_t *>(pBleServiceEnvironment->getValue(bleUuidCharacteristicTemperature).data()) / 100.0f;
Log.straceln(FPSTR(S_SENSORS_BLE), "Temperature: %.2f", temperature);
float humidity = *reinterpret_cast<const int16_t *>(pBleServiceEnvironment->getValue(bleUuidCharacteristicHumidity).data()) / 100.0f;
Log.straceln(FPSTR(S_SENSORS_BLE), "Humidity: %.2f", humidity);
vars.temperatures.indoor = temperature + settings.sensors.indoor.offset;
}
}
else
{
Log.straceln(FPSTR(S_SENSORS_BLE), "Not connected");
}
}
#endif
void outdoorTemperatureSensor() { void outdoorTemperatureSensor() {
if (!initOutdoorSensor) { if (!initOutdoorSensor) {
oneWireOutdoorSensor = new OneWire(settings.sensors.outdoor.pin); oneWireOutdoorSensor = new OneWire(settings.sensors.outdoor.pin);

View File

@@ -72,9 +72,10 @@ struct Settings {
} outdoor; } outdoor;
struct { struct {
// 1 - manual, 2 - ds18b20 // 1 - manual, 2 - ds18b20, 3 - ble
byte type = 1; byte type = 1;
byte pin = SENSOR_INDOOR_PIN_DEFAULT; byte pin = SENSOR_INDOOR_PIN_DEFAULT;
char bleAddresss[18];
float offset = 0.0f; float offset = 0.0f;
} indoor; } indoor;
} sensors; } sensors;

View File

@@ -28,6 +28,7 @@ CheckboxParameter* wmOtModSyncWithHeating;
UnsignedIntParameter* wmOutdoorSensorPin; UnsignedIntParameter* wmOutdoorSensorPin;
UnsignedIntParameter* wmIndoorSensorPin; UnsignedIntParameter* wmIndoorSensorPin;
WiFiManagerParameter* wmBleAddress;
CheckboxParameter* wmExtPumpUse; CheckboxParameter* wmExtPumpUse;
UnsignedIntParameter* wmExtPumpPin; UnsignedIntParameter* wmExtPumpPin;
@@ -153,6 +154,9 @@ protected:
wmIndoorSensorPin = new UnsignedIntParameter("indoor_sensor_pin", "Indoor sensor GPIO", settings.sensors.indoor.pin, 2); wmIndoorSensorPin = new UnsignedIntParameter("indoor_sensor_pin", "Indoor sensor GPIO", settings.sensors.indoor.pin, 2);
wm.addParameter(wmIndoorSensorPin); wm.addParameter(wmIndoorSensorPin);
wmBleAddress = new WiFiManagerParameter("ble_address", "BLE sensor address", settings.sensors.indoor.bleAddresss, 17);
wm.addParameter(wmBleAddress);
wm.addParameter(wmSep); wm.addParameter(wmSep);
wmExtPumpUse = new CheckboxParameter("ext_pump_use", "Use external pump", settings.externalPump.use); wmExtPumpUse = new CheckboxParameter("ext_pump_use", "Use external pump", settings.externalPump.use);
@@ -427,6 +431,13 @@ protected:
Log.sinfoln(FPSTR(S_WIFI_SETTINGS), F("New sensors.indoor.pin: %hhu"), settings.sensors.indoor.pin); Log.sinfoln(FPSTR(S_WIFI_SETTINGS), F("New sensors.indoor.pin: %hhu"), settings.sensors.indoor.pin);
} }
if (strcmp(wmBleAddress->getValue(), settings.sensors.indoor.bleAddresss) != 0) {
changed = true;
strcpy(settings.sensors.indoor.bleAddresss, wmBleAddress->getValue());
Log.sinfoln(FPSTR(S_WIFI_SETTINGS), F("New BLE address: %s"), settings.sensors.indoor.bleAddresss);
}
if (wmExtPumpUse->getCheckboxValue() != settings.externalPump.use) { if (wmExtPumpUse->getCheckboxValue() != settings.externalPump.use) {
changed = true; changed = true;
settings.externalPump.use = wmExtPumpUse->getCheckboxValue(); settings.externalPump.use = wmExtPumpUse->getCheckboxValue();