Ajout Thermostat

This commit is contained in:
Serge NOEL
2026-03-11 11:50:45 +01:00
parent d3db51dba2
commit ab74dc5fbe
14 changed files with 496 additions and 0 deletions

63
Thermostat/README.md Normal file
View File

@@ -0,0 +1,63 @@
# 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).
### 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`
## Advanced
- Fallback to AP mode if WiFi or MQTT fails
- All configuration is stored in flash and can be reset via the web portal
## License
MIT

View 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="21" step="0.1"></label>
<label>Eco Temp (°C)<input name="eco" type="number" value="18" step="0.1"></label>
<label>Boost Temp (°C)<input name="boost" type="number" value="23" 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>

View File

@@ -0,0 +1,10 @@
#pragma once
#include <Arduino.h>
#include <PubSubClient.h>
void setup_mqtt(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);
extern String mqtt_device_id;
extern String mqtt_temp_topic;

View 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);

View 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;
};

View File

@@ -0,0 +1,19 @@
#pragma once
#include <Arduino.h>
void setup_web_config();
void handle_web_config();
bool should_save_config();
void save_config();
void load_config();
extern String wifi_ssid;
extern String wifi_pass;
extern String mqtt_server;
extern int mqtt_port;
extern String mqtt_user;
extern String mqtt_pass;
extern float preset_confort;
extern float preset_eco;
extern float preset_boost;
extern float preset_hors_gel;
extern String temp_sensor_id;

13
Thermostat/platformio.ini Normal file
View File

@@ -0,0 +1,13 @@
[env:sonoff_basic_r2]
platform = espressif8266
board = sonoff_basic_r2
framework = arduino
monitor_speed = 115200
upload_speed = 115200
build_flags = -DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY
lib_deps =
knolleary/PubSubClient
tzapu/WiFiManager
bblanchon/ArduinoJson
ESPAsyncWebServer
ESPAsyncTCP

36
Thermostat/src/main.cpp Normal file
View File

@@ -0,0 +1,36 @@
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include "thermostat.h"
#include "mqtt_handler.h"
#include "web_config.h"
#include "preset.h"
WiFiClient espClient;
PubSubClient mqttClient(espClient);
Thermostat thermostat;
float currentTemp = 0.0;
void setup() {
Serial.begin(115200);
load_config();
setup_web_config();
WiFi.begin(wifi_ssid.c_str(), wifi_pass.c_str());
mqttClient.setServer(mqtt_server.c_str(), mqtt_port);
setup_mqtt(mqttClient);
load_presets();
thermostat.setPresetTemp(MODE_CONFORT, preset_confort);
thermostat.setPresetTemp(MODE_ECO, preset_eco);
thermostat.setPresetTemp(MODE_BOOST, preset_boost);
thermostat.setPresetTemp(MODE_HORS_GEL, preset_hors_gel);
thermostat.setMode(MODE_OFF);
}
void loop() {
handle_web_config();
mqtt_loop(mqttClient);
thermostat.update(currentTemp);
mqtt_publish_state(mqttClient, thermostat.getMode(), thermostat.getTargetTemp(), thermostat.isHeating());
delay(1000);
}

View File

@@ -0,0 +1,33 @@
#include "mqtt_handler.h"
#include "thermostat.h"
#include <ArduinoJson.h>
String mqtt_device_id = "thermo1";
String mqtt_temp_topic = "home/room/temperature/thermo1";
void setup_mqtt(PubSubClient& client) {
// Setup MQTT connection, subscribe to temp topic
client.setCallback(mqtt_callback);
client.subscribe(mqtt_temp_topic.c_str());
}
void mqtt_loop(PubSubClient& client) {
if (!client.connected()) {
// reconnect logic here
}
client.loop();
}
void mqtt_publish_state(PubSubClient& client, ThermostatMode mode, float targetTemp, bool heating) {
StaticJsonDocument<128> doc;
doc["mode"] = mode;
doc["target"] = targetTemp;
doc["heating"] = heating;
char buf[128];
size_t n = serializeJson(doc, buf);
client.publish(("home/thermostat/" + mqtt_device_id + "/state").c_str(), buf, n);
}
void mqtt_callback(char* topic, byte* payload, unsigned int length) {
// Handle incoming MQTT messages (e.g., set mode, set preset, etc.)
}

18
Thermostat/src/preset.cpp Normal file
View File

@@ -0,0 +1,18 @@
#include "preset.h"
#include <EEPROM.h>
static float presets[5] = {0, 21.0, 18.0, 23.0, 7.0};
void load_presets() {
// Load from EEPROM or SPIFFS
}
void save_presets() {
// Save to EEPROM or SPIFFS
}
float get_preset_temp(ThermostatMode mode) {
return presets[mode];
}
void set_preset_temp(ThermostatMode mode, float temp) {
presets[mode] = temp;
save_presets();
}

View File

@@ -0,0 +1,35 @@
#include "thermostat.h"
Thermostat::Thermostat() : mode(MODE_OFF), targetTemp(0), heating(false) {
presetTemps[MODE_CONFORT] = 21.0;
presetTemps[MODE_ECO] = 18.0;
presetTemps[MODE_BOOST] = 23.0;
presetTemps[MODE_HORS_GEL] = 7.0;
presetTemps[MODE_OFF] = 0.0;
}
void Thermostat::setMode(ThermostatMode m) {
mode = m;
targetTemp = presetTemps[mode];
}
void Thermostat::setTemperature(float temp) {
targetTemp = temp;
}
void Thermostat::setPresetTemp(ThermostatMode m, float temp) {
presetTemps[m] = temp;
if (mode == m) targetTemp = temp;
}
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; }

View File

@@ -0,0 +1,29 @@
#include "web_config.h"
#include <WiFiManager.h>
String wifi_ssid, wifi_pass, mqtt_server, mqtt_user, mqtt_pass, temp_sensor_id;
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;
void save_config() {
// Save config to SPIFFS or EEPROM
}
void load_config() {
// Load config from SPIFFS or EEPROM
}
void setup_web_config() {
WiFiManager wm;
// Add custom parameters for MQTT, presets, sensor id
// On save, set shouldSaveConfigFlag = true
wm.autoConnect("ThermostatConfig");
if (shouldSaveConfigFlag) save_config();
}
void handle_web_config() {
// Handle web config portal if needed
}
bool should_save_config() { return shouldSaveConfigFlag; }