mirror of
https://github.com/Laxilef/OTGateway.git
synced 2025-12-11 18:54:28 +05:00
382 lines
12 KiB
C++
382 lines
12 KiB
C++
#include <OneWire.h>
|
|
#include <DallasTemperature.h>
|
|
|
|
#if USE_BLE
|
|
#include <NimBLEDevice.h>
|
|
#endif
|
|
|
|
class SensorsTask : public LeanTask {
|
|
public:
|
|
SensorsTask(bool _enabled = false, unsigned long _interval = 0) : LeanTask(_enabled, _interval) {
|
|
this->oneWireOutdoorSensor = new OneWire();
|
|
this->outdoorSensor = new DallasTemperature(this->oneWireOutdoorSensor);
|
|
this->outdoorSensor->setWaitForConversion(false);
|
|
|
|
this->oneWireIndoorSensor = new OneWire();
|
|
this->indoorSensor = new DallasTemperature(this->oneWireIndoorSensor);
|
|
this->indoorSensor->setWaitForConversion(false);
|
|
}
|
|
|
|
~SensorsTask() {
|
|
delete this->outdoorSensor;
|
|
delete this->oneWireOutdoorSensor;
|
|
delete this->indoorSensor;
|
|
delete this->oneWireIndoorSensor;
|
|
}
|
|
|
|
protected:
|
|
OneWire* oneWireOutdoorSensor = nullptr;
|
|
OneWire* oneWireIndoorSensor = nullptr;
|
|
|
|
DallasTemperature* outdoorSensor = nullptr;
|
|
DallasTemperature* indoorSensor = nullptr;
|
|
|
|
bool initOutdoorSensor = false;
|
|
unsigned long initOutdoorSensorTime = 0;
|
|
unsigned long startOutdoorConversionTime = 0;
|
|
float filteredOutdoorTemp = 0;
|
|
bool emptyOutdoorTemp = true;
|
|
|
|
bool initIndoorSensor = false;
|
|
unsigned long initIndoorSensorTime = 0;
|
|
unsigned long startIndoorConversionTime = 0;
|
|
float filteredIndoorTemp = 0;
|
|
bool emptyIndoorTemp = true;
|
|
|
|
#if defined(ARDUINO_ARCH_ESP32)
|
|
#if USE_BLE
|
|
BLEClient* pBleClient = nullptr;
|
|
bool initBleSensor = false;
|
|
bool initBleNotify = false;
|
|
#endif
|
|
|
|
const char* getTaskName() override {
|
|
return "Sensors";
|
|
}
|
|
|
|
BaseType_t getTaskCore() override {
|
|
// https://github.com/h2zero/NimBLE-Arduino/issues/676
|
|
#if USE_BLE && defined(CONFIG_BT_NIMBLE_PINNED_TO_CORE)
|
|
return CONFIG_BT_NIMBLE_PINNED_TO_CORE;
|
|
#else
|
|
return tskNO_AFFINITY;
|
|
#endif
|
|
}
|
|
|
|
int getTaskPriority() override {
|
|
return 4;
|
|
}
|
|
#endif
|
|
|
|
void loop() {
|
|
bool indoorTempUpdated = false;
|
|
bool outdoorTempUpdated = false;
|
|
|
|
if (settings.sensors.outdoor.type == SensorType::DS18B20 && GPIO_IS_VALID(settings.sensors.outdoor.gpio)) {
|
|
outdoorTemperatureSensor();
|
|
outdoorTempUpdated = true;
|
|
}
|
|
|
|
if (settings.sensors.indoor.type == SensorType::DS18B20 && GPIO_IS_VALID(settings.sensors.indoor.gpio)) {
|
|
indoorTemperatureSensor();
|
|
indoorTempUpdated = true;
|
|
}
|
|
#if USE_BLE
|
|
else if (settings.sensors.indoor.type == SensorType::BLUETOOTH) {
|
|
indoorTemperatureBluetoothSensor();
|
|
indoorTempUpdated = true;
|
|
}
|
|
#endif
|
|
|
|
if (outdoorTempUpdated) {
|
|
float newTemp = settings.sensors.outdoor.offset;
|
|
if (settings.system.unitSystem == UnitSystem::METRIC) {
|
|
newTemp += this->filteredOutdoorTemp;
|
|
|
|
} else if (settings.system.unitSystem == UnitSystem::IMPERIAL) {
|
|
newTemp += c2f(this->filteredOutdoorTemp);
|
|
}
|
|
|
|
if (fabs(vars.temperatures.outdoor - newTemp) > 0.099f) {
|
|
vars.temperatures.outdoor = newTemp;
|
|
Log.sinfoln(FPSTR(L_SENSORS_OUTDOOR), F("New temp: %f"), vars.temperatures.outdoor);
|
|
}
|
|
}
|
|
|
|
if (indoorTempUpdated) {
|
|
float newTemp = settings.sensors.indoor.offset;
|
|
if (settings.system.unitSystem == UnitSystem::METRIC) {
|
|
newTemp += this->filteredIndoorTemp;
|
|
|
|
} else if (settings.system.unitSystem == UnitSystem::IMPERIAL) {
|
|
newTemp += c2f(this->filteredIndoorTemp);
|
|
}
|
|
|
|
if (fabs(vars.temperatures.indoor - newTemp) > 0.099f) {
|
|
vars.temperatures.indoor = newTemp;
|
|
Log.sinfoln(FPSTR(L_SENSORS_INDOOR), F("New temp: %f"), vars.temperatures.indoor);
|
|
}
|
|
}
|
|
}
|
|
|
|
#if USE_BLE
|
|
void indoorTemperatureBluetoothSensor() {
|
|
static bool initBleNotify = false;
|
|
if (!initBleSensor && millis() > 5000) {
|
|
Log.sinfoln(FPSTR(L_SENSORS_BLE), F("Init BLE"));
|
|
BLEDevice::init("");
|
|
|
|
pBleClient = BLEDevice::createClient();
|
|
pBleClient->setConnectTimeout(5);
|
|
|
|
initBleSensor = true;
|
|
}
|
|
|
|
if (!initBleSensor || pBleClient->isConnected()) {
|
|
return;
|
|
}
|
|
|
|
// Reset init notify flag
|
|
this->initBleNotify = false;
|
|
|
|
// Connect to the remote BLE Server.
|
|
BLEAddress bleServerAddress(settings.sensors.indoor.bleAddresss);
|
|
if (!pBleClient->connect(bleServerAddress)) {
|
|
Log.swarningln(FPSTR(L_SENSORS_BLE), "Failed connecting to device at %s", bleServerAddress.toString().c_str());
|
|
return;
|
|
}
|
|
|
|
Log.sinfoln(FPSTR(L_SENSORS_BLE), "Connected to device at %s", bleServerAddress.toString().c_str());
|
|
|
|
NimBLEUUID serviceUUID((uint16_t) 0x181AU);
|
|
BLERemoteService* pRemoteService = pBleClient->getService(serviceUUID);
|
|
if (!pRemoteService) {
|
|
Log.straceln(FPSTR(L_SENSORS_BLE), F("Failed to find service UUID: %s"), serviceUUID.toString().c_str());
|
|
return;
|
|
}
|
|
|
|
Log.straceln(FPSTR(L_SENSORS_BLE), F("Found service UUID: %s"), serviceUUID.toString().c_str());
|
|
|
|
// 0x2A6E - Notify temperature x0.01C (pvvx)
|
|
if (!this->initBleNotify) {
|
|
NimBLEUUID charUUID((uint16_t) 0x2A6E);
|
|
BLERemoteCharacteristic* pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID);
|
|
if (pRemoteCharacteristic && pRemoteCharacteristic->canNotify()) {
|
|
Log.straceln(FPSTR(L_SENSORS_BLE), F("Found characteristic UUID: %s"), charUUID.toString().c_str());
|
|
|
|
this->initBleNotify = pRemoteCharacteristic->subscribe(true, [this](NimBLERemoteCharacteristic*, uint8_t* pData, size_t length, bool isNotify) {
|
|
if (length != 2) {
|
|
Log.swarningln(FPSTR(L_SENSORS_BLE), F("Invalid notification data"));
|
|
return;
|
|
}
|
|
|
|
float rawTemp = ((pData[0] | (pData[1] << 8)) * 0.01f);
|
|
Log.straceln(FPSTR(L_SENSORS_INDOOR), F("Raw temp: %f"), rawTemp);
|
|
|
|
if (this->emptyIndoorTemp) {
|
|
this->filteredIndoorTemp = rawTemp;
|
|
this->emptyIndoorTemp = false;
|
|
|
|
} else {
|
|
this->filteredIndoorTemp += (rawTemp - this->filteredIndoorTemp) * EXT_SENSORS_FILTER_K;
|
|
}
|
|
|
|
this->filteredIndoorTemp = floor(this->filteredIndoorTemp * 100) / 100;
|
|
});
|
|
|
|
if (this->initBleNotify) {
|
|
Log.straceln(FPSTR(L_SENSORS_BLE), F("Subscribed to characteristic UUID: %s"), charUUID.toString().c_str());
|
|
|
|
} else {
|
|
Log.swarningln(FPSTR(L_SENSORS_BLE), F("Failed to subscribe to characteristic UUID: %s"), charUUID.toString().c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
// 0x2A1F - Notify temperature x0.1C (atc1441/pvvx)
|
|
if (!this->initBleNotify) {
|
|
NimBLEUUID charUUID((uint16_t) 0x2A1F);
|
|
BLERemoteCharacteristic* pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID);
|
|
if (pRemoteCharacteristic && pRemoteCharacteristic->canNotify()) {
|
|
Log.straceln(FPSTR(L_SENSORS_BLE), F("Found characteristic UUID: %s"), charUUID.toString().c_str());
|
|
|
|
this->initBleNotify = pRemoteCharacteristic->subscribe(true, [this](NimBLERemoteCharacteristic*, uint8_t* pData, size_t length, bool isNotify) {
|
|
if (length != 2) {
|
|
Log.swarningln(FPSTR(L_SENSORS_BLE), F("Invalid notification data"));
|
|
return;
|
|
}
|
|
|
|
float rawTemp = ((pData[0] | (pData[1] << 8)) * 0.1);
|
|
Log.straceln(FPSTR(L_SENSORS_INDOOR), F("Raw temp: %f"), rawTemp);
|
|
|
|
if (this->emptyIndoorTemp) {
|
|
this->filteredIndoorTemp = rawTemp;
|
|
this->emptyIndoorTemp = false;
|
|
|
|
} else {
|
|
this->filteredIndoorTemp += (rawTemp - this->filteredIndoorTemp) * EXT_SENSORS_FILTER_K;
|
|
}
|
|
|
|
this->filteredIndoorTemp = floor(this->filteredIndoorTemp * 100) / 100;
|
|
});
|
|
|
|
if (this->initBleNotify) {
|
|
Log.straceln(FPSTR(L_SENSORS_BLE), F("Subscribed to characteristic UUID: %s"), charUUID.toString().c_str());
|
|
|
|
} else {
|
|
Log.swarningln(FPSTR(L_SENSORS_BLE), F("Failed to subscribe to characteristic UUID: %s"), charUUID.toString().c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!this->initBleNotify) {
|
|
Log.swarningln(FPSTR(L_SENSORS_BLE), F("Not found supported characteristics"));
|
|
pBleClient->disconnect();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void outdoorTemperatureSensor() {
|
|
if (!this->initOutdoorSensor) {
|
|
if (this->initOutdoorSensorTime && millis() - this->initOutdoorSensorTime < EXT_SENSORS_INTERVAL * 10) {
|
|
return;
|
|
}
|
|
|
|
Log.sinfoln(FPSTR(L_SENSORS_OUTDOOR), F("Starting on GPIO %hhu..."), settings.sensors.outdoor.gpio);
|
|
|
|
this->oneWireOutdoorSensor->begin(settings.sensors.outdoor.gpio);
|
|
this->oneWireOutdoorSensor->reset();
|
|
this->outdoorSensor->begin();
|
|
this->initOutdoorSensorTime = millis();
|
|
|
|
Log.straceln(
|
|
FPSTR(L_SENSORS_OUTDOOR),
|
|
F("Devices on bus: %hhu, DS18* devices: %hhu"),
|
|
this->outdoorSensor->getDeviceCount(),
|
|
this->outdoorSensor->getDS18Count()
|
|
);
|
|
|
|
if (this->outdoorSensor->getDeviceCount() > 0) {
|
|
this->initOutdoorSensor = true;
|
|
this->outdoorSensor->setResolution(12);
|
|
this->outdoorSensor->requestTemperatures();
|
|
this->startOutdoorConversionTime = millis();
|
|
|
|
Log.sinfoln(FPSTR(L_SENSORS_OUTDOOR), F("Started"));
|
|
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
|
|
unsigned long estimateConversionTime = millis() - this->startOutdoorConversionTime;
|
|
if (estimateConversionTime < this->outdoorSensor->millisToWaitForConversion()) {
|
|
return;
|
|
}
|
|
|
|
bool completed = this->outdoorSensor->isConversionComplete();
|
|
if (!completed && estimateConversionTime >= 1000) {
|
|
this->initOutdoorSensor = false;
|
|
|
|
Log.serrorln(FPSTR(L_SENSORS_OUTDOOR), F("Could not read temperature data (no response)"));
|
|
}
|
|
|
|
if (!completed) {
|
|
return;
|
|
}
|
|
|
|
float rawTemp = this->outdoorSensor->getTempCByIndex(0);
|
|
if (rawTemp == DEVICE_DISCONNECTED_C) {
|
|
this->initOutdoorSensor = false;
|
|
|
|
Log.serrorln(FPSTR(L_SENSORS_OUTDOOR), F("Could not read temperature data (not connected)"));
|
|
|
|
} else {
|
|
Log.straceln(FPSTR(L_SENSORS_OUTDOOR), F("Raw temp: %f"), rawTemp);
|
|
|
|
if (this->emptyOutdoorTemp) {
|
|
this->filteredOutdoorTemp = rawTemp;
|
|
this->emptyOutdoorTemp = false;
|
|
|
|
} else {
|
|
this->filteredOutdoorTemp += (rawTemp - this->filteredOutdoorTemp) * EXT_SENSORS_FILTER_K;
|
|
}
|
|
|
|
this->filteredOutdoorTemp = floor(this->filteredOutdoorTemp * 100) / 100;
|
|
this->outdoorSensor->requestTemperatures();
|
|
this->startOutdoorConversionTime = millis();
|
|
}
|
|
}
|
|
|
|
void indoorTemperatureSensor() {
|
|
if (!this->initIndoorSensor) {
|
|
if (this->initIndoorSensorTime && millis() - this->initIndoorSensorTime < EXT_SENSORS_INTERVAL * 10) {
|
|
return;
|
|
}
|
|
|
|
Log.sinfoln(FPSTR(L_SENSORS_INDOOR), F("Starting on GPIO %hhu..."), settings.sensors.indoor.gpio);
|
|
|
|
this->oneWireIndoorSensor->begin(settings.sensors.indoor.gpio);
|
|
this->oneWireIndoorSensor->reset();
|
|
this->indoorSensor->begin();
|
|
this->initIndoorSensorTime = millis();
|
|
|
|
Log.straceln(
|
|
FPSTR(L_SENSORS_INDOOR),
|
|
F("Devices on bus: %hhu, DS18* devices: %hhu"),
|
|
this->indoorSensor->getDeviceCount(),
|
|
this->indoorSensor->getDS18Count()
|
|
);
|
|
|
|
if (this->indoorSensor->getDeviceCount() > 0) {
|
|
this->initIndoorSensor = true;
|
|
this->indoorSensor->setResolution(12);
|
|
this->indoorSensor->requestTemperatures();
|
|
this->startIndoorConversionTime = millis();
|
|
|
|
Log.sinfoln(FPSTR(L_SENSORS_INDOOR), F("Started"));
|
|
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
|
|
unsigned long estimateConversionTime = millis() - this->startIndoorConversionTime;
|
|
if (estimateConversionTime < this->indoorSensor->millisToWaitForConversion()) {
|
|
return;
|
|
}
|
|
|
|
bool completed = this->indoorSensor->isConversionComplete();
|
|
if (!completed && estimateConversionTime >= 1000) {
|
|
this->initIndoorSensor = false;
|
|
|
|
Log.serrorln(FPSTR(L_SENSORS_INDOOR), F("Could not read temperature data (no response)"));
|
|
}
|
|
|
|
if (!completed) {
|
|
return;
|
|
}
|
|
|
|
float rawTemp = this->indoorSensor->getTempCByIndex(0);
|
|
if (rawTemp == DEVICE_DISCONNECTED_C) {
|
|
this->initIndoorSensor = false;
|
|
|
|
Log.serrorln(FPSTR(L_SENSORS_INDOOR), F("Could not read temperature data (not connected)"));
|
|
|
|
} else {
|
|
Log.straceln(FPSTR(L_SENSORS_INDOOR), F("Raw temp: %f"), rawTemp);
|
|
|
|
if (this->emptyIndoorTemp) {
|
|
this->filteredIndoorTemp = rawTemp;
|
|
this->emptyIndoorTemp = false;
|
|
|
|
} else {
|
|
this->filteredIndoorTemp += (rawTemp - this->filteredIndoorTemp) * EXT_SENSORS_FILTER_K;
|
|
}
|
|
|
|
this->filteredIndoorTemp = floor(this->filteredIndoorTemp * 100) / 100;
|
|
this->indoorSensor->requestTemperatures();
|
|
this->startIndoorConversionTime = millis();
|
|
}
|
|
}
|
|
}; |