HowTo: Vloerverwarming temp per groep in Home Assistant

Pagina: 1
Acties:

Vraag


  • nils_vdg
  • Registratie: November 2002
  • Laatst online: 02-12 07:42
Op verzoek vanuit een ander topic, hierbij mijn uitleg hoe ik mijn vloerverwarming temperatuur per groep kan uitlezen in Home Assistant.

Het is een vrij uitgebreide tutorial geworden omdat hij redelijk op noob-niveau is geschreven.
Ik ben zelf ook namelijk ook helemaal geen programmeur, en heb de code volledig geschreven met hulp van AI.

Het doel is het waterzijdig inregelen van de vloerverwarming. Ofwel; eenzelfde delta T per groep, ten behoeve van een efficiënte werking van je warmtepomp of CV.

Het vraagt enige investering, maar voor mij was het een simpel sommetje.
Waterzijdig inregelen via een extern bedrijf kost 70 euro per groep. Ik heb zelf 17 groepen, dus reken maar uit.
En de bedrijven die waterzijdig komen inregelen komen 1 keer met een warmtebeeld camera langs. Stellen de boel 1 keer af, en regelen vervolgens niks meer bij. Door continue te monitoren kan je dit veel beter perfectioneren.


Benodigd
2 x DS18B20 sensoren per groep a € 0,85 per stuk.
1 x 4,7 kOhm per input pin. Gebruik je 5 inputs, dan dus ook 5 weerstandjes.
1 x 400-pins breadboard a € 4,49
1 x ESP32 NodeMCU-32S Development Board a € 7,64
12 cm buis isolatie per sensor a € 1,59
Wat Dupont stekkers en een krimptang, bijvoorbeeld deze.
Wat flexibele AWG22 draad en krimpkousjes.

Software
CP210x USB to UART driver
VSCode
Platform IO

Om platform IO te installeren moet je VSCode installeren, en ga je vervolgens naar "Extensions" en zoek je daar naar Platform IO.

Afbeeldingslocatie: https://tweakers.net/i/uI-y68aDVlmxgNFwkepvyS5qQ2E=/fit-in/4000x4000/filters:no_upscale():strip_exif()/f/image/VkxqFWtqei6202uNhykf66Sm.png?f=user_large

Om de driver te installeren sluit je de ESP32 aan via USB-C, ga je naar apparaat beheer, en update je het stuurprogramma.

Afbeeldingslocatie: https://tweakers.net/i/IrstYey3VuYqyiToFAqZPXCF2Ls=/fit-in/4000x4000/filters:no_upscale():strip_exif()/f/image/ZzHJw6XOUoYtr3TLXCez7LPi.png?f=user_large


Aansluiten
Je kan middels een onewire protocol meerdere sensoren aansluiten op een enkele input/output van de ESP32, alleen bij te veel sensoren wordt het onstabiel. Via trial en error ben ik er echter gekomen dat je tot maximaal 5 sensoren stabiel kan aansluiten op 1 pin. Iets meer is ook mogelijk wanneer je een lagere pull-up weerstand er tussen zet (bijvoorbeeld 2,2 kOhm ipv 4,7 kOhm). Voor de zekerheid heb ik het gelimiteerd tot 4. Dit kwam ook beter uit op mijn breadboard met de 4-pins stekkers.

Strip de draden van 3 stuks DS18B20 sensoren, en zet hier Dupont male stekkers aan.
Pak vervolgens een 3-pin stekkerhuis en doe hier alle 3,3v draden in (rood).
Pak een 3-pins huis voor alle signaal draden (geel)
Pak een 3-pins huis en doe hier alle massa draden in (zwart).

Met mijn 10-groeps vloerverwarming heb ik er voor gekozen om 4 sensoren per input pin te doen. Over 5 inputs verdeeld. Wil je dat ook, pak dan 4 sensoren samen in plaats van 3.

Ik heb de code zo laten maken door Gemini dat het niet uit maakt of je nu 1, 2, 3, 4, of 5 sensoren op een pin aansluit. En het maakt ook niet uit over welke pinnen je dit verdeeld. Je kan dus een andere setup maken met dezelfde code.

Afbeeldingslocatie: https://tweakers.net/i/E32UXGZ8nJXtmzomNowpJvAJleg=/800x/filters:strip_exif()/f/image/BP86R3QToIG4QqdnXcur13NE.png?f=fotoalbum_large

Prik de ESP32 in het breadboard. Sluit de 3,3v aan op een 1 rail, en sluit de ground aan op 1 rail.
Brug de + rail van de ene zijde naar de andere zijde met een stukje AWG22 draad met 2 dupont stekkers er aan. Op die manier heb je meer ruimte voor de 3,3V + rail en de pull-up weerstanden.

Maak wat extra sensor draden om de sensoren via de breadboard aan te sluiten op de ESP32.
Per 4 sensoren (2 groepen) heb je nodig
1 draad van +/- 12 cm
1 draad van +/- 4 cm
Doe aan iedere draad 2 male dupont stekker (1-pins)

De groene lijnen hieronder moeten de pull-up weerstanden voorstellen.
Geel zijn de sensor draden.
Zwart is de ground
Rood is de 3,3v voeding.

Afbeeldingslocatie: https://tweakers.net/i/xiMsq0jsWBB7gvrukICzqG-xw4k=/fit-in/4920x3264/filters:max_bytes(3145728):no_upscale():strip_icc():strip_exif()/f/image/j7OvMZPXsIkQE8tciOMpmimz.jpg?f=user_large
(Let op, in werkelijkheid is de ESP32 groter dan in het voorbeeld, en heb je maar 1 rij pinnen vrij onder het board, en niet ook 1 rij boven het board).

Sluit de boel aan, bijvoorbeeld zoals in het voorbeeld hieronder.
Voor de sensor inputs kan je de volgende pinnen gebruiken:
4, 13, 14, 16, 17, 18, 19, 21, 22, 23, 25, 26, 27, 32, 33

Belangrijk is dat er over elke sensor rij (die aangesloten wordt op 1 pin van de ESP32) een pull up weerstand wordt geplaatst. Dus 1 kant van de weerstand op de sensor-rij van het breadboard, en 1 kant van de weerstand aan de 3,3v. Om deze reden heb ik een tweede "plus" rij gemaakt.

Afbeeldingslocatie: https://tweakers.net/i/nef96E3FH4cPS50w2WZZXFwiS54=/fit-in/4920x3264/filters:max_bytes(3145728):no_upscale():strip_icc():strip_exif()/f/image/n8Jj9dlrUckaDcUbQPTdmjcu.jpg?f=user_large

Afbeeldingslocatie: https://tweakers.net/i/r9ZeCrOooTu3NgIdGh1myHaWIrg=/800x/filters:strip_exif()/f/image/jaOxREbSMJnwiywH8Zon9JEJ.png?f=fotoalbum_large

Afbeeldingslocatie: https://tweakers.net/i/GuFba8Z3uChFbLlLoWifXJPr-Y4=/800x/filters:strip_exif()/f/image/ekkyfZdV2FHDVrIlH9p8TWZE.png?f=fotoalbum_large

Afbeeldingslocatie: https://tweakers.net/i/VClP6ICYPMWopW6FJ97woSZGGEQ=/800x/filters:strip_exif()/f/image/qVf1P12uNFrjCnctD4ZQP04p.png?f=fotoalbum_large
(In verband met ruimte gebrek, zit mijn tweede plus-rij op de min-rij aangesloten. In het schema hierboven zitten de plus en min rij andersom.)

Sensor herkenning
Eerst wil je de unieke sensor ID's uitlezen.
Dit zorgt er voor dat je de juiste namen kan toekennen aan iedere sensor.

Open platform IO in VSCode en open een nieuw project.

Afbeeldingslocatie: https://tweakers.net/i/7NczGqwxiLVWQb4uk66leUJkC5M=/800x/filters:strip_exif()/f/image/PVfYLupC5rjigy2DY4091y5F.png?f=fotoalbum_large

Geef je project een naam (bijvoorbeeld sensorherkenning).
Selecteer bij board: NodeMCU-32S
Framework: Arduino

Open vervolgens main.cpp, wis de inhoud, en plak onderstaande code er in.

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
#include <Arduino.h>
#include <OneWire.h>
#include <DallasTemperature.h>

// Definieer het totale aantal te gebruiken OneWire-bussen
#define NUM_BUSES 15

// Definieer de GPIO-pinnen voor ALLE 15 bussen
// Lijst van veilige, bruikbare GPIO's: 4, 13, 14, 16, 17, 18, 19, 21, 22, 23, 25, 26, 27, 32, 33
const int oneWirePins[NUM_BUSES] = {4, 13, 14, 16, 17, 18, 19, 21, 22, 23, 25, 26, 27, 32, 33};

// Maximale sensoren per bus instellen op 5. Dit maakt een totaal van 75 sensoren mogelijk (15 * 5 = 75).
#define MAX_SENSORS_PER_BUS 5 

// Arrays om de objecten en status van elke bus op te slaan
// De grootte van deze arrays is aangepast naar NUM_BUSES (15)
OneWire* oneWireBuses[NUM_BUSES];
DallasTemperature* sensors[NUM_BUSES];
DeviceAddress sensorAddresses[NUM_BUSES][MAX_SENSORS_PER_BUS]; 
uint8_t busDeviceCount[NUM_BUSES] = {0}; // Telt het aantal gevonden sensoren per bus

/**
 * @brief Een hulpfunctie om een 8-byte adres in hex-formaat af te drukken.
 * @param deviceAddress De array met het 64-bit adres.
 */
void printAddress(DeviceAddress deviceAddress) {
  for (uint8_t i = 0; i < 8; i++) {
    if (deviceAddress[i] < 16) Serial.print("0");
    Serial.print(deviceAddress[i], HEX);
  }
}

void setup() {
  Serial.begin(115200);
  Serial.println("\n--- ESP32 Multi-Bus DS18B20 Zoeker en Lezer (Max. 75 Sensoren over 15 Bussen) ---");

  // Initialiseer elke bus en zoek naar sensoren. De loop gaat nu tot 15.
  for (int i = 0; i < NUM_BUSES; i++) {
    Serial.print("\n--- Initialisatie Bus ");
    Serial.print(i + 1);
    Serial.print(" (GPIO");
    Serial.print(oneWirePins[i]);
    Serial.println(") ---");
    
    // 1. Initialiseer de OneWire- en DallasTemperature-objecten dynamisch
    // Gebruikt de pin uit de nieuwe array
    oneWireBuses[i] = new OneWire(oneWirePins[i]);
    sensors[i] = new DallasTemperature(oneWireBuses[i]);

    // 2. Start de sensorbibliotheek
    sensors[i]->begin();
    
    // 3. Lees het aantal gevonden sensoren en sla dit op
    busDeviceCount[i] = sensors[i]->getDeviceCount();
    Serial.print("Gevonden apparaten op deze bus: ");
    Serial.println(busDeviceCount[i]);

    // 4. Zoek en print de adressen, en sla ze op in de array
    for (int j = 0; j < busDeviceCount[i]; j++) {
      if (j < MAX_SENSORS_PER_BUS && sensors[i]->getAddress(sensorAddresses[i][j], j)) {
        Serial.print("  Sensor ");
        Serial.print(j + 1);
        Serial.print(" Adres: ");
        printAddress(sensorAddresses[i][j]);
        Serial.println();
      } else {
        Serial.println("  Waarschuwing: Maximum capaciteit van 5 sensoren voor deze bus overschreden of adres niet gevonden.");
      }
    }
  }
  
  Serial.println("\n--- Initialisatie Voltooid ---");
}

void loop() {
  Serial.println("\n---------------------------------");
  Serial.println("--- Huidige Temperaturen ---");

  // Vraag ALLE sensoren op ALLE 15 bussen om een temperatuurmeting te starten
  for (int i = 0; i < NUM_BUSES; i++) {
    // We controleren op null om er zeker van te zijn dat de initialisatie geslaagd is
    if (sensors[i] != nullptr) {
        sensors[i]->requestTemperatures(); 
    }
  }

  // Lees de temperatuur van elke sensor op elke bus
  int overallSensorCount = 0;
  for (int i = 0; i < NUM_BUSES; i++) {
    
    // We controleren op null om er zeker van te zijn dat de initialisatie geslaagd is
    if (sensors[i] == nullptr) continue;

    Serial.print("Bus ");
    Serial.print(i + 1);
    Serial.print(" (GPIO");
    Serial.print(oneWirePins[i]);
    Serial.println("):");

    for (int j = 0; j < busDeviceCount[i]; j++) {
      overallSensorCount++;
      
      // Gebruik HET OPGESLAGEN ADRES uit de array
      float tempC = sensors[i]->getTempC(sensorAddresses[i][j]);

      Serial.print("  Sensor ");
      Serial.print(overallSensorCount);
      Serial.print(" (");
      printAddress(sensorAddresses[i][j]); 
      Serial.print("): ");

      if (tempC != DEVICE_DISCONNECTED_C) {
        Serial.print(tempC);
        Serial.println(" °C");
      } else {
        Serial.println("FOUT: Temperatuur kon niet worden gelezen.");
      }
    }
  }

  Serial.println("---------------------------------");
  delay(15000); // Wacht 15 seconden voor de volgende meting
}


Open platformio.ini en plak onderstaande code er in:

code:
1
2
3
4
5
6
7
8
9
10
[env:nodemcu-32s]
platform = espressif32
board = nodemcu-32s
framework = arduino
monitor_speed = 115200

; Vereiste bibliotheken voor OneWire communicatie en DS18B20 beheer
lib_deps =
    milesburton/DallasTemperature@^3.11.0
    paulstoffregen/OneWire@^2.3.7


Bouw vervolgens de code via de build functie.
Build functie vind je links onderin op het "vinkje", of via Cntrl+Shift+B.
Afbeeldingslocatie: https://tweakers.net/i/8WMQU034tLytDmirKXY5aRgOguY=/fit-in/4000x4000/filters:no_upscale():strip_exif()/f/image/IIEwPuSx0HGiE9OJGItedgRs.png?f=user_large
Als je een waarschuwing krijgt tijdens het "builden", kan je deze negeren.

Zorg dat je ESP32 is aangesloten, en laad vervolgens de code in via de "Upload" functie.
De pijl links onderin.
Afbeeldingslocatie: https://tweakers.net/i/-SZykWieAvOb6T39dhTP2dcjplI=/fit-in/4000x4000/filters:no_upscale():strip_exif()/f/image/hx0JHKsyfTQPKupXkdNCfOnn.png?f=user_large

Let op, als de ESP32 niet gaat flashen moet je de boot knop op de ESP32 ingedrukt houden tijden het flashen.

Druk na het flashen op de reset knop, en open de serial monitor via Cntrl+Alt+S.
Als alles goed is gegaan kan je nu de unieke sensor ID's uitlezen.

Een werkwijze kan zijn om op voorhand je sensoren te labelen.
Bijvoorbeeld S101 voor Verdeler 1, aanvoer 0 (warm) groep 1.
S111 voor Verdeler 1, afvoer 1 (koud) groep 1.
Enz

Vervolgens kan je sensor 1 verwarmen (bijvoorbeeld beethouden in je hand), en in de serial monitor kijken welke temperatuur er omhoog gaat.

Dus als je S105 omhoog ziet gaan, en ID 48216513556425 hoort daarbij, noteer dit dan tijdelijk even in een kladblok oid. Totdat je alle sensoren hebt geïdentificeerd.

ESP32 coderen voor MQTT
Open een nieuw project net als hierboven.

Pas de platformio.ini aan met onderstaande code:

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
; PlatformIO Project Configuration File
;
;   Build options: build flags, source filter
;   Upload options: custom upload port, speed and extra flags
;   Library options: dependencies, extra library storages
;   Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html

[env:nodemcu-32s]
platform = espressif32
board = nodemcu-32s
framework = arduino
monitor_speed = 115200

; Hier komen de library dependencies
lib_deps =
    knolleary/PubSubClient@^2.8.0
    PaulStoffregen/OneWire@^2.3.7
    milesburton/DallasTemperature@^3.11.0


Maak een nieuw bestand aan in de map "src" en noem dit bestand sensorid.h
Plak onderstaande code in dat bestand:

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
// sensorid.h
// Bevat de voorgedefinieerde unieke DS18B20 sensor ID's en de bijbehorende namen.

#ifndef SENSORID_H
#define SENSORID_H

#include <Arduino.h>
#include <map>

// Definieer een type alias voor de map die Sensor ID (string) koppelt aan Sensor Naam (string)
using SensorIdMap = std::map<String, String>;

/**
 * @brief Genereert en vult de SensorIdMap met de voorgedefinieerde ID's en namen.
 * @return Een gevulde SensorIdMap.
 */
SensorIdMap createSensorIdMap() {
    SensorIdMap idToNameMap;

    // Voeg alle voorgedefinieerde sensor ID's en namen toe
    idToNameMap["2888546E000000XX"] = "S101";
    idToNameMap["28515177000000XX"] = "S102";
    idToNameMap["283CDA6F000000XX"] = "S103";
    idToNameMap["28701E77000000XX"] = "S104";
    idToNameMap["2848D36E000000XX"] = "S105";
    idToNameMap["28361867000000XX"] = "S106";
    idToNameMap["28A30E6D000000XX"] = "S111";
    idToNameMap["287B506E000000XX"] = "S112";
    idToNameMap["28FB5471000000XX"] = "S113";
    idToNameMap["285F176D000000XX"] = "S114";
    idToNameMap["283C3D6D000000XX"] = "S115";
    idToNameMap["282FC66F000000XX"] = "S116";
    idToNameMap["2851E26E000000XX"] = "S201";
    idToNameMap["282F1B6D000000XX"] = "S202";
    idToNameMap["287E3C6F000000XX"] = "S203";
    idToNameMap["281A616E000000XX"] = "S204";
    idToNameMap["28B1A46F000000XX"] = "S205";
    idToNameMap["2891666F000000XX"] = "S206";
    idToNameMap["2881C76F000000XX"] = "S207";
    idToNameMap["287D5C71000000XX"] = "S208";
    idToNameMap["28EC0F6D000000XX"] = "S209";
    idToNameMap["28F3756F000000XX"] = "S200"; 
    idToNameMap["28B0416D000000XX"] = "S211";
    idToNameMap["2833EC6E000000XX"] = "S212";
    idToNameMap["28C30571000000XX"] = "S213";
    idToNameMap["28068F6F000000XX"] = "S214";
    idToNameMap["28FC6771000000XX"] = "S215";
    idToNameMap["28D66871000000XX"] = "S216";
    idToNameMap["282C6C71000000XX"] = "S217";
    idToNameMap["287EEB74000000XX"] = "S218";
    idToNameMap["28C1F96E000000XX"] = "S219";
    idToNameMap["28556D71000000XX"] = "S210";
    idToNameMap["2851E26E000000XX"] = "S301";
    idToNameMap["2851E26E000000XX"] = "S302";
    idToNameMap["2851E26E000000XX"] = "S303";
    idToNameMap["2851E26E000000XX"] = "S304";
    idToNameMap["2851E26E000000XX"] = "S305";
    idToNameMap["2851E26E000000XX"] = "S306";
    idToNameMap["2851E26E000000XX"] = "S311";
    idToNameMap["2833EC6E000000XX"] = "S312";
    idToNameMap["28C30571000000XX"] = "S313";
    idToNameMap["28068F6F000000XX"] = "S314";
    idToNameMap["28FC6771000000XX"] = "S315";
    idToNameMap["28D66871000000XX"] = "S316";

    return idToNameMap;
}

#endif // SENSORID_H


Pas in bovenstaande code de sensor ID's aan, naar de sensor ID's die je eerder gevonden hebt.
Op die manier kan je ze een pre-fixed naam gezen in home assistant.

Ook als je een sensor toevoegd die niet vooraf gedefineerd is, dan wordt de sensor gerapporteerd in MQTT.
Alle sensoren die niet gedefinieerd zijn heten standaard SX101, SX102, SX103 enz.
Dus als je wel alle sensoren hebt gedefineerd, en je ziet toch ergens een SXxxx geraporteerd worden, dan heb je waarschijnlijk een tikfout in je sensor ID gemaakt.

Pas als laatst ook de main.cpp aan met onderstaande code.

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
// main.cpp
#include <Arduino.h>
#include <WiFi.h>
#include <PubSubClient.h> // Vereist: PlatformIO lib_deps = knolleary/PubSubClient
#include <OneWire.h>
#include <DallasTemperature.h>
#include "sensorid.h" // Inclusie van het aangepaste header-bestand

// --- Configuratie ---
// WiFi
const char* WIFI_SSID = "WifiSSID123";
const char* WIFI_PASSWORD = "WifiPassword123";

// MQTT
const char* MQTT_SERVER = "192.168.1.123";
const int MQTT_PORT = 1883;
const char* MQTT_USER = "mqttuser123";
const char* MQTT_PASSWORD = "mqttpassword123";
const char* MQTT_CLIENT_ID = "Vloerverwarming_hal";
const char* MQTT_BASE_TOPIC = "esp32/temperatuur/";
const unsigned long MEASUREMENT_INTERVAL_MS = 30000; // 30 seconden

// --- Hardware Configuratie (Zoals in de basiscode) ---
#define NUM_BUSES 15
const int oneWirePins[NUM_BUSES] = {4, 13, 14, 16, 17, 18, 19, 21, 22, 23, 25, 26, 27, 32, 33};

// Arrays om de objecten en status van elke bus op te slaan
// We verhogen de maximum sensoren per bus voor onbekend aantal sensoren.
#define MAX_SENSORS_PER_BUS 10 // Aangepast naar 10, voor het geval er meer zijn
OneWire* oneWireBuses[NUM_BUSES];
DallasTemperature* sensors[NUM_BUSES];
DeviceAddress sensorAddresses[NUM_BUSES][MAX_SENSORS_PER_BUS]; 
uint8_t busDeviceCount[NUM_BUSES] = {0};

// --- Globale variabelen voor Netwerk & Mapping ---
WiFiClient espClient;
PubSubClient mqttClient(espClient);
SensorIdMap sensorIDToName; // Map voor de voorgedefinieerde sensor ID's en namen
int unknownSensorCount = 0; // Teller voor onbekende sensoren (SX001, SX002, ...)
unsigned long lastMeasurementTime = 0;

// --- Hulpfuncties ---

/**
 * @brief Converteert een 8-byte DeviceAddress naar een hoofdletter hexadecimale String.
 * @param deviceAddress De array met het 64-bit adres.
 * @return De geconverteerde String.
 */
String addressToString(DeviceAddress deviceAddress) {
    String address = "";
    for (uint8_t i = 0; i < 8; i++) {
        if (deviceAddress[i] < 16) address += "0";
        address += String(deviceAddress[i], HEX);
    }
    address.toUpperCase(); // Zorg ervoor dat het consistent is met de map-keys
    return address;
}

/**
 * @brief Een hulpfunctie om een 8-byte adres in hex-formaat af te drukken (voor Serial).
 * @param deviceAddress De array met het 64-bit adres.
 */
void printAddress(DeviceAddress deviceAddress) {
    for (uint8_t i = 0; i < 8; i++) {
        if (deviceAddress[i] < 16) Serial.print("0");
        Serial.print(deviceAddress[i], HEX);
    }
}

/**
 * @brief Bepaalt de sensornaam op basis van het adres en beheert de onbekende sensoren.
 * @param addressString Het 8-byte sensoradres als String.
 * @return De naam die voor de sensor gebruikt moet worden in MQTT.
 */
String getSensorName(const String& addressString) {
    // 1. Zoek naar de ID in de voorgedefinieerde map
    if (sensorIDToName.count(addressString)) {
        return sensorIDToName[addressString];
    } else {
        // 2. ID is onbekend, wijs een nieuwe SX-naam toe
        unknownSensorCount++;
        // Formatteer als SX001, SX002, etc.
        char nameBuffer[6]; // Bijv. "SX001" + '\0'
        sprintf(nameBuffer, "SX%03d", unknownSensorCount);
        // Sla de naam op voor toekomstige metingen in deze sessie
        sensorIDToName[addressString] = nameBuffer; 
        
        Serial.print("WAARSCHUWING: Onbekende sensor ID ");
        Serial.print(addressString);
        Serial.print(" toegewezen aan naam: ");
        Serial.println(nameBuffer);
        
        return nameBuffer;
    }
}

// --- Netwerk Functies ---

/**
 * @brief Maakt verbinding met het WiFi-netwerk.
 */
void setup_wifi() {
    delay(10);
    Serial.println();
    Serial.print("Verbinden met WiFi: ");
    Serial.println(WIFI_SSID);

    WiFi.begin(WIFI_SSID, WIFI_PASSWORD);

    while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.print(".");
    }

    Serial.println("");
    Serial.println("WiFi verbonden.");
    Serial.print("IP-adres: ");
    Serial.println(WiFi.localIP());
}

/**
 * @brief Controleert en herstelt de MQTT-verbinding.
 */
void reconnect_mqtt() {
    // Loop totdat we opnieuw verbonden zijn
    while (!mqttClient.connected()) {
        Serial.print("POGING MQTT-verbinding... ");
        
        // Probeer verbinding te maken
        if (mqttClient.connect(MQTT_CLIENT_ID, MQTT_USER, MQTT_PASSWORD)) {
            Serial.println("verbonden!");
        } else {
            Serial.print("mislukt, rc=");
            Serial.print(mqttClient.state());
            Serial.println(". Opnieuw proberen in 5 seconden.");
            // Wacht 5 seconden voordat u opnieuw probeert
            delay(5000);
        }
    }
}

// --- Setup Functies ---

void setup() {
    Serial.begin(115200);
    Serial.println("\n--- ESP32 Multi-Bus DS18B20 Zoeker en MQTT Zender ---");

    // 1. Initialiseer WiFi
    setup_wifi();

    // 2. Initialiseer MQTT
    mqttClient.setServer(MQTT_SERVER, MQTT_PORT);

    // 3. Vul de Sensor ID map in (eenmalig)
    sensorIDToName = createSensorIdMap();
    Serial.print("\nGeladen voorgedefinieerde sensor ID's: ");
    Serial.println(sensorIDToName.size());

    // 4. Initialiseer DS18B20-bussen (gebruik de robuuste code als basis)
    for (int i = 0; i < NUM_BUSES; i++) {
        Serial.print("\n--- Initialisatie Bus ");
        Serial.print(i + 1);
        Serial.print(" (GPIO");
        Serial.print(oneWirePins[i]);
        Serial.println(") ---");
        
        oneWireBuses[i] = new OneWire(oneWirePins[i]);
        sensors[i] = new DallasTemperature(oneWireBuses[i]);

        sensors[i]->begin();
        
        // Lees het aantal gevonden sensoren en sla dit op
        uint8_t count = sensors[i]->getDeviceCount();
        busDeviceCount[i] = (count > MAX_SENSORS_PER_BUS) ? MAX_SENSORS_PER_BUS : count; // Beperk tot MAX_SENSORS_PER_BUS
        Serial.print("Gevonden apparaten op deze bus: ");
        Serial.println(count);

        // Zoek en print de adressen, en sla ze op in de array
        for (int j = 0; j < busDeviceCount[i]; j++) {
            if (sensors[i]->getAddress(sensorAddresses[i][j], j)) {
                String idString = addressToString(sensorAddresses[i][j]);
                String name = getSensorName(idString); // Bepaalt de naam en wijst SX-naam toe indien onbekend
                
                Serial.print("  Sensor ");
                Serial.print(j + 1);
                Serial.print(" Adres: ");
                printAddress(sensorAddresses[i][j]);
                Serial.print(" -> Naam: ");
                Serial.println(name);
            } else {
                Serial.println("  Waarschuwing: Adres niet gevonden.");
            }
        }
    }
    
    Serial.print("\n--- Initialisatie Voltooid. Totaal unieke sensoren: ");
    Serial.println(sensorIDToName.size());
    Serial.println("---");
}

// --- Loop Functies ---

void loop() {
    // 1. Zorg ervoor dat de verbindingen in stand worden gehouden
    if (WiFi.status() != WL_CONNECTED) {
        setup_wifi(); // Probeer opnieuw verbinding te maken met WiFi
    }
    if (!mqttClient.connected()) {
        reconnect_mqtt(); // Probeer opnieuw verbinding te maken met MQTT
    }
    mqttClient.loop(); // Verwerk inkomende MQTT-berichten (indien er een callback is gedefinieerd)

    // 2. Meet en publiceer temperaturen met het ingestelde interval
    if (millis() - lastMeasurementTime >= MEASUREMENT_INTERVAL_MS) {
        lastMeasurementTime = millis();

        Serial.println("\n---------------------------------");
        Serial.println("--- Meting gestart en verzenden via MQTT ---");

        // Vraag ALLE sensoren om een temperatuurmeting te starten
        for (int i = 0; i < NUM_BUSES; i++) {
            if (sensors[i] != nullptr) {
                sensors[i]->requestTemperatures(); 
            }
        }

        // Lees en publiceer de temperatuur van elke sensor op elke bus
        for (int i = 0; i < NUM_BUSES; i++) {
            if (sensors[i] == nullptr) continue;

            for (int j = 0; j < busDeviceCount[i]; j++) {
                
                // Gebruik HET OPGESLAGEN ADRES uit de array
                float tempC = sensors[i]->getTempC(sensorAddresses[i][j]);

                String idString = addressToString(sensorAddresses[i][j]);
                String sensorName = sensorIDToName[idString]; // We weten zeker dat deze ID bestaat na de setup

                Serial.print("  Sensor ");
                Serial.print(sensorName);
                Serial.print(" (");
                Serial.print(idString); 
                Serial.print("): ");

                if (tempC != DEVICE_DISCONNECTED_C) {
                    Serial.print(tempC);
                    Serial.println(" °C");
                    
                    // Construeer het MQTT-topic en de payload
                    String topic = MQTT_BASE_TOPIC + sensorName;
                    String payload = String(tempC, 2); // Temperatuur met 2 decimalen

                    // Publiceer het bericht
                    if (mqttClient.publish(topic.c_str(), payload.c_str())) {
                        Serial.print("    -> MQTT OK. Topic: ");
                        Serial.println(topic);
                    } else {
                        Serial.println("    -> MQTT FOUT: Kon niet publiceren.");
                    }

                } else {
                    Serial.println("FOUT: Temperatuur kon niet worden gelezen (DEVICE_DISCONNECTED_C).");
                }
            }
        }

        Serial.println("---------------------------------");
    }

    // Een kleine vertraging om de watchdog timer te resetten en taken te laten draaien
    delay(10); 
}


Let op, in deze code dien je aanpassingen te doen. Pas onderstaande stukje aan voor jouw eigen situatie, dus je wifi inlog gegevens, en het IP adres en de inlog van je MQTT server.

const char* MQTT_SERVER = "192.168.1.123";
const int MQTT_PORT = 1883;
const char* MQTT_USER = "mqttuser123";
const char* MQTT_PASSWORD = "mqttpassword123";
const char* MQTT_CLIENT_ID = "Vloerverwarming_hal"; //Apparaat naam in Home Assistant
const char* MQTT_BASE_TOPIC = "esp32/temperatuur/";
const unsigned long MEASUREMENT_INTERVAL_MS = 30000; // 30 seconden


Bouw vervolgens deze code op, en flash hem naar de ESP32 (net zoals in de vorige stap hierboven).
Nu kan je alles installeren. Maar voordat je dat doet zou ik adviseren alles te testen via de serial monitor, en vervolgens in Home Assistant (of je eigen MQTT server).

Installatie
Het breadboard icm de dupont stekkers is vrij fragiel.
Je kan er voor kiezen om de stekkers vast te zetten met wat hot-glue, maar dan wordt het lastig om in de toekomst nog reparaties uit te voeren.

Ik heb daarom een simpele behuizing geprint, waarbij het belangrijk is om een trek-ontlasting voor de bedrading te hebben. Op die manier trek je niet perongeluk bedrading of stekkers los tijdens de installatie.

Indien gewenst kan ik het ontwerp van de behuizing nog wel delen, maar gebruik dit op eigen risico. In jouw situatie kan een ander ontwerp beter uitkomen.

Ik heb iedere sensor met wat isolatie tape aan een vloerverwarmingsbuis vast gezet. Vervolgens heb ik er een stukje buis isolatie omheen gedaan om temperatuursinvloeden van buitenaf te voorkomen. Als je geen 3D printe hebt kan je het breadboard dmv de plakstrip ook rechtstreeks op de muur plakken.

Afbeeldingslocatie: https://tweakers.net/i/baZnBuZb53b4JQSQ5L5L6gxvG1o=/800x/filters:strip_exif()/f/image/OzW5LXvMHT0m2kF110SlAmyt.png?f=fotoalbum_large

Afbeeldingslocatie: https://tweakers.net/i/_YhroSP2n5JCXSzVTU5xc5_lv_8=/x800/filters:strip_exif()/f/image/iOzeRsmz2egQvv2UDq0SvBVF.png?f=fotoalbum_large

Home Assistant
Er van uitgaande dat je reeds een MQTT server in home assistant hebt (zo niet, zie Youtube voor uitleg), hier de uitleg om entity's toe te kennen aan de sensoren, op basis van de MQTT payload die binnen komt op de Home Assistant.

Pas je configuration.yaml aan met de volgende code. Onder MQTT:

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
mqtt:
  sensor:
    - unique_id: 'vloer_s101_temp'
      name: 'Garage Vloer S101'
      state_topic: "esp32/temperatuur/S101"
      unit_of_measurement: "°C"
      device_class: temperature
      state_class: measurement
    
    - unique_id: 'vloer_s102_temp'
      name: 'Garage Vloer S102'
      state_topic: "esp32/temperatuur/S102"
      unit_of_measurement: "°C"
      device_class: temperature
      state_class: measurement

    - unique_id: 'vloer_s201_temp'
      name: 'Hal Vloer S201'
      state_topic: "esp32/temperatuur/S201"
      unit_of_measurement: "°C"
      device_class: temperature
      state_class: measurement
      
    - unique_id: 'vloer_s202_temp'
      name: 'Hal Vloer S202'
      state_topic: "esp32/temperatuur/S202"
      unit_of_measurement: "°C"
      device_class: temperature
      state_class: measurement
      
ENZ
ENZ


Ook in de configuration.yaml, maar dan onder onder "Template > Sensor", neem je het volgende op.

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
template:
  - sensor:
      # Delta T Groep 5 (S105 - S115)
    - name: "Delta T Garage Groep 5"
      unique_id: "delta_t_garage_groep_5"
      unit_of_measurement: "°C"
      device_class: temperature
      state_class: measurement
      state: >
        {{ (states('sensor.garage_vloer_s105') | float(0) - states('sensor.garage_vloer_s115') | float(0)) | round(2) }}

      # Delta T Groep 6 (S106 - S116)
    - name: "Delta T Garage Groep 6"
      unique_id: "delta_t_garage_groep_6"
      unit_of_measurement: "°C"
      device_class: temperature
      state_class: measurement
      state: >
        {{ (states('sensor.garage_vloer_s106') | float(0) - states('sensor.garage_vloer_s116') | float(0)) | round(2) }}

## Delta T's voor de hal groep

      # Delta T Groep 1 (S201 - S211)
    - name: "Delta T Hal Groep 1"
      unique_id: "delta_t_hal_groep_1"
      unit_of_measurement: "°C"
      device_class: temperature
      state_class: measurement
      state: >
        {{ (states('sensor.hal_vloer_s201') | float(0) - states('sensor.hal_vloer_s211') | float(0)) | round(2) }}

      # Delta T Groep 2 (S202 - S212)
    - name: "Delta T Hal Groep 2"
      unique_id: "delta_t_hal_groep_2"
      unit_of_measurement: "°C"
      device_class: temperature
      state_class: measurement
      state: >
        {{ (states('sensor.hal_vloer_s202') | float(0) - states('sensor.hal_vloer_s212') | float(0)) | round(2) }}


Ook bovenstaande pas je aan, aan de hand van je eigen sensor-namen en het aantal sensoren wat je hebt.
Na het aanpassen start je de home assistant opnieuw op, en kent hij de sensoren als het goed is aan een entity toe, die je bijvoorbeeld op je dashboard kan gebruiken.

Afbeeldingslocatie: https://tweakers.net/i/Vwx9fs-1tDLyDGdUacUkfyhNe68=/800x/filters:strip_exif()/f/image/4LZmgHgi788cyXJkiaSX7Rqx.png?f=fotoalbum_large

Afbeeldingslocatie: https://tweakers.net/i/dxfz78VlDHA5BmMgOzsjje5ERpA=/fit-in/4000x4000/filters:no_upscale():strip_exif()/f/image/77Q7eulCmTgOJedvjLr9KX0y.png?f=user_large

Doe er je voordeel mee! :)

Alle reacties


  • nils_vdg
  • Registratie: November 2002
  • Laatst online: 02-12 07:42
Gereserveerd

  • Oogje
  • Registratie: Oktober 2003
  • Niet online
Mooi stukje huisvlijt en uitgebreide beschrijving! Ik zat hier al eens over na te denken om te meten, morgen maar eens uitgebreid lezen 👍

Any errors in spelling, tact, or fact are transmission errors.


  • Oilman
  • Registratie: December 2012
  • Laatst online: 19:00
Waarom niet via ESPHome, dan is het programmeerwerk nog een stuk minder en de integratie met HA helemaal straight forward.

  • Melvin15397
  • Registratie: Januari 2001
  • Laatst online: 18:01
Mooie beschrijving, ook voor mensen (zoals ik) met beperkte kennis van elektronica en programmeren :) Misschien dat ik het in de toekomst ook volgens jouw beschrijving ga maken. Voorlopig eerst teveel andere klussen die afgemaakt moeten worden.

Nog wel een suggestie voor een iets eenvoudigere opzet:
Bij een pomploze vloerverwarming verdeler (zoals op je foto) is de aanvoer temperatuur van elke groep gelijk aan de aanvoertemperatuur van leiding naar de verdeler. Je zou dus kunnen volstaan met een enkele meting op de aanvoerleiding voor de verdeler en deze gebruiken bij de berekening voor de delta T van elke groep.
Met een mengverdeler met pomp (uitgaande van goede menging voordat de eerste groep bereikt is), zouden alle groepen ook dezelfde aanvoer temperatuur moeten hebben. Dan zou je op de verdeler kunnen meten in plaats van de aanvoerleiding.

  • nils_vdg
  • Registratie: November 2002
  • Laatst online: 02-12 07:42
Daar heb je helemaal gelijk in Melvin. Dat scheelt weer een flink aantal sensoren.
Oilman schreef op zaterdag 29 november 2025 @ 19:32:
Waarom niet via ESPHome, dan is het programmeerwerk nog een stuk minder en de integratie met HA helemaal straight forward.
Als dat lukt zou ik dat zeker doen. Nog geen ervaring mee. Ik zal me daar ook eens in verdiepen.

  • Pejdref
  • Registratie: November 2012
  • Laatst online: 17:02
Mooie topicstart, staat ook nog op mijn lijstje.
In mijn geval wil ik hiermee ook detecteren of mijn 3-weg klep nog werkt, om te voorkomen dat er te heet water de vloer in gaat tijdens het verwarmen van de boiler.
Pagina: 1