Open Kzekih opened 1 year ago
I've not used bluetooth on the ESP32 so unfortunately can't advise on that portion of the project. Are you able to read the thermometer properly using bluetooth but without HomeSpan? In other words, is the issue related to incorporating readings you have successfully taken into HomeSpan, or is it with reading the thermometer via bluetooth?
Note, for greater visibility you may wish to post questions on the main HomeSpan issues and discussions pages instead (this repository is simply a subset of the main HomeSpan docs/examples).
Thanks for your interest. I establish a homkit connection via Homspan, and a card with a Wemoos ESP32 Oled Bluetooth and battery is provided. See the information of the Xiaomi Ble thermometer with your card. So far everything is normal. Homspan sends temperature information to Homekit as Duumy Tem Sensor, 22 degrees C. Instead of installing the Dummy Temp Sensor, which I could not do, he explained the temperature information of the xiaomi Atc Thermometer, which is already running on the same card. However, I still couldn't. I keep making mistakes. I would like to ask you, how can I use the temperature sensor of the xiaomi thermometer instead of the Dummy Temp Sensor in the easiest way. We don't have a problem on the Bluetooth side because I'm already reading on the screen and serial output.
For example: Dummy Temp Sensor = (miThermometer.data[i].temperature / 100.0);
I will be grateful if you could help me. It should be simple actually, but for some reason I couldn't make it.
Also, if you have an example of a Homespan Thermostat that works with a real temperature sensor (DHT , DHT22...), we would appreciate it if you could send it to me. Maybe we can work it that way. kzekih@gmail.com
I have an example of a real temperature sensor that uses the I2C bus: https://github.com/HomeSpan/TempSensorI2C. DHT22 and similar sensors require special timing protocols and can be quite unreliable in certain instances. I generally use I2C components whenever available since they all use a standard bus and the ESP32 has a robust library (called Wire
) to handle all I2C communications.
Hello again, I haven't been successful for a long time, because I couldn't do it, I'm trying to solve it. Also thanks for your help. I examined the example you sent, but no matter what I did, I could not change it to BLE and communicate. An idea came to my mind, I can print the temperature of the ATC Thermometer on the oled screen and see it in the serial output. I can add @c to the beginning and read the heat denominator in the serial output. I wonder if we can't pretend to send the user temperature value from serial.
Serial Out : Sensor 0: a4:c1:38:b2:c7:32 25.50°C 52.00% 3.539V 100% -79dBm
@c 25.50
Sensor 0: a4:c1:38:b2:c7:32 25.50°C 52.00% 3.539V 100% -85dBm
@c 25.50
I'm not sure I fully understand what's working and what's not working. From above, it seems that you are able to read the thermometer on the ESP32. Is that correct? If so, what code are you running to produce the "Serial Out" information above? Did you add that code to the example TempSensorI2C sketch, replacing the code that reads from the I2C sensor? If you are able to run a HomeSpan sketch at the same time you can read your sensor via BLE, what exactly is not working?
Also, can you confirm that you can run any of the HomeSpan sketches, such as Example 1, without any modification, and the it pairs to HomeKit and operates as expected. Making sure you can do this is a first step to then adding in your BLE sensor.
Hello again, I finally made it. It works smoothly. It was an interesting project. Like the Xiaomi Thermometer user, it reports the temperature via serial2. @c <28.00> etc. I am adding the code. Thank you for your help.
`
const int scanTime = 5; // BLE scan time in seconds //static BLEAddress *knownBLEAddresses; //static boolean doConnect = false; //static boolean connected = false;
// List of known sensors' BLE addresses
std::vector
ATC_MiThermometer miThermometer(knownBLEAddresses);
U8G2_SSD1306_128X64_NONAME_F_SW_I2C u8g2(U8G2_R0, /SCL/ 4, /SDA/ 5, /RESET/ -1);
struct DummyTempSensor { static float temp;
DummyTempSensor(float t) {
temp = t;
new SpanUserCommand('f', "
float read() { return (temp); } };
float DummyTempSensor::temp;
struct Reference_Thermostat : Service::Thermostat {
// Create characteristics, set initial values, and set storage in NVS to true
Characteristic::CurrentHeatingCoolingState currentState{ 0, true }; Characteristic::TargetHeatingCoolingState targetState{ 0, true }; Characteristic::CurrentTemperature currentTemp{ 22, true }; Characteristic::TargetTemperature targetTemp{ 22, true }; Characteristic::CurrentRelativeHumidity currentHumidity{ 50, true }; Characteristic::TargetRelativeHumidity targetHumidity{ 50, true }; Characteristic::HeatingThresholdTemperature heatingThreshold{ 22, true }; Characteristic::CoolingThresholdTemperature coolingThreshold{ 22, true }; Characteristic::TemperatureDisplayUnits displayUnits{ 0, true }; // this is for changing the display on the actual thermostat (if any), NOT in the Home App
DummyTempSensor tempSensor{ 22 }; // instantiate a dummy temperature sensor with initial temp=22 degrees C
Reference_Thermostat() : Service::Thermostat() { Serial.printf("\n Creating HomeSpan Thermostat\n");
currentTemp.setRange(MIN_TEMP, MAX_TEMP); // set all ranges the same to make sure Home App displays them correctly on the same dial
targetTemp.setRange(MIN_TEMP, MAX_TEMP);
heatingThreshold.setRange(MIN_TEMP, MAX_TEMP);
coolingThreshold.setRange(MIN_TEMP, MAX_TEMP);
}
boolean update() override {
if (targetState.updated()) {
switch (targetState.getNewVal()) {
case 0:
Serial.printf("Thermostat turning OFF\n");
break;
case 1:
Serial.printf("Thermostat set to HEAT at %s\n", temp2String(targetTemp.getVal<float>()).c_str());
break;
case 2:
Serial.printf("Thermostat set to COOL at %s\n", temp2String(targetTemp.getVal<float>()).c_str());
break;
case 3:
Serial.printf("Thermostat set to AUTO from %s to %s\n", temp2String(heatingThreshold.getVal<float>()).c_str(), temp2String(coolingThreshold.getVal<float>()).c_str());
break;
}
}
if (heatingThreshold.updated() || coolingThreshold.updated())
Serial.printf("Temperature range changed to %s to %s\n", temp2String(heatingThreshold.getNewVal<float>()).c_str(), temp2String(coolingThreshold.getNewVal<float>()).c_str());
else if (targetTemp.updated())
Serial.printf("Temperature target changed to %s\n", temp2String(targetTemp.getNewVal<float>()).c_str());
if (displayUnits.updated())
Serial.printf("Display Units changed to %c\n", displayUnits.getNewVal() ? 'F' : 'C');
if (targetHumidity.updated())
Serial.printf("Humidity target changed to %d%%\n", targetHumidity.getNewVal());
return (true);
}
// Here's where all the main logic exists to turn on/off heating/cooling by comparing the current temperature to the Thermostat's settings
void loop() override {
float temp = tempSensor.read(); // read temperature sensor (which in this example is just a dummy sensor)
if (temp < MIN_TEMP) // limit value to stay between MIN_TEMP and MAX_TEMP
temp = MIN_TEMP;
if (temp > MAX_TEMP)
temp = MAX_TEMP;
if (currentTemp.timeVal() > 5000 && fabs(currentTemp.getVal<float>() - temp) > 0.25) { // if it's been more than 5 seconds since last update, and temperature has changed
currentTemp.setVal(temp);
Serial.printf("Current Temperature is now %s.\n", temp2String(currentTemp.getNewVal<float>()).c_str());
}
switch (targetState.getVal()) {
case 0:
if (currentState.getVal() != 0) {
Serial.printf("Thermostat OFF\n");
currentState.setVal(0);
}
break;
case 1:
if (currentTemp.getVal<float>() < targetTemp.getVal<float>() && currentState.getVal() != 1) {
Serial.printf("Turning HEAT ON\n");
currentState.setVal(1);
} else if (currentTemp.getVal<float>() >= targetTemp.getVal<float>() && currentState.getVal() == 1) {
Serial.printf("Turning HEAT OFF\n");
currentState.setVal(0);
} else if (currentState.getVal() == 2) {
Serial.printf("Turning COOL OFF\n");
currentState.setVal(0);
}
break;
case 2:
if (currentTemp.getVal<float>() > targetTemp.getVal<float>() && currentState.getVal() != 2) {
Serial.printf("Turning COOL ON\n");
currentState.setVal(2);
} else if (currentTemp.getVal<float>() <= targetTemp.getVal<float>() && currentState.getVal() == 2) {
Serial.printf("Turning COOL OFF\n");
currentState.setVal(0);
} else if (currentState.getVal() == 1) {
Serial.printf("Turning HEAT OFF\n");
currentState.setVal(0);
}
break;
case 3:
if (currentTemp.getVal<float>() < heatingThreshold.getVal<float>() && currentState.getVal() != 1) {
Serial.printf("Turning HEAT ON\n");
currentState.setVal(1);
} else if (currentTemp.getVal<float>() >= heatingThreshold.getVal<float>() && currentState.getVal() == 1) {
Serial.printf("Turning HEAT OFF\n");
currentState.setVal(0);
}
if (currentTemp.getVal<float>() > coolingThreshold.getVal<float>() && currentState.getVal() != 2) {
Serial.printf("Turning COOL ON\n");
currentState.setVal(2);
} else if (currentTemp.getVal<float>() <= coolingThreshold.getVal<float>() && currentState.getVal() == 2) {
Serial.printf("Turning COOL OFF\n");
currentState.setVal(0);
}
break;
}
}
// This "helper" function makes it easy to display temperatures on the serial monitor in either F or C depending on TemperatureDisplayUnits
String temp2String(float temp) { String t = displayUnits.getVal() ? String(round(temp * 1.8 + 32.0)) : String(temp); t += displayUnits.getVal() ? " F" : " C"; return (t); } };
void drawOLED() { miThermometer.getData(scanTime);
unsigned found = miThermometer.getData(scanTime);
for (int i = 0; i < miThermometer.data.size(); i++) { if (miThermometer.data[i].valid) u8g2.setFont(u8g2_font_logisoso54_tf); u8g2.setCursor(0, 60); u8g2.print(miThermometer.data[i].temperature / 100.0); u8g2.sendBuffer(); } } //drawOLED()
void scanBLE() {
// miThermometer.begin(); miThermometer.resetData(); miThermometer.getData(scanTime);
unsigned found = miThermometer.getData(scanTime);
for (int i = 0; i < miThermometer.data.size(); i++) { if (miThermometer.data[i].valid) { Serial.println(); Serial.printf("Sensor %d: %s\n", i, knownBLEAddresses[i].c_str()); Serial.printf("%.2f°C\n", miThermometer.data[i].temperature / 100.0); Serial.printf("%.2f%%\n", miThermometer.data[i].humidity / 100.0); Serial.printf("%.3fV\n", miThermometer.data[i].batt_voltage / 1000.0); Serial.printf("%d%%\n", miThermometer.data[i].batt_level); Serial.printf("%ddBm\n", miThermometer.data[i].rssi); Serial.println(); delay(1000); Serial2.printf("@c %.2f \r\n", miThermometer.data[i].temperature / 100.0) + String(RX); delay(1000); } }
while (Serial2.available()) { Serial.print(char(Serial2.read())); //Serial2.println("@c %.2f \r\n", miThermometer.data[i].temperature / 100.0) + String(RX); } }
void setup() {
Serial.begin(115200);
Serial2.begin(115200, SERIAL_8N1, RXD2, TXD2); Serial2.println("Serial Txd is on pin: " + String(TX)); Serial2.println("Serial Rxd is on pin: " + String(RX));
u8g2.begin();
pinMode(HEATING_RELAY, OUTPUT);
// if Wi-Fi credentials are defined, supply // them here, otherwise set them using the HomeSpan CLI homeSpan.setWifiCredentials(WIFI_SSID, WIFI_PASSWORD);
homeSpan.begin(Category::Thermostats, "HomeSpan Thermostat");
new SpanAccessory(); new Service::AccessoryInformation(); new Characteristic::Identify();
new Reference_Thermostat();
miThermometer.begin(); scanBLE();
drawOLED(); }
void loop() {
homeSpan.poll(); }`
Thanks for the update and good news. Really glad that you got it all working.
Hello again guys. BLE execution protection was limited to WiFi. There have been escapes. I accept the code. I am sharing the latest version. Good luck.
`
////////////////////////////////////////////////////////////////////////
const int HOMESPAN_STATUS_PIN = 2; const int HOMESPAN_CONTROL_PIN = 32;
const int scanTime = 5; // BLE scan time in seconds
// List of known sensors' BLE addresses
std::vector
ATC_MiThermometer miThermometer(knownBLEAddresses);
U8G2_SSD1306_128X64_NONAME_F_SW_I2C u8g2(U8G2_R0, /SCL/ 4, /SDA/ 5, /RESET/ -1);
struct DummyTempSensor { static float temp;
DummyTempSensor(float t) {
temp = t;
new SpanUserCommand('f', "
float read() { return (temp); } };
float DummyTempSensor::temp;
struct Reference_Thermostat : Service::Thermostat {
// Create characteristics, set initial values, and set storage in NVS to true
Characteristic::CurrentHeatingCoolingState currentState{ 0, true }; Characteristic::TargetHeatingCoolingState targetState{ 0, true }; Characteristic::CurrentTemperature currentTemp{ 22, true }; Characteristic::TargetTemperature targetTemp{ 22, true }; Characteristic::CurrentRelativeHumidity currentHumidity{ 50, true }; Characteristic::TargetRelativeHumidity targetHumidity{ 50, true }; Characteristic::HeatingThresholdTemperature heatingThreshold{ 22, true }; Characteristic::CoolingThresholdTemperature coolingThreshold{ 22, true }; Characteristic::TemperatureDisplayUnits displayUnits{ 0, true }; // this is for changing the display on the actual thermostat (if any), NOT in the Home App
DummyTempSensor tempSensor{ 22 }; // instantiate a dummy temperature sensor with initial temp=22 degrees C
Reference_Thermostat() : Service::Thermostat() { Serial.printf("\n Creating HomeSpan Thermostat\n");
currentTemp.setRange(MIN_TEMP, MAX_TEMP); // set all ranges the same to make sure Home App displays them correctly on the same dial
targetTemp.setRange(MIN_TEMP, MAX_TEMP);
heatingThreshold.setRange(MIN_TEMP, MAX_TEMP);
coolingThreshold.setRange(MIN_TEMP, MAX_TEMP);
}
boolean update() override {
if (targetState.updated()) {
switch (targetState.getNewVal()) {
case 0:
Serial.printf("Thermostat turning OFF\n");
digitalWrite(HEATING_RELAY, LOW);
break;
case 1:
Serial.printf("Thermostat set to HEAT at %s\n", temp2String(targetTemp.getVal<float>()).c_str());
break;
case 2:
Serial.printf("Thermostat set to COOL at %s\n", temp2String(targetTemp.getVal<float>()).c_str());
break;
case 3:
Serial.printf("Thermostat set to AUTO from %s to %s\n", temp2String(heatingThreshold.getVal<float>()).c_str(), temp2String(coolingThreshold.getVal<float>()).c_str());
break;
}
}
if (heatingThreshold.updated() || coolingThreshold.updated())
Serial.printf("Temperature range changed to %s to %s\n", temp2String(heatingThreshold.getNewVal<float>()).c_str(), temp2String(coolingThreshold.getNewVal<float>()).c_str());
else if (targetTemp.updated())
Serial.printf("Temperature target changed to %s\n", temp2String(targetTemp.getNewVal<float>()).c_str());
if (displayUnits.updated())
Serial.printf("Display Units changed to %c\n", displayUnits.getNewVal() ? 'F' : 'C');
if (targetHumidity.updated())
Serial.printf("Humidity target changed to %d%%\n", targetHumidity.getNewVal());
return (true);
}
// Here's where all the main logic exists to turn on/off heating/cooling by comparing the current temperature to the Thermostat's settings
void loop() override {
float temp = tempSensor.read(); // read temperature sensor (which in this example is just a dummy sensor)
if (temp < MIN_TEMP) // limit value to stay between MIN_TEMP and MAX_TEMP
temp = MIN_TEMP;
if (temp > MAX_TEMP)
temp = MAX_TEMP;
if (currentTemp.timeVal() > 5000 && fabs(currentTemp.getVal<float>() - temp) > 0.25) { // if it's been more than 5 seconds since last update, and temperature has changed
currentTemp.setVal(temp);
Serial.printf("Current Temperature is now %s.\n", temp2String(currentTemp.getNewVal<float>()).c_str());
}
switch (targetState.getVal()) {
case 0:
if (currentState.getVal() != 0) {
Serial.printf("Thermostat OFF\n");
digitalWrite(HEATING_RELAY, LOW);
currentState.setVal(0);
}
break;
case 1:
if (currentTemp.getVal<float>() < targetTemp.getVal<float>() && currentState.getVal() != 1) {
Serial.printf("Turning HEAT ON\n");
digitalWrite(HEATING_RELAY, HIGH);
currentState.setVal(1);
} else if (currentTemp.getVal<float>() >= targetTemp.getVal<float>() && currentState.getVal() == 1) {
Serial.printf("Turning HEAT OFF\n");
digitalWrite(HEATING_RELAY, LOW);
currentState.setVal(0);
} else if (currentState.getVal() == 2) {
Serial.printf("Turning COOL OFF\n");
currentState.setVal(0);
}
break;
case 2:
if (currentTemp.getVal<float>() > targetTemp.getVal<float>() && currentState.getVal() != 2) {
Serial.printf("Turning COOL ON\n");
currentState.setVal(2);
} else if (currentTemp.getVal<float>() <= targetTemp.getVal<float>() && currentState.getVal() == 2) {
Serial.printf("Turning COOL OFF\n");
currentState.setVal(0);
} else if (currentState.getVal() == 1) {
Serial.printf("Turning HEAT OFF\n");
digitalWrite(HEATING_RELAY, LOW);
currentState.setVal(0);
}
break;
case 3:
if (currentTemp.getVal<float>() < heatingThreshold.getVal<float>() && currentState.getVal() != 1) {
Serial.printf("Turning HEAT ON\n");
digitalWrite(HEATING_RELAY, HIGH);
currentState.setVal(1);
} else if (currentTemp.getVal<float>() >= heatingThreshold.getVal<float>() && currentState.getVal() == 1) {
Serial.printf("Turning HEAT OFF\n");
digitalWrite(HEATING_RELAY, LOW);
currentState.setVal(0);
}
if (currentTemp.getVal<float>() > coolingThreshold.getVal<float>() && currentState.getVal() != 2) {
Serial.printf("Turning COOL ON\n");
digitalWrite(HEATING_RELAY, LOW);
currentState.setVal(2);
} else if (currentTemp.getVal<float>() <= coolingThreshold.getVal<float>() && currentState.getVal() == 2) {
Serial.printf("Turning COOL OFF\n");
currentState.setVal(0);
}
break;
}
}
// This "helper" function makes it easy to display temperatures on the serial monitor in either F or C depending on TemperatureDisplayUnits
String temp2String(float temp) { String t = displayUnits.getVal() ? String(round(temp * 1.8 + 32.0)) : String(temp); t += displayUnits.getVal() ? " F" : " C"; return (t); } };
void scanBLE() { // miThermometer.begin(); miThermometer.resetData(); miThermometer.getData(scanTime);
unsigned found = miThermometer.getData(scanTime);
for (int i = 0; i < miThermometer.data.size(); i++) { if (miThermometer.data[i].valid) { Serial.println(); Serial.printf("Sensor %d: %s\n", i, knownBLEAddresses[i].c_str()); Serial.printf("%.2f°C\n", miThermometer.data[i].temperature / 100.0); Serial.printf("%.2f%%\n", miThermometer.data[i].humidity / 100.0); Serial.printf("%.3fV\n", miThermometer.data[i].batt_voltage / 1000.0); Serial.printf("%d%%\n", miThermometer.data[i].batt_level); Serial.printf("%ddBm\n", miThermometer.data[i].rssi); Serial.println(); delay(1000); Serial2.printf("@c %.2f \r\n", miThermometer.data[i].temperature / 100.0) + String(RX); delay(1000); u8g2.setFont(u8g2_font_logisoso54_tf); u8g2.setCursor(0, 60); u8g2.print(miThermometer.data[i].temperature / 100.0); u8g2.sendBuffer(); } }
while (Serial2.available()) { Serial.print(char(Serial2.read())); //Serial2.println("@c %.2f \r\n", miThermometer.data[i].temperature / 100.0) + String(RX); } }
void setup() {
Serial.begin(115200);
Serial2.begin(115200, SERIAL_8N1, RXD2, TXD2); Serial2.println("Serial Txd is on pin: " + String(TX)); Serial2.println("Serial Rxd is on pin: " + String(RX));
homeSpan.setStatusPin(HOMESPAN_STATUS_PIN); homeSpan.setControlPin(HOMESPAN_CONTROL_PIN);
u8g2.begin();
pinMode(HEATING_RELAY, OUTPUT);
// if Wi-Fi credentials are defined, supply // them here, otherwise set them using the HomeSpan CLI homeSpan.setWifiCredentials(WIFI_SSID, WIFI_PASSWORD);
homeSpan.begin(Category::Thermostats, "HomeSpan Thermostat");
new SpanAccessory(); new Service::AccessoryInformation(); new Characteristic::Identify();
new Reference_Thermostat();
miThermometer.begin(); homeSpan.autoPoll(); }
void loop() { scanBLE(); } `
Hello everyone. I've been trying to get the Homespan thermostat example to work with the xiaomi thermometer, but I haven't been able to. I want to use bluetooth temperature sensor instead of local temperature sensor. I'm running the thermostat example I bought the screen shot of the Xiaomi mi thermometer, but I couldn't use the values of the Atc thermometer instead of a kind of DummyTempSensor. Homekit values 22 C. I am very glad for your help. I've been trying for 24 hours and I couldn't succeed.