mirror of
https://github.com/Laxilef/OTGateway.git
synced 2025-12-11 10:44:29 +05:00
Merge pull request #19 from mennodegraaf/ble-support
Add support for BLE temp sensors
This commit is contained in:
@@ -51,6 +51,7 @@ platform = espressif32
|
||||
lib_deps =
|
||||
${env.lib_deps}
|
||||
laxilef/ESP32Scheduler@^1.0.1
|
||||
h2zero/NimBLE-Arduino@^1.4.1
|
||||
lib_ignore =
|
||||
extra_scripts =
|
||||
post:tools/esp32.py
|
||||
@@ -58,6 +59,7 @@ extra_scripts =
|
||||
build_flags =
|
||||
${env.build_flags}
|
||||
-D CORE_DEBUG_LEVEL=0
|
||||
-D USE_BLE=1
|
||||
|
||||
|
||||
; Boards
|
||||
@@ -175,6 +177,7 @@ board = wemos_d1_mini32
|
||||
lib_deps = ${esp32_defaults.lib_deps}
|
||||
lib_ignore = ${esp32_defaults.lib_ignore}
|
||||
extra_scripts = ${esp32_defaults.extra_scripts}
|
||||
;board_build.partitions = huge_app.csv
|
||||
build_flags =
|
||||
${esp32_defaults.build_flags}
|
||||
-D OT_IN_PIN_DEFAULT=21
|
||||
|
||||
@@ -29,16 +29,17 @@ public:
|
||||
bool publishSelectIndoorSensorType(bool enabledByDefault = true) {
|
||||
StaticJsonDocument<1536> doc;
|
||||
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_UNIQUE_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_NAME)] = F("Indoor temperature source");
|
||||
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)][1] = F("External");
|
||||
doc[FPSTR(HA_OPTIONS)][2] = F("Bluetooth");
|
||||
|
||||
return publish(getTopic("select", "indoor_sensor_type").c_str(), doc);
|
||||
}
|
||||
|
||||
@@ -299,7 +299,7 @@ protected:
|
||||
}
|
||||
|
||||
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>();
|
||||
flag = true;
|
||||
}
|
||||
|
||||
@@ -394,7 +394,7 @@ protected:
|
||||
}
|
||||
|
||||
bool updateSlaveConfig() {
|
||||
unsigned long response = ot->sendRequest(ot->buildRequest(OpenThermRequestType::READ, OpenThermMessageID::SConfigSMemberIDcode, 0));
|
||||
unsigned long response = ot->sendRequest(ot->buildRequest(OpenThermRequestType::READ_DATA, OpenThermMessageID::SConfigSMemberIDcode, 0));
|
||||
if (!ot->isValidResponse(response)) {
|
||||
return false;
|
||||
}
|
||||
@@ -451,7 +451,7 @@ protected:
|
||||
}
|
||||
|
||||
unsigned long response = ot->sendRequest(ot->buildRequest(
|
||||
OpenThermRequestType::WRITE,
|
||||
OpenThermRequestType::WRITE_DATA,
|
||||
OpenThermMessageID::MConfigMMemberIDcode,
|
||||
request
|
||||
));
|
||||
@@ -461,7 +461,7 @@ protected:
|
||||
|
||||
bool setMaxModulationLevel(byte value) {
|
||||
unsigned long response = ot->sendRequest(ot->buildRequest(
|
||||
OpenThermRequestType::WRITE,
|
||||
OpenThermRequestType::WRITE_DATA,
|
||||
OpenThermMessageID::MaxRelModLevelSetting,
|
||||
ot->toF88(value)
|
||||
));
|
||||
@@ -475,7 +475,7 @@ protected:
|
||||
}
|
||||
|
||||
bool updateSlaveOtVersion() {
|
||||
unsigned long response = ot->sendRequest(ot->buildRequest(OpenThermRequestType::READ, OpenThermMessageID::OpenThermVersionSlave, 0));
|
||||
unsigned long response = ot->sendRequest(ot->buildRequest(OpenThermRequestType::READ_DATA, OpenThermMessageID::OpenThermVersionSlave, 0));
|
||||
if (!ot->isValidResponse(response)) {
|
||||
return false;
|
||||
}
|
||||
@@ -501,7 +501,7 @@ protected:
|
||||
}
|
||||
|
||||
bool updateSlaveVersion() {
|
||||
unsigned long response = ot->sendRequest(ot->buildRequest(OpenThermRequestType::READ, OpenThermMessageID::SlaveVersion, 0));
|
||||
unsigned long response = ot->sendRequest(ot->buildRequest(OpenThermRequestType::READ_DATA, OpenThermMessageID::SlaveVersion, 0));
|
||||
if (!ot->isValidResponse(response)) {
|
||||
return false;
|
||||
}
|
||||
@@ -514,7 +514,7 @@ protected:
|
||||
|
||||
bool setMasterVersion(uint8_t version, uint8_t type) {
|
||||
unsigned long response = ot->sendRequest(ot->buildRequest(
|
||||
OpenThermRequestType::WRITE,
|
||||
OpenThermRequestType::WRITE_DATA,
|
||||
OpenThermMessageID::MasterVersion,
|
||||
(unsigned int) version | (unsigned int) type << 8 // 0x013F
|
||||
));
|
||||
@@ -530,7 +530,7 @@ protected:
|
||||
}
|
||||
|
||||
bool updateMinMaxDhwTemp() {
|
||||
unsigned long response = ot->sendRequest(ot->buildRequest(OpenThermRequestType::READ, OpenThermMessageID::TdhwSetUBTdhwSetLB, 0));
|
||||
unsigned long response = ot->sendRequest(ot->buildRequest(OpenThermRequestType::READ_DATA, OpenThermMessageID::TdhwSetUBTdhwSetLB, 0));
|
||||
if (!ot->isValidResponse(response)) {
|
||||
return false;
|
||||
}
|
||||
@@ -549,7 +549,7 @@ protected:
|
||||
}
|
||||
|
||||
bool updateMinMaxHeatingTemp() {
|
||||
unsigned long response = ot->sendRequest(ot->buildRequest(OpenThermRequestType::READ, OpenThermMessageID::MaxTSetUBMaxTSetLB, 0));
|
||||
unsigned long response = ot->sendRequest(ot->buildRequest(OpenThermRequestType::READ_DATA, OpenThermMessageID::MaxTSetUBMaxTSetLB, 0));
|
||||
if (!ot->isValidResponse(response)) {
|
||||
return false;
|
||||
}
|
||||
@@ -576,7 +576,7 @@ protected:
|
||||
}
|
||||
|
||||
bool updateOutsideTemp() {
|
||||
unsigned long response = ot->sendRequest(ot->buildRequest(OpenThermRequestType::READ, OpenThermMessageID::Toutside, 0));
|
||||
unsigned long response = ot->sendRequest(ot->buildRequest(OpenThermRequestType::READ_DATA, OpenThermMessageID::Toutside, 0));
|
||||
if (!ot->isValidResponse(response)) {
|
||||
return false;
|
||||
}
|
||||
@@ -597,7 +597,7 @@ protected:
|
||||
|
||||
|
||||
bool updateDhwTemp() {
|
||||
unsigned long response = ot->sendRequest(ot->buildRequest(OpenThermMessageType::READ, OpenThermMessageID::Tdhw, 0));
|
||||
unsigned long response = ot->sendRequest(ot->buildRequest(OpenThermMessageType::READ_DATA, OpenThermMessageID::Tdhw, 0));
|
||||
if (!ot->isValidResponse(response)) {
|
||||
return false;
|
||||
}
|
||||
@@ -607,7 +607,7 @@ protected:
|
||||
}
|
||||
|
||||
bool updateDhwFlowRate() {
|
||||
unsigned long response = ot->sendRequest(ot->buildRequest(OpenThermMessageType::READ, OpenThermMessageID::DHWFlowRate, 0));
|
||||
unsigned long response = ot->sendRequest(ot->buildRequest(OpenThermMessageType::READ_DATA, OpenThermMessageID::DHWFlowRate, 0));
|
||||
if (!ot->isValidResponse(response)) {
|
||||
return false;
|
||||
}
|
||||
@@ -617,7 +617,7 @@ protected:
|
||||
}
|
||||
|
||||
bool updateFaultCode() {
|
||||
unsigned long response = ot->sendRequest(ot->buildRequest(OpenThermRequestType::READ, OpenThermMessageID::ASFflags, 0));
|
||||
unsigned long response = ot->sendRequest(ot->buildRequest(OpenThermRequestType::READ_DATA, OpenThermMessageID::ASFflags, 0));
|
||||
|
||||
if (!ot->isValidResponse(response)) {
|
||||
return false;
|
||||
@@ -628,7 +628,7 @@ protected:
|
||||
}
|
||||
|
||||
bool updateModulationLevel() {
|
||||
unsigned long response = ot->sendRequest(ot->buildRequest(OpenThermRequestType::READ, OpenThermMessageID::RelModLevel, 0));
|
||||
unsigned long response = ot->sendRequest(ot->buildRequest(OpenThermRequestType::READ_DATA, OpenThermMessageID::RelModLevel, 0));
|
||||
|
||||
if (!ot->isValidResponse(response)) {
|
||||
return false;
|
||||
@@ -645,7 +645,7 @@ protected:
|
||||
}
|
||||
|
||||
bool updatePressure() {
|
||||
unsigned long response = ot->sendRequest(ot->buildRequest(OpenThermRequestType::READ, OpenThermMessageID::CHPressure, 0));
|
||||
unsigned long response = ot->sendRequest(ot->buildRequest(OpenThermRequestType::READ_DATA, OpenThermMessageID::CHPressure, 0));
|
||||
|
||||
if (!ot->isValidResponse(response)) {
|
||||
return false;
|
||||
|
||||
@@ -1,8 +1,20 @@
|
||||
#include <OneWire.h>
|
||||
#include <DallasTemperature.h>
|
||||
|
||||
#if USE_BLE
|
||||
#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_INDOOR[] PROGMEM = "SENSORS.INDOOR";
|
||||
const char S_SENSORS_BLE[] PROGMEM = "SENSORS.BLE";
|
||||
|
||||
class SensorsTask : public LeanTask {
|
||||
public:
|
||||
@@ -25,6 +37,12 @@ protected:
|
||||
float filteredIndoorTemp = 0;
|
||||
bool emptyIndoorTemp = true;
|
||||
|
||||
#if USE_BLE
|
||||
BLEClient* pBleClient = nullptr;
|
||||
BLERemoteService* pBleServiceBattery = nullptr;
|
||||
BLERemoteService* pBleServiceEnvironment = nullptr;
|
||||
bool initBleSensor = false;
|
||||
#endif
|
||||
|
||||
const char* getTaskName() {
|
||||
return "Sensors";
|
||||
@@ -39,15 +57,71 @@ protected:
|
||||
}
|
||||
|
||||
void loop() {
|
||||
if (settings.sensors.outdoor.type == 2) {
|
||||
if (settings.sensors.outdoor.type == 2 && settings.sensors.outdoor.pin) {
|
||||
outdoorTemperatureSensor();
|
||||
}
|
||||
|
||||
if (settings.sensors.indoor.type == 2) {
|
||||
if (settings.sensors.indoor.type == 2 && settings.sensors.indoor.pin) {
|
||||
indoorTemperatureSensor();
|
||||
}
|
||||
#if USE_BLE
|
||||
else if (settings.sensors.indoor.type == 3 && strlen(settings.sensors.indoor.bleAddresss)) {
|
||||
bluetoothSensor();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if USE_BLE
|
||||
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() {
|
||||
if (!initOutdoorSensor) {
|
||||
oneWireOutdoorSensor = new OneWire(settings.sensors.outdoor.pin);
|
||||
|
||||
@@ -72,9 +72,10 @@ struct Settings {
|
||||
} outdoor;
|
||||
|
||||
struct {
|
||||
// 1 - manual, 2 - ds18b20
|
||||
// 1 - manual, 2 - ds18b20, 3 - ble
|
||||
byte type = 1;
|
||||
byte pin = SENSOR_INDOOR_PIN_DEFAULT;
|
||||
char bleAddresss[18];
|
||||
float offset = 0.0f;
|
||||
} indoor;
|
||||
} sensors;
|
||||
|
||||
@@ -28,6 +28,7 @@ CheckboxParameter* wmOtModSyncWithHeating;
|
||||
|
||||
UnsignedIntParameter* wmOutdoorSensorPin;
|
||||
UnsignedIntParameter* wmIndoorSensorPin;
|
||||
WiFiManagerParameter* wmBleAddress;
|
||||
|
||||
CheckboxParameter* wmExtPumpUse;
|
||||
UnsignedIntParameter* wmExtPumpPin;
|
||||
@@ -170,6 +171,9 @@ protected:
|
||||
wmIndoorSensorPin = new UnsignedIntParameter("indoor_sensor_pin", "Indoor sensor GPIO", settings.sensors.indoor.pin, 2);
|
||||
wm.addParameter(wmIndoorSensorPin);
|
||||
|
||||
wmBleAddress = new WiFiManagerParameter("ble_address", "BLE sensor address", settings.sensors.indoor.bleAddresss, 17);
|
||||
wm.addParameter(wmBleAddress);
|
||||
|
||||
wmExtPumpHeader = new HeaderParameter("External pump");
|
||||
wm.addParameter(wmExtPumpHeader);
|
||||
|
||||
@@ -445,6 +449,13 @@ protected:
|
||||
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) {
|
||||
changed = true;
|
||||
settings.externalPump.use = wmExtPumpUse->getCheckboxValue();
|
||||
|
||||
@@ -34,6 +34,10 @@
|
||||
#define USE_TELNET true
|
||||
#endif
|
||||
|
||||
#ifndef USE_BLE
|
||||
#define USE_BLE false
|
||||
#endif
|
||||
|
||||
#ifndef DEBUG_BY_DEFAULT
|
||||
#define DEBUG_BY_DEFAULT false
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user