Version OK avec Mqtt
This commit is contained in:
296
README.md
Normal file
296
README.md
Normal file
@@ -0,0 +1,296 @@
|
||||
# Thermostat Project for Sonoff Basic R2
|
||||
|
||||
This project implements a WiFi thermostat using a Sonoff Basic R2, designed for integration with Home Assistant via MQTT. It supports preset modes (confort, eco, off, boost), receives room temperature from an MQTT gateway, and provides a fallback WiFi configuration portal for setup.
|
||||
|
||||
## Features
|
||||
- MQTT-based Home Assistant integration as a thermostat device (with preset modes)
|
||||
- Receives room temperature from an external MQTT sensor (e.g., Xiaomi)
|
||||
- Fallback WiFi configuration page for setting WiFi, MQTT server, preset temperatures, and device ID
|
||||
- Configurable preset temperatures (confort, eco, boost, hors gel)
|
||||
- Clean code structure for maintainability
|
||||
|
||||
## Project Structure
|
||||
- `src/` : Main application logic
|
||||
- `include/` : Header files
|
||||
- `data/` : Static files for web configuration portal
|
||||
- `platformio.ini` : PlatformIO project configuration
|
||||
- `README.md` : This documentation
|
||||
|
||||
## Getting Started
|
||||
|
||||
### 1. Prerequisites
|
||||
- PlatformIO extension for VS Code
|
||||
- Sonoff Basic R2 device
|
||||
- MQTT broker (e.g., Mosquitto)
|
||||
- Home Assistant instance
|
||||
|
||||
### 2. Build & Upload
|
||||
1. Clone this repository or copy the folder to your PlatformIO workspace.
|
||||
2. Edit `platformio.ini` if needed (WiFi/MQTT credentials can be set via the web portal after first boot).
|
||||
3. Build and upload the firmware to your Sonoff Basic R2.
|
||||
|
||||
### 3. First Boot & Configuration
|
||||
- On first boot (or if WiFi/MQTT fails), the device starts a WiFi AP for configuration.
|
||||
- Connect to the AP and open the captive portal to set:
|
||||
- WiFi SSID/password
|
||||
- MQTT server/port/credentials
|
||||
- Preset temperatures (confort, eco, boost, hors gel)
|
||||
- Device ID for temperature sensor
|
||||
|
||||
### 4. Home Assistant Integration
|
||||
- The device publishes/receives via MQTT using the Home Assistant climate platform.
|
||||
- Preset modes (confort, eco, off, boost) are supported and can be set from Home Assistant.
|
||||
- The device subscribes to a temperature topic (e.g., from a Xiaomi sensor via Zigbee2MQTT).
|
||||
|
||||
## Home Assistant Integration
|
||||
|
||||
### MQTT Topics
|
||||
|
||||
The thermostat uses the following MQTT topics:
|
||||
|
||||
**Temperature Input (Subscribe):**
|
||||
```
|
||||
home/OMG_ESP32_BLE/BTtoMQTT/<SENSOR_MAC_ADDRESS>
|
||||
```
|
||||
- The device subscribes to receive temperature updates from your sensor
|
||||
- Example: `home/OMG_ESP32_BLE/BTtoMQTT/A4C1389ADA64`
|
||||
- Payload format: `{"tempc": 21.5, "hum": 45.2, "batt": 85, ...}`
|
||||
- Only the `tempc` field is required
|
||||
|
||||
**Thermostat State (Publish):**
|
||||
```
|
||||
home/thermostat/<DEVICE_ID>/state
|
||||
```
|
||||
- The device publishes its current state
|
||||
- Payload format: `{"mode": 1, "target": 21.0, "heating": true}`
|
||||
|
||||
**Thermostat Commands (Subscribe):**
|
||||
```
|
||||
home/thermostat/<DEVICE_ID>/set
|
||||
```
|
||||
- Home Assistant sends commands to control the thermostat
|
||||
- Payload format: `{"mode": 1, "target": 22.0}`
|
||||
|
||||
### Manual Configuration in Home Assistant
|
||||
|
||||
Add the following to your Home Assistant `configuration.yaml`:
|
||||
|
||||
```yaml
|
||||
mqtt:
|
||||
climate:
|
||||
- name: "Living Room Thermostat"
|
||||
unique_id: "thermostat_living_room"
|
||||
|
||||
# Current temperature from sensor
|
||||
current_temperature_topic: "home/OMG_ESP32_BLE/BTtoMQTT/A4C1389ADA64"
|
||||
current_temperature_template: "{{ value_json.tempc }}"
|
||||
|
||||
# Temperature control
|
||||
temperature_command_topic: "home/thermostat/sonoff_thermo_01/set"
|
||||
temperature_state_topic: "home/thermostat/sonoff_thermo_01/state"
|
||||
temperature_state_template: "{{ value_json.target }}"
|
||||
|
||||
# Mode control
|
||||
mode_command_topic: "home/thermostat/sonoff_thermo_01/set"
|
||||
mode_command_template: '{"mode": {% if value == "off" %}0{% elif value == "heat" %}1{% endif %}}'
|
||||
mode_state_topic: "home/thermostat/sonoff_thermo_01/state"
|
||||
mode_state_template: >
|
||||
{% if value_json.mode == 0 %}off
|
||||
{% elif value_json.mode == 1 %}heat
|
||||
{% else %}off{% endif %}
|
||||
modes:
|
||||
- "off"
|
||||
- "heat"
|
||||
|
||||
# Preset modes (confort, eco, boost, hors gel)
|
||||
preset_mode_command_topic: "home/thermostat/sonoff_thermo_01/set"
|
||||
preset_mode_command_template: >
|
||||
{"mode": {% if value == "comfort" %}1
|
||||
{% elif value == "eco" %}2
|
||||
{% elif value == "boost" %}3
|
||||
{% elif value == "away" %}4
|
||||
{% else %}0{% endif %}}
|
||||
preset_mode_state_topic: "home/thermostat/sonoff_thermo_01/state"
|
||||
preset_mode_value_template: >
|
||||
{% if value_json.mode == 1 %}comfort
|
||||
{% elif value_json.mode == 2 %}eco
|
||||
{% elif value_json.mode == 3 %}boost
|
||||
{% elif value_json.mode == 4 %}away
|
||||
{% else %}none{% endif %}
|
||||
preset_modes:
|
||||
- "comfort"
|
||||
- "eco"
|
||||
- "boost"
|
||||
- "away"
|
||||
|
||||
# Temperature settings
|
||||
min_temp: 5
|
||||
max_temp: 30
|
||||
temp_step: 0.5
|
||||
precision: 0.1
|
||||
|
||||
sensor:
|
||||
- platform: mqtt
|
||||
name: "Living Room Temperature"
|
||||
state_topic: "home/OMG_ESP32_BLE/BTtoMQTT/A4C1389ADA64"
|
||||
value_template: "{{ value_json.tempc }}"
|
||||
unit_of_measurement: "°C"
|
||||
device_class: temperature
|
||||
|
||||
- platform: mqtt
|
||||
name: "Living Room Humidity"
|
||||
state_topic: "home/OMG_ESP32_BLE/BTtoMQTT/A4C1389ADA64"
|
||||
value_template: "{{ value_json.hum }}"
|
||||
unit_of_measurement: "%"
|
||||
device_class: humidity
|
||||
|
||||
binary_sensor:
|
||||
- platform: mqtt
|
||||
name: "Living Room Thermostat Heating"
|
||||
state_topic: "home/thermostat/sonoff_thermo_01/state"
|
||||
value_template: "{{ value_json.heating }}"
|
||||
payload_on: true
|
||||
payload_off: false
|
||||
device_class: heat
|
||||
```
|
||||
|
||||
**Important:** Replace:
|
||||
- `A4C1389ADA64` with your Xiaomi sensor's MAC address (without colons)
|
||||
- `sonoff_thermo_01` with your device ID configured in the web portal
|
||||
|
||||
### Using the Thermostat in Home Assistant
|
||||
|
||||
After configuration and restart, you can:
|
||||
|
||||
1. **Set Temperature:** Use the climate card to adjust target temperature
|
||||
2. **Change Modes:**
|
||||
- **Off:** Heating disabled
|
||||
- **Heat:** Heating enabled with current preset
|
||||
3. **Select Presets:**
|
||||
- **Comfort:** Standard comfort temperature (e.g., 21°C)
|
||||
- **Eco:** Energy-saving mode (e.g., 19°C)
|
||||
- **Boost:** Maximum heating (e.g., 24°C)
|
||||
- **Away (Hors Gel):** Frost protection (e.g., 7°C)
|
||||
|
||||
### Automation Example
|
||||
|
||||
```yaml
|
||||
automation:
|
||||
- alias: "Thermostat Night Mode"
|
||||
trigger:
|
||||
- platform: time
|
||||
at: "22:00:00"
|
||||
action:
|
||||
- service: climate.set_preset_mode
|
||||
target:
|
||||
entity_id: climate.living_room_thermostat
|
||||
data:
|
||||
preset_mode: "eco"
|
||||
|
||||
- alias: "Thermostat Morning Mode"
|
||||
trigger:
|
||||
- platform: time
|
||||
at: "06:30:00"
|
||||
condition:
|
||||
- condition: state
|
||||
entity_id: binary_sensor.workday
|
||||
state: "on"
|
||||
action:
|
||||
- service: climate.set_preset_mode
|
||||
target:
|
||||
entity_id: climate.living_room_thermostat
|
||||
data:
|
||||
preset_mode: "comfort"
|
||||
```
|
||||
|
||||
### Lovelace Card Example
|
||||
|
||||
```yaml
|
||||
type: thermostat
|
||||
entity: climate.living_room_thermostat
|
||||
features:
|
||||
- type: climate-preset-modes
|
||||
preset_modes:
|
||||
- comfort
|
||||
- eco
|
||||
- boost
|
||||
- away
|
||||
```
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
**No temperature updates:**
|
||||
1. Check MQTT broker logs: `mosquitto_sub -h localhost -t "home/OMG_ESP32_BLE/BTtoMQTT/#" -v`
|
||||
2. Verify sensor MAC address matches configuration (use uppercase without colons)
|
||||
3. Check sensor battery level
|
||||
4. Ensure sensor is in range of BLE gateway
|
||||
|
||||
**Thermostat not responding to commands:**
|
||||
1. Verify MQTT topics match your device ID
|
||||
2. Check MQTT broker connection from Home Assistant
|
||||
3. Monitor device serial output for errors
|
||||
4. Ensure MQTT buffer size is set to 512 bytes (see `platformio.ini`)
|
||||
|
||||
**Important Note:** The default MQTT buffer size for PubSubClient is 128 bytes, which is too small for Xiaomi sensor payloads (~317 bytes). This project increases it to 512 bytes via the build flag `-DMQTT_MAX_PACKET_SIZE=512` in `platformio.ini`.
|
||||
|
||||
### 5. File Overview
|
||||
- `src/main.cpp` : Main application entry point
|
||||
- `src/thermostat.cpp` / `include/thermostat.h` : Thermostat logic
|
||||
- `src/mqtt_handler.cpp` / `include/mqtt_handler.h` : MQTT communication
|
||||
- `src/web_config.cpp` / `include/web_config.h` : WiFi/MQTT/web config portal
|
||||
- `src/preset.cpp` / `include/preset.h` : Preset management
|
||||
- `data/` : HTML/CSS/JS for configuration portal
|
||||
|
||||
## Example MQTT Topics
|
||||
- Temperature: `home/room/temperature/<device_id>`
|
||||
- State: `home/thermostat/<device_id>/state`
|
||||
- Command: `home/thermostat/<device_id>/set`
|
||||
|
||||
## Over-The-Air (OTA) Updates
|
||||
|
||||
The thermostat supports OTA updates, allowing you to upload new firmware wirelessly without physical access to the device.
|
||||
|
||||
### Using OTA with PlatformIO
|
||||
|
||||
1. **Ensure the device is connected to WiFi** and note its IP address from the serial monitor
|
||||
|
||||
2. **Configure OTA upload in platformio.ini** (optional - for easier uploads):
|
||||
```ini
|
||||
upload_protocol = espota
|
||||
upload_port = <DEVICE_IP_ADDRESS>
|
||||
```
|
||||
|
||||
3. **Upload via OTA**:
|
||||
```bash
|
||||
# Using PlatformIO CLI
|
||||
platformio run --target upload --upload-port <DEVICE_IP_ADDRESS>
|
||||
|
||||
# Or use the PlatformIO IDE upload button after setting upload_protocol
|
||||
```
|
||||
|
||||
### Using OTA with Arduino IDE
|
||||
|
||||
1. Open **Tools > Port** and select the network port (shows as `<device_hostname> at <IP>`)
|
||||
2. Click **Upload** as normal
|
||||
|
||||
### OTA Behavior
|
||||
|
||||
- During OTA update, the LED stays solid ON
|
||||
- Progress is shown in the serial monitor
|
||||
- Device automatically reboots after successful update
|
||||
- If OTA fails, the device continues running the existing firmware
|
||||
|
||||
### Security Note
|
||||
|
||||
By default, OTA has no password protection. For production use, add a password:
|
||||
```cpp
|
||||
ArduinoOTA.setPassword("your-password-here");
|
||||
```
|
||||
|
||||
## Advanced
|
||||
- Fallback to AP mode if WiFi or MQTT fails
|
||||
- All configuration is stored in flash and can be reset via the web portal
|
||||
- OTA updates for remote firmware deployment
|
||||
|
||||
## License
|
||||
MIT
|
||||
32
data/index.html
Normal file
32
data/index.html
Normal file
@@ -0,0 +1,32 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Thermostat Config</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; margin: 2em; }
|
||||
label { display: block; margin-top: 1em; }
|
||||
input, select { width: 100%; padding: 0.5em; }
|
||||
button { margin-top: 2em; padding: 1em; width: 100%; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h2>Thermostat WiFi & MQTT Config</h2>
|
||||
<form method="POST" action="/save">
|
||||
<label>WiFi SSID<input name="ssid" required></label>
|
||||
<label>WiFi Password<input name="wpass" type="password"></label>
|
||||
<label>MQTT Server<input name="mqtt" required></label>
|
||||
<label>MQTT Port<input name="mqttport" type="number" value="1883"></label>
|
||||
<label>MQTT User<input name="mqttuser"></label>
|
||||
<label>MQTT Password<input name="mqttpass" type="password"></label>
|
||||
<label>Device ID<input name="devid" required></label>
|
||||
<label>Temperature Sensor ID<input name="tempid" required></label>
|
||||
<label>Confort Temp (°C)<input name="confort" type="number" value="20" step="0.1"></label>
|
||||
<label>Eco Temp (°C)<input name="eco" type="number" value="17" step="0.1"></label>
|
||||
<label>Boost Temp (°C)<input name="boost" type="number" value="22" step="0.1"></label>
|
||||
<label>Hors Gel Temp (°C)<input name="horsgel" type="number" value="7" step="0.1"></label>
|
||||
<button type="submit">Save & Reboot</button>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
7
include/main.h
Normal file
7
include/main.h
Normal file
@@ -0,0 +1,7 @@
|
||||
|
||||
|
||||
//GPIO12 // Relay
|
||||
//GPIO13 // LED
|
||||
|
||||
#define LED_PIN 13
|
||||
#define RELAY_PIN 12
|
||||
12
include/mqtt_handler.h
Normal file
12
include/mqtt_handler.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
#include <Arduino.h>
|
||||
#include <PubSubClient.h>
|
||||
#include "thermostat.h"
|
||||
|
||||
extern float currentTemp; // Global temperature variable updated by MQTT callback
|
||||
|
||||
bool mqtt_connect(PubSubClient& client);
|
||||
void mqtt_loop(PubSubClient& client);
|
||||
void mqtt_publish_state(PubSubClient& client, ThermostatMode mode, float targetTemp, bool heating);
|
||||
void mqtt_callback(char* topic, byte* payload, unsigned int length);
|
||||
|
||||
8
include/preset.h
Normal file
8
include/preset.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
#include <Arduino.h>
|
||||
#include "thermostat.h"
|
||||
|
||||
void load_presets();
|
||||
void save_presets();
|
||||
float get_preset_temp(ThermostatMode mode);
|
||||
void set_preset_temp(ThermostatMode mode, float temp);
|
||||
21
include/thermostat.h
Normal file
21
include/thermostat.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
#include <Arduino.h>
|
||||
|
||||
enum ThermostatMode { MODE_OFF, MODE_CONFORT, MODE_ECO, MODE_BOOST, MODE_HORS_GEL };
|
||||
|
||||
class Thermostat {
|
||||
public:
|
||||
Thermostat();
|
||||
void setMode(ThermostatMode mode);
|
||||
void setTemperature(float temp);
|
||||
void setPresetTemp(ThermostatMode mode, float temp);
|
||||
void update(float currentTemp);
|
||||
bool isHeating() const;
|
||||
ThermostatMode getMode() const;
|
||||
float getTargetTemp() const;
|
||||
private:
|
||||
ThermostatMode mode;
|
||||
float presetTemps[5];
|
||||
float targetTemp;
|
||||
bool heating;
|
||||
};
|
||||
32
include/web_config.h
Normal file
32
include/web_config.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
#include <Arduino.h>
|
||||
|
||||
void setup_web_config();
|
||||
void handle_web_config();
|
||||
bool should_save_config();
|
||||
void save_config();
|
||||
void load_config();
|
||||
|
||||
#define CFG_STRLEN 64
|
||||
|
||||
/**
|
||||
* Config struct for EEPROM storage.
|
||||
*/
|
||||
struct ConfigData
|
||||
{
|
||||
char wifi_ssid[CFG_STRLEN]; // Wifi SSID
|
||||
char wifi_pass[CFG_STRLEN]; // Wifi Password
|
||||
char mqtt_server[CFG_STRLEN]; // MQTT Server
|
||||
char mqtt_user[CFG_STRLEN]; // MQTT User
|
||||
char mqtt_pass[CFG_STRLEN]; // MQTT Password
|
||||
char temp_sensor_id[CFG_STRLEN]; // Temperature Sensor ID
|
||||
char mqtt_device_id[CFG_STRLEN]; // MQTT Device ID
|
||||
char mqtt_temp_topic[CFG_STRLEN]; // MQTT Temperature Topic
|
||||
int mqtt_port; // MQTT Port
|
||||
float preset_confort; // Confort Mode Temperature
|
||||
float preset_eco; // Eco Mode Temperature
|
||||
float preset_boost; // Boost Mode Temperature
|
||||
float preset_hors_gel; // Hors Gel Mode Temperature
|
||||
};
|
||||
|
||||
extern ConfigData config;
|
||||
2
include/web_routes.h
Normal file
2
include/web_routes.h
Normal file
@@ -0,0 +1,2 @@
|
||||
#pragma once
|
||||
void setup_web_routes();
|
||||
4
include/web_server_instance.h
Normal file
4
include/web_server_instance.h
Normal file
@@ -0,0 +1,4 @@
|
||||
#include <ESPAsyncWebServer.h>
|
||||
|
||||
// Declare a global server instance on port 80
|
||||
extern AsyncWebServer server;
|
||||
367
monitor.log
Normal file
367
monitor.log
Normal file
@@ -0,0 +1,367 @@
|
||||
--- Terminal on /dev/ttyUSB0 | 115200 8-N-1
|
||||
--- Available filters and text transformations: debug, default, direct, esp8266_exception_decoder, hexlify, log2file, nocontrol, printable, send_on_enter, time
|
||||
--- More details at https://bit.ly/pio-monitor-filters
|
||||
--- Quit: Ctrl+C | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H
|
||||
␀
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/A4C1388F5F89
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"A4:C1:38:8F:5F:89","mac_type":0,"adv_type":0,"rssi":-80,"servicedata":"895f8f38c1a462074d16e20a536b0e","servicedatauuid":"0x181a","brand":"Xiaomi","model":"TH Sensor","model_id":"LYWSD03MMC/MJW
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/43A69133165F
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"43:A6:91:33:16:5F","mac_type":1,"adv_type":0,"rssi":-83,"servicedata":"0000000000000000000000000000000000000000","servicedatauuid":"0xfe9f"}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/E0036BF020BF
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"E0:03:6B:F0:20:BF","mac_type":0,"adv_type":0,"manufacturerdata":"75004204012066210f0002014101010001000000000000000006","rssi":-66}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/6FF06B19BD96
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"6F:F0:6B:19:BD:96","mac_type":1,"adv_type":0,"rssi":-82}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/66FC2A8FC38F
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"66:FC:2A:8F:C3:8F","mac_type":1,"adv_type":0,"rssi":-88}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/66FC2A8FC38F
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"66:FC:2A:8F:C3:8F","mac_type":1,"adv_type":0,"rssi":-86}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/6FF06B19BD96
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"6F:F0:6B:19:BD:96","mac_type":1,"adv_type":0,"rssi":-82}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/E0036BF020BF
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"E0:03:6B:F0:20:BF","mac_type":0,"adv_type":0,"manufacturerdata":"75004204012066210f0002014101010001000000000000000006","rssi":-66}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/6FF06B19BD96
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"6F:F0:6B:19:BD:96","mac_type":1,"adv_type":0,"rssi":-82}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/E0036BF020BF
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"E0:03:6B:F0:20:BF","mac_type":0,"adv_type":0,"manufacturerdata":"75004204010166e0036bf020bfe2036bf020be01580000000000","rssi":-66}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/66FC2A8FC38F
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"66:FC:2A:8F:C3:8F","mac_type":1,"adv_type":0,"rssi":-87}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/E0036BF020BF
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"E0:03:6B:F0:20:BF","mac_type":0,"adv_type":0,"manufacturerdata":"75004204010166e0036bf020bfe2036bf020be01580000000000","rssi":-74}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/6FF06B19BD96
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"6F:F0:6B:19:BD:96","mac_type":1,"adv_type":0,"rssi":-81}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/A4C13841E067
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"A4:C1:38:41:E0:67","mac_type":0,"adv_type":0,"rssi":-88,"servicedata":"a4c13841e067009542550b9bdd","servicedatauuid":"0x181a","brand":"Xiaomi","model":"TH Sensor","model_id":"LYWSD03MMC/MJWSD05
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/A4C1388F5F89
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"A4:C1:38:8F:5F:89","mac_type":0,"adv_type":0,"rssi":-84,"servicedata":"895f8f38c1a465074d16e20a536c0e","servicedatauuid":"0x181a","brand":"Xiaomi","model":"TH Sensor","model_id":"LYWSD03MMC/MJW
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/66FC2A8FC38F
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"66:FC:2A:8F:C3:8F","mac_type":1,"adv_type":0,"rssi":-91}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/6FF06B19BD96
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"6F:F0:6B:19:BD:96","mac_type":1,"adv_type":0,"rssi":-81}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/E0036BF020BF
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"E0:03:6B:F0:20:BF","mac_type":0,"adv_type":0,"manufacturerdata":"75004204010166e0036bf020bfe2036bf020be01580000000000","rssi":-74}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/43A69133165F
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"43:A6:91:33:16:5F","mac_type":1,"adv_type":0,"rssi":-84,"servicedata":"0000000000000000000000000000000000000000","servicedatauuid":"0xfe9f"}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/E0036BF020BF
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"E0:03:6B:F0:20:BF","mac_type":0,"adv_type":0,"manufacturerdata":"75004204012066210f0002014101010001000000000000000006","rssi":-66}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/43A69133165F
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"43:A6:91:33:16:5F","mac_type":1,"adv_type":0,"rssi":-83,"servicedata":"0000000000000000000000000000000000000000","servicedatauuid":"0xfe9f"}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/66FC2A8FC38F
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"66:FC:2A:8F:C3:8F","mac_type":1,"adv_type":0,"rssi":-90}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/6FF06B19BD96
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"6F:F0:6B:19:BD:96","mac_type":1,"adv_type":0,"rssi":-81}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/66FC2A8FC38F
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"66:FC:2A:8F:C3:8F","mac_type":1,"adv_type":0,"rssi":-87}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/A4C1389ADA64
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"A4:C1:38:9A:DA:64","mac_type":0,"adv_type":0,"rssi":-63,"servicedata":"64da9a38c1a45907a316580a40e60e","servicedatauuid":"0x181a","brand":"Xiaomi","model":"TH Sensor","model_id":"LYWSD03MMC/MJW
|
||||
-> OK! MQTT topic matches temp sensor ID. Processing message...
|
||||
-> SUCCESS! Received tempc from MQTT: 18.81 °C
|
||||
Current temperature (polled): 18.81
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/A4C138CF2D7E
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"A4:C1:38:CF:2D:7E","mac_type":0,"adv_type":0,"rssi":-84,"servicedata":"7e2dcf38c1a43208d812970a49f60e","servicedatauuid":"0x181a","brand":"Xiaomi","model":"TH Sensor","model_id":"LYWSD03MMC/MJW
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/A4C13841E067
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"A4:C1:38:41:E0:67","mac_type":0,"adv_type":0,"rssi":-89,"servicedata":"a4c13841e067009542550b9bdd","servicedatauuid":"0x181a","brand":"Xiaomi","model":"TH Sensor","model_id":"LYWSD03MMC/MJWSD05
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/6FF06B19BD96
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"6F:F0:6B:19:BD:96","mac_type":1,"adv_type":0,"rssi":-82}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/E0036BF020BF
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"E0:03:6B:F0:20:BF","mac_type":0,"adv_type":0,"manufacturerdata":"75004204012066210f0002014101010001000000000000000006","rssi":-66}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/E0036BF020BF
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"E0:03:6B:F0:20:BF","mac_type":0,"adv_type":0,"manufacturerdata":"75004204012066210f0002014101010001000000000000000006","rssi":-73}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/6FF06B19BD96
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"6F:F0:6B:19:BD:96","mac_type":1,"adv_type":0,"rssi":-82}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/A4C13841E067
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"A4:C1:38:41:E0:67","mac_type":0,"adv_type":0,"rssi":-81,"servicedata":"a4c13841e067009542550b9bdd","servicedatauuid":"0x181a","brand":"Xiaomi","model":"TH Sensor","model_id":"LYWSD03MMC/MJWSD05
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/66FC2A8FC38F
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"66:FC:2A:8F:C3:8F","mac_type":1,"adv_type":0,"rssi":-90}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/A4C1388F5F89
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"A4:C1:38:8F:5F:89","mac_type":0,"adv_type":0,"rssi":-81,"servicedata":"895f8f38c1a461074e16e20a536e0e","servicedatauuid":"0x181a","brand":"Xiaomi","model":"TH Sensor","model_id":"LYWSD03MMC/MJW
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/E0036BF020BF
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"E0:03:6B:F0:20:BF","mac_type":0,"adv_type":0,"manufacturerdata":"75004204012066210f0002014101010001000000000000000006","rssi":-66}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/6FF06B19BD96
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"6F:F0:6B:19:BD:96","mac_type":1,"adv_type":0,"rssi":-84}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/43A69133165F
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"43:A6:91:33:16:5F","mac_type":1,"adv_type":0,"rssi":-83,"servicedata":"0000000000000000000000000000000000000000","servicedatauuid":"0xfe9f"}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/A4C138CF2D7E
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"A4:C1:38:CF:2D:7E","mac_type":0,"adv_type":0,"rssi":-78,"servicedata":"7e2dcf38c1a43208d812970a49f60e","servicedatauuid":"0x181a","brand":"Xiaomi","model":"TH Sensor","model_id":"LYWSD03MMC/MJW
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/66FC2A8FC38F
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"66:FC:2A:8F:C3:8F","mac_type":1,"adv_type":0,"rssi":-88}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/43A69133165F
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"43:A6:91:33:16:5F","mac_type":1,"adv_type":0,"rssi":-83,"servicedata":"0000000000000000000000000000000000000000","servicedatauuid":"0xfe9f"}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/6FF06B19BD96
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"6F:F0:6B:19:BD:96","mac_type":1,"adv_type":0,"rssi":-82}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/66FC2A8FC38F
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"66:FC:2A:8F:C3:8F","mac_type":1,"adv_type":0,"rssi":-98}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/E0036BF020BF
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"E0:03:6B:F0:20:BF","mac_type":0,"adv_type":0,"manufacturerdata":"75004204012066210f0002014101010001000000000000000006","rssi":-74}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/6FF06B19BD96
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"6F:F0:6B:19:BD:96","mac_type":1,"adv_type":0,"rssi":-84}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/E0036BF020BF
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"E0:03:6B:F0:20:BF","mac_type":0,"adv_type":0,"name":"[TV] Samsung AU7095 65 TV","manufacturerdata":"75004204012066210f0002014101010001000000000000000006","rssi":-72}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/66FC2A8FC38F
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"66:FC:2A:8F:C3:8F","mac_type":1,"adv_type":0,"rssi":-86}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/A4C13841E067
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"A4:C1:38:41:E0:67","mac_type":0,"adv_type":0,"name":"ATC_41E067","rssi":-88,"servicedata":"a4c13841e067009542550b9bdd","servicedatauuid":"0x181a","brand":"Xiaomi","model":"TH Sensor","model_id"
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/A4C1389ADA64
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"A4:C1:38:9A:DA:64","mac_type":0,"adv_type":0,"rssi":-68,"servicedata":"64da9a38c1a45a079a16580a40e70e","servicedatauuid":"0x181a","brand":"Xiaomi","model":"TH Sensor","model_id":"LYWSD03MMC/MJW
|
||||
-> OK! MQTT topic matches temp sensor ID. Processing message...
|
||||
-> SUCCESS! Received tempc from MQTT: 18.82 °C
|
||||
Current temperature (polled): 18.82
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/E0036BF020BF
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"E0:03:6B:F0:20:BF","mac_type":0,"adv_type":0,"manufacturerdata":"75004204012066210f0002014101010001000000000000000006","rssi":-67}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/6FF06B19BD96
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"6F:F0:6B:19:BD:96","mac_type":1,"adv_type":0,"rssi":-81}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/66FC2A8FC38F
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"66:FC:2A:8F:C3:8F","mac_type":1,"adv_type":0,"rssi":-94}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/E0036BF020BF
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"E0:03:6B:F0:20:BF","mac_type":0,"adv_type":0,"manufacturerdata":"75004204012066210f0002014101010001000000000000000006","rssi":-72}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/6FF06B19BD96
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"6F:F0:6B:19:BD:96","mac_type":1,"adv_type":0,"rssi":-82}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/E0036BF020BF
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"E0:03:6B:F0:20:BF","mac_type":0,"adv_type":0,"manufacturerdata":"75004204012066210f0002014101010001000000000000000006","rssi":-74}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/6FF06B19BD96
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"6F:F0:6B:19:BD:96","mac_type":1,"adv_type":0,"rssi":-83}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/A4C13841E067
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"A4:C1:38:41:E0:67","mac_type":0,"adv_type":0,"rssi":-86,"servicedata":"a4c13841e067009542550b9bdd","servicedatauuid":"0x181a","brand":"Xiaomi","model":"TH Sensor","model_id":"LYWSD03MMC/MJWSD05
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/66FC2A8FC38F
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"66:FC:2A:8F:C3:8F","mac_type":1,"adv_type":0,"rssi":-96}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/66FC2A8FC38F
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"66:FC:2A:8F:C3:8F","mac_type":1,"adv_type":0,"rssi":-93}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/E0036BF020BF
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"E0:03:6B:F0:20:BF","mac_type":0,"adv_type":0,"manufacturerdata":"75004204010166e0036bf020bfe2036bf020be01580000000000","rssi":-72}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/A4C1388F5F89
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"A4:C1:38:8F:5F:89","mac_type":0,"adv_type":0,"rssi":-80,"servicedata":"895f8f38c1a466074e16e30a53700e","servicedatauuid":"0x181a","brand":"Xiaomi","model":"TH Sensor","model_id":"LYWSD03MMC/MJW
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/43A69133165F
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"43:A6:91:33:16:5F","mac_type":1,"adv_type":0,"rssi":-84,"servicedata":"0000000000000000000000000000000000000000","servicedatauuid":"0xfe9f"}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/6FF06B19BD96
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"6F:F0:6B:19:BD:96","mac_type":1,"adv_type":0,"rssi":-83}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/6FF06B19BD96
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"6F:F0:6B:19:BD:96","mac_type":1,"adv_type":0,"rssi":-82}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/E0036BF020BF
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"E0:03:6B:F0:20:BF","mac_type":0,"adv_type":0,"manufacturerdata":"75004204012066210f0002014101010001000000000000000006","rssi":-67}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/66FC2A8FC38F
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"66:FC:2A:8F:C3:8F","mac_type":1,"adv_type":0,"rssi":-94}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/A4C1389ADA64
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"A4:C1:38:9A:DA:64","mac_type":0,"adv_type":0,"rssi":-69,"servicedata":"64da9a38c1a45b078f16590a40e90e","servicedatauuid":"0x181a","brand":"Xiaomi","model":"TH Sensor","model_id":"LYWSD03MMC/MJW
|
||||
-> OK! MQTT topic matches temp sensor ID. Processing message...
|
||||
-> SUCCESS! Received tempc from MQTT: 18.83 °C
|
||||
Current temperature (polled): 18.83
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/66FC2A8FC38F
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"66:FC:2A:8F:C3:8F","mac_type":1,"adv_type":0,"rssi":-86}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/E0036BF020BF
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"E0:03:6B:F0:20:BF","mac_type":0,"adv_type":0,"manufacturerdata":"75004204012066210f0002014101010001000000000000000006","rssi":-74}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/6FF06B19BD96
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"6F:F0:6B:19:BD:96","mac_type":1,"adv_type":0,"rssi":-82}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/A4C13841E067
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"A4:C1:38:41:E0:67","mac_type":0,"adv_type":0,"rssi":-84,"servicedata":"a4c13841e067009542550b9bdd","servicedatauuid":"0x181a","brand":"Xiaomi","model":"TH Sensor","model_id":"LYWSD03MMC/MJWSD05
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/43A69133165F
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"43:A6:91:33:16:5F","mac_type":1,"adv_type":0,"rssi":-83,"servicedata":"0000000000000000000000000000000000000000","servicedatauuid":"0xfe9f"}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/E0036BF020BF
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"E0:03:6B:F0:20:BF","mac_type":0,"adv_type":0,"manufacturerdata":"75004204010166e0036bf020bfe2036bf020be01580000000000","rssi":-67}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/66FC2A8FC38F
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"66:FC:2A:8F:C3:8F","mac_type":1,"adv_type":0,"rssi":-87}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/43A69133165F
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"43:A6:91:33:16:5F","mac_type":1,"adv_type":0,"rssi":-83,"servicedata":"0000000000000000000000000000000000000000","servicedatauuid":"0xfe9f"}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/6FF06B19BD96
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"6F:F0:6B:19:BD:96","mac_type":1,"adv_type":0,"rssi":-81}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/A4C13841E067
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"A4:C1:38:41:E0:67","mac_type":0,"adv_type":0,"rssi":-87,"servicedata":"a4c13841e067009542550b9bdd","servicedatauuid":"0x181a","brand":"Xiaomi","model":"TH Sensor","model_id":"LYWSD03MMC/MJWSD05
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/66FC2A8FC38F
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"66:FC:2A:8F:C3:8F","mac_type":1,"adv_type":0,"rssi":-94}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/43A69133165F
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"43:A6:91:33:16:5F","mac_type":1,"adv_type":0,"rssi":-83,"servicedata":"0000000000000000000000000000000000000000","servicedatauuid":"0xfe9f"}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/6FF06B19BD96
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"6F:F0:6B:19:BD:96","mac_type":1,"adv_type":0,"rssi":-81}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/E0036BF020BF
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"E0:03:6B:F0:20:BF","mac_type":0,"adv_type":0,"manufacturerdata":"75004204010166e0036bf020bfe2036bf020be01580000000000","rssi":-74}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/A4C138CF2D7E
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"A4:C1:38:CF:2D:7E","mac_type":0,"adv_type":0,"rssi":-84,"servicedata":"7e2dcf38c1a42e08d712970a49f90e","servicedatauuid":"0x181a","brand":"Xiaomi","model":"TH Sensor","model_id":"LYWSD03MMC/MJW
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/A4C1388F5F89
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"A4:C1:38:8F:5F:89","mac_type":0,"adv_type":0,"rssi":-80,"servicedata":"895f8f38c1a462074d16e30a53710e","servicedatauuid":"0x181a","brand":"Xiaomi","model":"TH Sensor","model_id":"LYWSD03MMC/MJW
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/E0036BF020BF
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"E0:03:6B:F0:20:BF","mac_type":0,"adv_type":0,"manufacturerdata":"75004204010166e0036bf020bfe2036bf020be01580000000000","rssi":-67}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/66FC2A8FC38F
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"66:FC:2A:8F:C3:8F","mac_type":1,"adv_type":0,"rssi":-87}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/A4C13841E067
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"A4:C1:38:41:E0:67","mac_type":0,"adv_type":0,"rssi":-85,"servicedata":"a4c13841e067009642550b9bde","servicedatauuid":"0x181a","brand":"Xiaomi","model":"TH Sensor","model_id":"LYWSD03MMC/MJWSD05
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/6FF06B19BD96
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"6F:F0:6B:19:BD:96","mac_type":1,"adv_type":0,"rssi":-84}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/43A69133165F
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"43:A6:91:33:16:5F","mac_type":1,"adv_type":0,"rssi":-83,"servicedata":"0000000000000000000000000000000000000000","servicedatauuid":"0xfe9f"}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/E0036BF020BF
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"E0:03:6B:F0:20:BF","mac_type":0,"adv_type":0,"manufacturerdata":"75004204010166e0036bf020bfe2036bf020be01580000000000","rssi":-74}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
Topic: home/OMG_ESP32_BLE/BTtoMQTT/6FF06B19BD96
|
||||
Searching for: 'A4C1389ADA64' in topic
|
||||
Payload: {"id":"6F:F0:6B:19:BD:96","mac_type":1,"adv_type":0,"rssi":-84}
|
||||
-> MQTT topic does not match expected temp sensor ID. Ignoring message.
|
||||
16
platformio.ini
Normal file
16
platformio.ini
Normal file
@@ -0,0 +1,16 @@
|
||||
[env:sonoff_basic]
|
||||
platform = espressif8266
|
||||
board = sonoff_basic
|
||||
framework = arduino
|
||||
monitor_speed = 115200
|
||||
; upload_protocol = espota
|
||||
; upload_port = 192.168.1.95
|
||||
upload_speed = 115200
|
||||
build_flags =
|
||||
-DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY
|
||||
-DMQTT_MAX_PACKET_SIZE=512
|
||||
lib_deps =
|
||||
knolleary/PubSubClient
|
||||
tzapu/WiFiManager
|
||||
bblanchon/ArduinoJson
|
||||
me-no-dev/ESPAsyncWebServer
|
||||
151
src/main.cpp
Normal file
151
src/main.cpp
Normal file
@@ -0,0 +1,151 @@
|
||||
#include <Arduino.h>
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <PubSubClient.h>
|
||||
#include <Ticker.h>
|
||||
#include <ArduinoOTA.h>
|
||||
#include "thermostat.h"
|
||||
#include "mqtt_handler.h"
|
||||
#include "web_config.h"
|
||||
#include "preset.h"
|
||||
#include "web_routes.h"
|
||||
#include "main.h"
|
||||
|
||||
#include "web_server_instance.h"
|
||||
|
||||
WiFiClient espClient;
|
||||
PubSubClient mqttClient(espClient);
|
||||
Thermostat thermostat;
|
||||
Ticker ledTicker;
|
||||
|
||||
float currentTemp = 0.0;
|
||||
float oldTemp = 0.0;
|
||||
|
||||
#define LED_PIN 13 // Sonoff Basic LED pin
|
||||
|
||||
// Toggle LED state
|
||||
void toggleLED() {
|
||||
digitalWrite(LED_PIN, !digitalRead(LED_PIN));
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize serial, load configuration and presets, connect to WiFi and MQTT, and set up the thermostat.
|
||||
*
|
||||
*/
|
||||
void setup() {
|
||||
int i;
|
||||
// Turn on LED to indicate startup (will turn off when setup is complete)
|
||||
pinMode(LED_PIN, OUTPUT);
|
||||
pinMode(RELAY_PIN, OUTPUT);
|
||||
// Start with LED ON to indicate startup
|
||||
digitalWrite(LED_PIN, LOW);
|
||||
digitalWrite(RELAY_PIN, HIGH); // Ensure relay is off at startup
|
||||
ledTicker.attach(0.2, toggleLED);
|
||||
|
||||
Serial.begin(115200);
|
||||
i=0;
|
||||
|
||||
// Retrieve config from EEPROM, connect to WiFi and MQTT, and set up thermostat presets
|
||||
load_config();
|
||||
// Debug: print current config and actions
|
||||
Serial.println("Thermostat Unit Startup - Version 1.0");
|
||||
// Serial.print("WiFi SSID: "); Serial.println(config.wifi_ssid);
|
||||
// Serial.print("MQTT Server: "); Serial.println(config.mqtt_server);
|
||||
// Serial.print("MQTT Port: "); Serial.println(config.mqtt_port);
|
||||
// Serial.print("MQTT User: "); Serial.println(config.mqtt_user);
|
||||
// Serial.print("MQTT Device ID: "); Serial.println(config.mqtt_device_id);
|
||||
// Serial.print("MQTT Temp Topic: "); Serial.println(config.mqtt_temp_topic);
|
||||
// Serial.print("Temp Sensor ID: "); Serial.println(config.temp_sensor_id);
|
||||
// Serial.print("Preset Confort: "); Serial.println(config.preset_confort);
|
||||
// Serial.print("Preset Eco: "); Serial.println(config.preset_eco);
|
||||
// Serial.print("Preset Boost: "); Serial.println(config.preset_boost);
|
||||
// Serial.print("Preset Hors Gel: "); Serial.println(config.preset_hors_gel);
|
||||
|
||||
Serial.println("Connecting to WiFi...");
|
||||
// setup wifi and mqtt, and load presets into thermostat
|
||||
WiFi.begin(config.wifi_ssid, config.wifi_pass);
|
||||
// Display wifi config
|
||||
i=0;
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
|
||||
Serial.print(".");
|
||||
i++;
|
||||
if (i > 30) { // After 30 seconds of trying to connect, start config portal
|
||||
Serial.println("\nFailed to connect to WiFi. Starting config portal...");
|
||||
ledTicker.attach(0.5, toggleLED);
|
||||
setup_web_config();
|
||||
break;
|
||||
}
|
||||
delay(1000);
|
||||
}
|
||||
Serial.println("\nConnected to WiFi!");
|
||||
ledTicker.detach();
|
||||
digitalWrite(LED_PIN, LOW);
|
||||
// Display IP
|
||||
Serial.print("IP Address: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
setup_web_routes();
|
||||
server.begin();
|
||||
Serial.println("Connecting to MQTT...");
|
||||
mqttClient.setServer(config.mqtt_server, config.mqtt_port);
|
||||
mqttClient.setCallback(mqtt_callback);
|
||||
|
||||
// Display MQTT buffer size
|
||||
Serial.print("MQTT buffer size: ");
|
||||
Serial.print(mqttClient.getBufferSize());
|
||||
Serial.println(" bytes");
|
||||
|
||||
Serial.print("MQTT Client configured. Attempting to connect...");
|
||||
mqttClient.connect(config.mqtt_device_id, config.mqtt_user, config.mqtt_pass);
|
||||
|
||||
if (mqttClient.connected()) {
|
||||
Serial.println("MQTT Connected!");
|
||||
// Subscribe with wildcard to get all sensors
|
||||
String topic = String(config.mqtt_temp_topic) + "/#";
|
||||
Serial.print("Subscribing to topic: ");
|
||||
Serial.println(topic);
|
||||
bool subResult = mqttClient.subscribe(topic.c_str());
|
||||
Serial.print("Subscription result: ");
|
||||
Serial.println(subResult ? "SUCCESS" : "FAILED");
|
||||
} else {
|
||||
Serial.print("MQTT Connection FAILED, state: ");
|
||||
Serial.println(mqttClient.state());
|
||||
}
|
||||
|
||||
// Start LED blinking to indicate normal operation (blink every 2 seconds)
|
||||
ledTicker.attach(2.0, toggleLED);
|
||||
|
||||
Serial.println("Setup complete. Entering main loop...");
|
||||
|
||||
delay(1000); // Short delay to ensure LED state is visible before turning off
|
||||
digitalWrite(RELAY_PIN, HIGH); // Ensure relay is on
|
||||
delay(1000); // Short delay to ensure relay state is stable before turning off LED
|
||||
digitalWrite(RELAY_PIN, LOW);
|
||||
// Publsh initial state to MQTT to inform Home Assistant of current thermostat status on startup
|
||||
|
||||
mqtt_publish_state(mqttClient, thermostat.getMode(), thermostat.getTargetTemp(), thermostat.isHeating());
|
||||
}
|
||||
|
||||
int j = 0;
|
||||
void loop()
|
||||
{
|
||||
// Handle OTA updates
|
||||
ArduinoOTA.handle();
|
||||
|
||||
// Ask Mqtt to process incoming messages and maintain connection
|
||||
mqtt_loop(mqttClient);
|
||||
|
||||
// Poll the current temperature (updated asynchronously by MQTT callback)
|
||||
if (currentTemp != oldTemp) {
|
||||
Serial.print("Temperature change detected: ");
|
||||
Serial.println(currentTemp);
|
||||
oldTemp = currentTemp;
|
||||
}
|
||||
|
||||
// // Update thermostat state based on current temp and publish state to MQTT
|
||||
// thermostat.update(currentTemp);
|
||||
// // Publish state to MQTT every second (or on state change, if you want to optimize)
|
||||
// mqtt_publish_state(mqttClient, thermostat.getMode(), thermostat.getTargetTemp(), thermostat.isHeating());
|
||||
// // Delay to avoid flooding MQTT with messages. Adjust as needed for your use case (e.g., publish on state change
|
||||
// // instead of every loop).
|
||||
delay(10);
|
||||
}
|
||||
103
src/mqtt_handler.cpp
Normal file
103
src/mqtt_handler.cpp
Normal file
@@ -0,0 +1,103 @@
|
||||
|
||||
#include "mqtt_handler.h"
|
||||
#include "thermostat.h"
|
||||
#include <ArduinoJson.h>
|
||||
#include "web_config.h"
|
||||
|
||||
// Helper function to normalize MAC address (remove colons, convert to uppercase)
|
||||
String normalizeMac(const char* mac) {
|
||||
String normalized = "";
|
||||
for (int i = 0; mac[i] != '\0'; i++) {
|
||||
if (mac[i] != ':') {
|
||||
normalized += (char)toupper(mac[i]);
|
||||
}
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
|
||||
// Variables now defined in web_config.cpp
|
||||
|
||||
// Process incoming MQTT messages asynchronously
|
||||
// The callback (mqtt_callback) is invoked automatically when messages arrive
|
||||
void mqtt_loop(PubSubClient& client) {
|
||||
client.loop();
|
||||
}
|
||||
|
||||
|
||||
void mqtt_publish_state(PubSubClient& client, ThermostatMode mode, float targetTemp, bool heating) {
|
||||
JsonDocument doc;
|
||||
doc["mode"] = mode;
|
||||
doc["target"] = targetTemp;
|
||||
doc["heating"] = heating;
|
||||
char buf[128];
|
||||
size_t n = serializeJson(doc, buf);
|
||||
String topic = String("home/thermostat/") + String(config.mqtt_device_id) + String("/state");
|
||||
client.publish(topic.c_str(), buf, n);
|
||||
}
|
||||
|
||||
void mqtt_callback(char* topic, byte* payload, unsigned int length)
|
||||
{
|
||||
static bool first_call = true;
|
||||
static String normalized_sensor_id;
|
||||
|
||||
if (first_call) {
|
||||
// Serial.print("Looking for sensor ID: ");
|
||||
// Serial.println(config.temp_sensor_id);
|
||||
|
||||
// FIX: Normalize the sensor ID to match topic format
|
||||
// Topics come as: home/OMG_ESP32_BLE/BTtoMQTT/A4C1389ADA64 (no colons, uppercase)
|
||||
// Config may have: A4:C1:38:9A:DA:64 (with colons)
|
||||
// Solution: Remove colons and convert to uppercase for comparison
|
||||
normalized_sensor_id = normalizeMac(config.temp_sensor_id);
|
||||
|
||||
// TESTING: Uncomment to test with a sensor that's actually broadcasting
|
||||
// normalized_sensor_id = "A4C1389ADA64"; // Your Xiaomi (NOT broadcasting)
|
||||
// normalized_sensor_id = "E0036BF020BF"; // Test with active sensor
|
||||
// normalized_sensor_id = "74CBD1F131B6"; // Test with active sensor
|
||||
|
||||
// Serial.print("Normalized sensor ID: ");
|
||||
// Serial.println(normalized_sensor_id);
|
||||
first_call = false;
|
||||
}
|
||||
|
||||
// Serial.print("Topic: ");
|
||||
// Serial.println(topic);
|
||||
|
||||
// Debug: Print what we're comparing
|
||||
// Serial.print(" Searching for: '");
|
||||
// Serial.print(normalized_sensor_id);
|
||||
// Serial.println("' in topic");
|
||||
|
||||
// Print payload for debugging (first 200 chars)
|
||||
// Serial.print(" Payload: ");
|
||||
// for (unsigned int i = 0; i < length && i < 200; i++) {
|
||||
// Serial.write(payload[i]);
|
||||
// }
|
||||
// Serial.println();
|
||||
|
||||
// Check if topic contains the normalized sensor ID
|
||||
if(strstr(topic, normalized_sensor_id.c_str()) == nullptr) {
|
||||
// Serial.println(" -> MQTT topic does not match expected temp sensor ID. Ignoring message.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse JSON payload from subscribed topic
|
||||
JsonDocument doc;
|
||||
// Serial.println(" -> OK! MQTT topic matches temp sensor ID. Processing message...");
|
||||
DeserializationError err = deserializeJson(doc, payload, length);
|
||||
if (err) {
|
||||
Serial.print(" -> MQTT JSON parse error: ");
|
||||
Serial.println(err.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for temperature field
|
||||
if (doc["tempc"].is<float>()) {
|
||||
currentTemp = doc["tempc"].as<float>();
|
||||
// Serial.print(" -> SUCCESS! Received tempc from MQTT: ");
|
||||
// Serial.print(currentTemp);
|
||||
// Serial.println(" °C");
|
||||
} else {
|
||||
Serial.println(" -> WARNING: No 'tempc' field found in JSON payload");
|
||||
}
|
||||
}
|
||||
39
src/preset.cpp
Normal file
39
src/preset.cpp
Normal file
@@ -0,0 +1,39 @@
|
||||
#include "preset.h"
|
||||
#include <EEPROM.h>
|
||||
|
||||
static float presets[5] = {0, 20.0, 17.0, 22.0, 7.0};
|
||||
#define PRESET_EEPROM_START 0
|
||||
#define PRESET_COUNT 5
|
||||
#define PRESET_EEPROM_SIZE (PRESET_COUNT * sizeof(float))
|
||||
|
||||
void load_presets() {
|
||||
EEPROM.begin(PRESET_EEPROM_SIZE);
|
||||
bool valid = true;
|
||||
for (int i = 0; i < PRESET_COUNT; ++i) {
|
||||
float value = 0;
|
||||
EEPROM.get(PRESET_EEPROM_START + i * sizeof(float), value);
|
||||
// Check for uninitialized EEPROM (NaN or out of range)
|
||||
if (isnan(value) || value < 0 || value > 50) {
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
presets[i] = value;
|
||||
}
|
||||
if (!valid) {
|
||||
// If invalid, write defaults
|
||||
save_presets();
|
||||
}
|
||||
}
|
||||
void save_presets() {
|
||||
for (int i = 0; i < PRESET_COUNT; ++i) {
|
||||
EEPROM.put(PRESET_EEPROM_START + i * sizeof(float), presets[i]);
|
||||
}
|
||||
EEPROM.commit();
|
||||
}
|
||||
float get_preset_temp(ThermostatMode mode) {
|
||||
return presets[mode];
|
||||
}
|
||||
void set_preset_temp(ThermostatMode mode, float temp) {
|
||||
presets[mode] = temp;
|
||||
save_presets();
|
||||
}
|
||||
50
src/thermostat.cpp
Normal file
50
src/thermostat.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
#include "thermostat.h"
|
||||
|
||||
Thermostat::Thermostat() : mode(MODE_OFF), targetTemp(0), heating(false) {
|
||||
presetTemps[MODE_CONFORT] = 20.0;
|
||||
presetTemps[MODE_ECO] = 17.0;
|
||||
presetTemps[MODE_BOOST] = 22.0;
|
||||
presetTemps[MODE_HORS_GEL] = 7.0;
|
||||
presetTemps[MODE_OFF] = 0.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the thermostat mode. This will automatically update the target temperature to the preset for that mode.
|
||||
*/
|
||||
void Thermostat::setMode(ThermostatMode m) {
|
||||
mode = m;
|
||||
targetTemp = presetTemps[mode];
|
||||
}
|
||||
|
||||
/**
|
||||
* Manually set the target temperature. This does not change the mode or presets, but will override the preset for the
|
||||
* current mode until the mode is changed again.
|
||||
*/
|
||||
void Thermostat::setTemperature(float temp) {
|
||||
targetTemp = temp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the preset temperature for a specific mode. If the current mode is the one being updated, also update the
|
||||
* target temperature.
|
||||
*/
|
||||
void Thermostat::setPresetTemp(ThermostatMode m, float temp) {
|
||||
presetTemps[m] = temp;
|
||||
if (mode == m) targetTemp = temp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the heating state based on the current temperature and target temperature.
|
||||
* this is real simple logic: if current temp is below target, turn on heating, otherwise turn it off. In a real system, you would want to add some hysteresis to prevent rapid on/off cycling.
|
||||
*/
|
||||
void Thermostat::update(float currentTemp) {
|
||||
if (mode == MODE_OFF) {
|
||||
heating = false;
|
||||
} else {
|
||||
heating = (currentTemp < targetTemp);
|
||||
}
|
||||
}
|
||||
|
||||
bool Thermostat::isHeating() const { return heating; }
|
||||
ThermostatMode Thermostat::getMode() const { return mode; }
|
||||
float Thermostat::getTargetTemp() const { return targetTemp; }
|
||||
126
src/web_config.cpp
Normal file
126
src/web_config.cpp
Normal file
@@ -0,0 +1,126 @@
|
||||
|
||||
#include "web_config.h"
|
||||
#include <WiFiManager.h>
|
||||
#include <EEPROM.h>
|
||||
|
||||
|
||||
// String wifi_ssid, wifi_pass, mqtt_server, mqtt_user, mqtt_pass, temp_sensor_id;
|
||||
// String mqtt_device_id = "thermo1";
|
||||
// String mqtt_temp_topic = "home/room/temperature/thermo1";
|
||||
// int mqtt_port = 1883;
|
||||
// float preset_confort = 21.0, preset_eco = 18.0, preset_boost = 23.0, preset_hors_gel = 7.0;
|
||||
bool shouldSaveConfigFlag = false;
|
||||
|
||||
#define CFG_EEPROM_START 100
|
||||
#define CFG_EEPROM_SIZE 512
|
||||
|
||||
|
||||
|
||||
|
||||
ConfigData config; // Global config variable to hold current config in memory.
|
||||
// This is what is read/written to EEPROM, and used by the rest of the code.
|
||||
/**
|
||||
* Config saving. In a production system, consider adding error handling and validation (e.g., check string lengths,
|
||||
* valid port range, etc.).
|
||||
* NB: config is stored as a struct for simplicity,
|
||||
*/
|
||||
void save_config() {
|
||||
// Save config to EEPROM
|
||||
EEPROM.begin(CFG_EEPROM_START + sizeof(ConfigData));
|
||||
EEPROM.put(CFG_EEPROM_START, config);
|
||||
EEPROM.commit();
|
||||
// Reboot device to apply new config (e.g., reconnect to WiFi with new credentials)
|
||||
ESP.restart();
|
||||
}
|
||||
|
||||
/**
|
||||
* Config loading from EEPROM.
|
||||
* NB: config is stored as a struct for simplicity.
|
||||
*
|
||||
*/
|
||||
void load_config() {
|
||||
EEPROM.begin(CFG_EEPROM_START + sizeof(ConfigData));
|
||||
EEPROM.get(CFG_EEPROM_START, config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Web config setup. This will block until WiFi is configured, which is fine for initial setup but may not be ideal
|
||||
* for all use cases.
|
||||
*
|
||||
* If config parameters are defined in EEPROm, try to connect to WiFi. If connection fails, start config portal.
|
||||
* If parameters are not defined, start config portal directly.
|
||||
* On config save, set shouldSaveConfigFlag to true so that main loop can save the config to EEPROM.
|
||||
* This is a bit of a hack, but it allows us to avoid blocking the main loop while waiting for user input.
|
||||
*/
|
||||
void setup_web_config() {
|
||||
WiFiManager wm;
|
||||
|
||||
// WiFiManagerParameter: id, placeholder, default, length
|
||||
WiFiManagerParameter custom_mqtt_server("mqtt_server", "MQTT Server", config.mqtt_server, CFG_STRLEN);
|
||||
WiFiManagerParameter custom_mqtt_port("mqtt_port", "MQTT Port", String(config.mqtt_port).c_str(), 6);
|
||||
WiFiManagerParameter custom_mqtt_user("mqtt_user", "MQTT User", config.mqtt_user, CFG_STRLEN);
|
||||
WiFiManagerParameter custom_mqtt_pass("mqtt_pass", "MQTT Password", config.mqtt_pass, CFG_STRLEN);
|
||||
WiFiManagerParameter custom_temp_sensor_id("temp_sensor_id", "Temp Sensor ID", config.temp_sensor_id, CFG_STRLEN);
|
||||
WiFiManagerParameter custom_mqtt_device_id("mqtt_device_id", "MQTT Device ID", config.mqtt_device_id, CFG_STRLEN);
|
||||
WiFiManagerParameter custom_mqtt_temp_topic("mqtt_temp_topic", "MQTT Temp Topic", config.mqtt_temp_topic, CFG_STRLEN);
|
||||
WiFiManagerParameter custom_preset_confort("preset_confort", "Preset Confort", String(config.preset_confort).c_str(), 8);
|
||||
WiFiManagerParameter custom_preset_eco("preset_eco", "Preset Eco", String(config.preset_eco).c_str(), 8);
|
||||
WiFiManagerParameter custom_preset_boost("preset_boost", "Preset Boost", String(config.preset_boost).c_str(), 8);
|
||||
WiFiManagerParameter custom_preset_hors_gel("preset_hors_gel", "Preset Hors Gel", String(config.preset_hors_gel).c_str(), 8);
|
||||
|
||||
wm.addParameter(&custom_mqtt_server);
|
||||
wm.addParameter(&custom_mqtt_port);
|
||||
wm.addParameter(&custom_mqtt_user);
|
||||
wm.addParameter(&custom_mqtt_pass);
|
||||
wm.addParameter(&custom_temp_sensor_id);
|
||||
wm.addParameter(&custom_mqtt_device_id);
|
||||
wm.addParameter(&custom_mqtt_temp_topic);
|
||||
wm.addParameter(&custom_preset_confort);
|
||||
wm.addParameter(&custom_preset_eco);
|
||||
wm.addParameter(&custom_preset_boost);
|
||||
wm.addParameter(&custom_preset_hors_gel);
|
||||
|
||||
wm.setSaveConfigCallback([]() {
|
||||
shouldSaveConfigFlag = true;
|
||||
});
|
||||
|
||||
if (!wm.autoConnect("ThermostatConfig")) {
|
||||
// If failed to connect or configure, restart or handle error
|
||||
ESP.restart();
|
||||
}
|
||||
|
||||
// Always update WiFi credentials after connection
|
||||
String ssid = WiFi.SSID();
|
||||
String pass = WiFi.psk();
|
||||
ssid.toCharArray(config.wifi_ssid, CFG_STRLEN);
|
||||
pass.toCharArray(config.wifi_pass, CFG_STRLEN);
|
||||
|
||||
// If config was changed, update variables and save
|
||||
if (shouldSaveConfigFlag) {
|
||||
strncpy(config.mqtt_server, custom_mqtt_server.getValue(), CFG_STRLEN);
|
||||
config.mqtt_server[CFG_STRLEN-1] = '\0';
|
||||
config.mqtt_port = String(custom_mqtt_port.getValue()).toInt();
|
||||
strncpy(config.mqtt_user, custom_mqtt_user.getValue(), CFG_STRLEN);
|
||||
config.mqtt_user[CFG_STRLEN-1] = '\0';
|
||||
strncpy(config.mqtt_pass, custom_mqtt_pass.getValue(), CFG_STRLEN);
|
||||
config.mqtt_pass[CFG_STRLEN-1] = '\0';
|
||||
strncpy(config.temp_sensor_id, custom_temp_sensor_id.getValue(), CFG_STRLEN);
|
||||
config.temp_sensor_id[CFG_STRLEN-1] = '\0';
|
||||
strncpy(config.mqtt_device_id, custom_mqtt_device_id.getValue(), CFG_STRLEN);
|
||||
config.mqtt_device_id[CFG_STRLEN-1] = '\0';
|
||||
strncpy(config.mqtt_temp_topic, custom_mqtt_temp_topic.getValue(), CFG_STRLEN);
|
||||
config.mqtt_temp_topic[CFG_STRLEN-1] = '\0';
|
||||
config.preset_confort = String(custom_preset_confort.getValue()).toFloat();
|
||||
config.preset_eco = String(custom_preset_eco.getValue()).toFloat();
|
||||
config.preset_boost = String(custom_preset_boost.getValue()).toFloat();
|
||||
config.preset_hors_gel = String(custom_preset_hors_gel.getValue()).toFloat();
|
||||
save_config();
|
||||
shouldSaveConfigFlag = false;
|
||||
}
|
||||
}
|
||||
|
||||
void handle_web_config() {
|
||||
// No-op: all web server logic moved to web_routes.cpp
|
||||
}
|
||||
|
||||
bool should_save_config() { return shouldSaveConfigFlag; }
|
||||
126
src/web_routes.cpp
Normal file
126
src/web_routes.cpp
Normal file
@@ -0,0 +1,126 @@
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include "web_server_instance.h"
|
||||
#include "web_config.h"
|
||||
#include <string.h>
|
||||
|
||||
const char* config_html_template = R"HTML(
|
||||
<html>
|
||||
<head>
|
||||
<meta name='viewport' content='width=device-width, initial-scale=1'>
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; background: #f7f7f7; margin: 0; padding: 0; }
|
||||
.container { max-width: 400px; margin: 30px auto; background: #fff; border-radius: 8px; box-shadow: 0 2px 8px #ccc; padding: 20px; }
|
||||
h2 { text-align: center; color: #333; }
|
||||
form { display: flex; flex-direction: column; }
|
||||
label { margin-top: 12px; color: #555; font-size: 1em; }
|
||||
input[type='text'], input[type='password'], input[type='number'] {
|
||||
padding: 8px; margin-top: 4px; border: 1px solid #ccc; border-radius: 4px; font-size: 1em;
|
||||
}
|
||||
input[type='submit'] {
|
||||
margin-top: 18px; padding: 12px; background: #007bff; color: #fff; border: none; border-radius: 4px; font-size: 1.1em; cursor: pointer;
|
||||
}
|
||||
input[type='submit']:active { background: #0056b3; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class='container'>
|
||||
<h2>Thermostat Config</h2>
|
||||
<form method='POST' action='/config'>
|
||||
<label>WiFi SSID:</label><input name='wifi_ssid' type='text' value='%WIFI_SSID%'>
|
||||
<label>WiFi Pass:</label><input name='wifi_pass' type='password' value='%WIFI_PASS%'>
|
||||
<label>MQTT Server:</label><input name='mqtt_server' type='text' value='%MQTT_SERVER%'>
|
||||
<label>MQTT User:</label><input name='mqtt_user' type='text' value='%MQTT_USER%'>
|
||||
<label>MQTT Pass:</label><input name='mqtt_pass' type='password' value='%MQTT_PASS%'>
|
||||
<label>Temp Sensor ID:</label><input name='temp_sensor_id' type='text' value='%TEMP_SENSOR_ID%'>
|
||||
<label>MQTT Device ID:</label><input name='mqtt_device_id' type='text' value='%MQTT_DEVICE_ID%'>
|
||||
<label>MQTT Temp Topic:</label><input name='mqtt_temp_topic' type='text' value='%MQTT_TEMP_TOPIC%'>
|
||||
<label>MQTT Port:</label><input name='mqtt_port' type='number' value='%MQTT_PORT%'>
|
||||
<label>Preset Confort:</label><input name='preset_confort' type='number' step='0.1' value='%PRESET_CONFORT%'>
|
||||
<label>Preset Eco:</label><input name='preset_eco' type='number' step='0.1' value='%PRESET_ECO%'>
|
||||
<label>Preset Boost:</label><input name='preset_boost' type='number' step='0.1' value='%PRESET_BOOST%'>
|
||||
<label>Preset Hors Gel:</label><input name='preset_hors_gel' type='number' step='0.1' value='%PRESET_HORS_GEL%'>
|
||||
<input type='submit' value='Save'>
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
)HTML";
|
||||
|
||||
String render_config_html() {
|
||||
String html(config_html_template);
|
||||
html.replace("%WIFI_SSID%", String(config.wifi_ssid));
|
||||
html.replace("%WIFI_PASS%", String(config.wifi_pass));
|
||||
html.replace("%MQTT_SERVER%", String(config.mqtt_server));
|
||||
html.replace("%MQTT_USER%", String(config.mqtt_user));
|
||||
html.replace("%MQTT_PASS%", String(config.mqtt_pass));
|
||||
html.replace("%TEMP_SENSOR_ID%", String(config.temp_sensor_id));
|
||||
html.replace("%MQTT_DEVICE_ID%", String(config.mqtt_device_id));
|
||||
html.replace("%MQTT_TEMP_TOPIC%", String(config.mqtt_temp_topic));
|
||||
html.replace("%MQTT_PORT%", String(config.mqtt_port));
|
||||
html.replace("%PRESET_CONFORT%", String(config.preset_confort));
|
||||
html.replace("%PRESET_ECO%", String(config.preset_eco));
|
||||
html.replace("%PRESET_BOOST%", String(config.preset_boost));
|
||||
html.replace("%PRESET_HORS_GEL%", String(config.preset_hors_gel));
|
||||
return html;
|
||||
}
|
||||
|
||||
void setup_web_routes() {
|
||||
// Serve config form at / and /config
|
||||
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||
request->send(200, "text/html", render_config_html());
|
||||
});
|
||||
server.on("/config", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||
request->send(200, "text/html", render_config_html());
|
||||
});
|
||||
server.on("/config", HTTP_POST, [](AsyncWebServerRequest *request){
|
||||
if (request->hasParam("wifi_ssid", true)) {
|
||||
strncpy(config.wifi_ssid, request->getParam("wifi_ssid", true)->value().c_str(), CFG_STRLEN);
|
||||
config.wifi_ssid[CFG_STRLEN-1] = '\0';
|
||||
}
|
||||
if (request->hasParam("wifi_pass", true)) {
|
||||
strncpy(config.wifi_pass, request->getParam("wifi_pass", true)->value().c_str(), CFG_STRLEN);
|
||||
config.wifi_pass[CFG_STRLEN-1] = '\0';
|
||||
}
|
||||
if (request->hasParam("mqtt_server", true)) {
|
||||
strncpy(config.mqtt_server, request->getParam("mqtt_server", true)->value().c_str(), CFG_STRLEN);
|
||||
config.mqtt_server[CFG_STRLEN-1] = '\0';
|
||||
}
|
||||
if (request->hasParam("mqtt_user", true)) {
|
||||
strncpy(config.mqtt_user, request->getParam("mqtt_user", true)->value().c_str(), CFG_STRLEN);
|
||||
config.mqtt_user[CFG_STRLEN-1] = '\0';
|
||||
}
|
||||
if (request->hasParam("mqtt_pass", true)) {
|
||||
strncpy(config.mqtt_pass, request->getParam("mqtt_pass", true)->value().c_str(), CFG_STRLEN);
|
||||
config.mqtt_pass[CFG_STRLEN-1] = '\0';
|
||||
}
|
||||
if (request->hasParam("temp_sensor_id", true)) {
|
||||
strncpy(config.temp_sensor_id, request->getParam("temp_sensor_id", true)->value().c_str(), CFG_STRLEN);
|
||||
config.temp_sensor_id[CFG_STRLEN-1] = '\0';
|
||||
}
|
||||
if (request->hasParam("mqtt_device_id", true)) {
|
||||
strncpy(config.mqtt_device_id, request->getParam("mqtt_device_id", true)->value().c_str(), CFG_STRLEN);
|
||||
config.mqtt_device_id[CFG_STRLEN-1] = '\0';
|
||||
}
|
||||
if (request->hasParam("mqtt_temp_topic", true)) {
|
||||
strncpy(config.mqtt_temp_topic, request->getParam("mqtt_temp_topic", true)->value().c_str(), CFG_STRLEN);
|
||||
config.mqtt_temp_topic[CFG_STRLEN-1] = '\0';
|
||||
}
|
||||
if (request->hasParam("mqtt_port", true)) {
|
||||
config.mqtt_port = request->getParam("mqtt_port", true)->value().toInt();
|
||||
}
|
||||
if (request->hasParam("preset_confort", true)) {
|
||||
config.preset_confort = request->getParam("preset_confort", true)->value().toFloat();
|
||||
}
|
||||
if (request->hasParam("preset_eco", true)) {
|
||||
config.preset_eco = request->getParam("preset_eco", true)->value().toFloat();
|
||||
}
|
||||
if (request->hasParam("preset_boost", true)) {
|
||||
config.preset_boost = request->getParam("preset_boost", true)->value().toFloat();
|
||||
}
|
||||
if (request->hasParam("preset_hors_gel", true)) {
|
||||
config.preset_hors_gel = request->getParam("preset_hors_gel", true)->value().toFloat();
|
||||
}
|
||||
save_config();
|
||||
request->send(200, "text/html", "<html><body><h2>Config Saved!</h2><a href='/config'>Back</a></body></html>");
|
||||
});
|
||||
}
|
||||
3
src/web_server_instance.cpp
Normal file
3
src/web_server_instance.cpp
Normal file
@@ -0,0 +1,3 @@
|
||||
#include <ESPAsyncWebServer.h>
|
||||
|
||||
AsyncWebServer server(80); // Global server instance
|
||||
Reference in New Issue
Block a user