Commit aedbed70d121a026b53042ce963765fc77bace0b

Authored by Jeroen88
1 parent a7de2e9c

Changed MQTT client back to PubSubClient but TCPIP is handled by sketch, added E…

…SP8266 support, changed shortID() to pick the least significant bytes of chip ID, made StaticJsonDocument DynamicJsonDocument, small ISR related changes in OTPhysicalLayer
examples/Advanced_Thermostat/Advanced_Thermostat.ino
@@ -112,21 +112,16 @@ static const char * TAG = __FILE__; @@ -112,21 +112,16 @@ static const char * TAG = __FILE__;
112 112
113 #include <Arduino.h> 113 #include <Arduino.h>
114 114
  115 +#if defined(ESP8266)
  116 +#include <ESP8266WiFi.h>
  117 +#else
115 #include <WiFi.h> 118 #include <WiFi.h>
  119 +#endif
116 120
117 #include <time.h> 121 #include <time.h>
118 122
119 #include <ArduinoJson.h> 123 #include <ArduinoJson.h>
120 124
121 -// This example does not use the more popular PubSubClient, because that has too many issues, like losing a connection without possibilities to signal it and correct it  
122 -// This MQTT client runs rock solid :)  
123 -// https://github.com/monstrenyatko/ArduinoMqtt  
124 -// Enable MqttClient logs  
125 -#define MQTT_LOG_ENABLED 1  
126 -// Include library  
127 -#include <MqttClient.h>  
128 -  
129 -  
130 #include <OneWire.h> 125 #include <OneWire.h>
131 #include <DallasTemperature.h> 126 #include <DallasTemperature.h>
132 127
@@ -149,6 +144,7 @@ const char * password = &quot;YOUR WIFI PASSWORD&quot;; @@ -149,6 +144,7 @@ const char * password = &quot;YOUR WIFI PASSWORD&quot;;
149 const char * mqtt_server = "YOUR MQTT SERVER NAME OR IP ADDRESS"; 144 const char * mqtt_server = "YOUR MQTT SERVER NAME OR IP ADDRESS";
150 const char * mqtt_user = "YOUR MQTT USER NAME"; 145 const char * mqtt_user = "YOUR MQTT USER NAME";
151 const char * mqtt_password = "YOUR MQTT PASSWORD"; 146 const char * mqtt_password = "YOUR MQTT PASSWORD";
  147 +const char * MQTT_ID = "EasyOpenTherm"; // Should be unique for your broker, see e.g. https://stackoverflow.com/questions/40078252/mosquitto-prevent-duplicate-login
152 148
153 149
154 // Your time zone, used to display times correctly (and needed for WiFiClientSecure TLS certificate validation, if used) 150 // Your time zone, used to display times correctly (and needed for WiFiClientSecure TLS certificate validation, if used)
@@ -156,8 +152,8 @@ const char * mqtt_password = &quot;YOUR MQTT PASSWORD&quot;; @@ -156,8 +152,8 @@ const char * mqtt_password = &quot;YOUR MQTT PASSWORD&quot;;
156 #define TIME_ZONE TZ_Europe_Amsterdam 152 #define TIME_ZONE TZ_Europe_Amsterdam
157 153
158 154
159 -// Define OT_RX_PIN, the GPIO pin used to read data from the boiler or HVAC. Must support interrupts  
160 -// Define OT_TX_PIN, the GPIO pin used to send data to the boiier or HVAC. Must not be a 'read only' GPIO 155 +// Define OT_RX_PIN, the GPIO pin used to read data from the boiler or HVAC (TxD). Must support interrupts
  156 +// Define OT_TX_PIN, the GPIO pin used to send data to the boiier or HVAC (RxD). Must not be a 'read only' GPIO
161 // Define DALLAS, the GPIO pin used for the Dallas sensor, if used 157 // Define DALLAS, the GPIO pin used for the Dallas sensor, if used
162 158
163 #if defined(ARDUINO_LOLIN_S2_MINI) 159 #if defined(ARDUINO_LOLIN_S2_MINI)
@@ -168,12 +164,32 @@ const char * mqtt_password = &quot;YOUR MQTT PASSWORD&quot;; @@ -168,12 +164,32 @@ const char * mqtt_password = &quot;YOUR MQTT PASSWORD&quot;;
168 #define OT_RX_PIN (10) 164 #define OT_RX_PIN (10)
169 #define OT_TX_PIN (8) 165 #define OT_TX_PIN (8)
170 #define DALLAS (4) 166 #define DALLAS (4)
  167 +#elif defined(ARDUINO_ESP8266_WEMOS_D1MINIPRO)
  168 +// I can't get my "D1 MINI PRO Based ESP8266EX" passed a WiFi connection
  169 +// D1 is GPIO5
  170 +#define OT_RX_PIN (5)
  171 +// D2 is GPIO4
  172 +#define OT_TX_PIN (4)
  173 +// D7 is GPIO13
  174 +#define DALLAS (13)
  175 +#elif defined(ESP32)
  176 +#define OT_RX_PIN (33)
  177 +#define OT_TX_PIN (16)
  178 +#define DALLAS (17)
  179 +#elif defined(ESP8266)
  180 +// GPIO5 is D1
  181 +#define OT_RX_PIN (5)
  182 +// GPIO4 is D2
  183 +#define OT_TX_PIN (4)
  184 +// D7 is GPIO13
  185 +#define DALLAS (13)
171 #else 186 #else
172 #define OT_RX_PIN (35) 187 #define OT_RX_PIN (35)
173 #define OT_TX_PIN (33) 188 #define OT_TX_PIN (33)
174 #define DALLAS (-1) 189 #define DALLAS (-1)
175 #endif 190 #endif
176 191
  192 +
177 // The maximum room temperature 193 // The maximum room temperature
178 #define ROOM_TEMPERATURE_MAX_SETPOINT (30.0f) 194 #define ROOM_TEMPERATURE_MAX_SETPOINT (30.0f)
179 // The minimum room temperature 195 // The minimum room temperature
@@ -212,8 +228,7 @@ const size_t MSG_BUFFER_RECV_SIZE = 256; // Too small a receive buffer will fai @@ -212,8 +228,7 @@ const size_t MSG_BUFFER_RECV_SIZE = 256; // Too small a receive buffer will fai
212 // Define a global WiFiClient instance for WiFi connection 228 // Define a global WiFiClient instance for WiFi connection
213 WiFiClient wiFiClient; 229 WiFiClient wiFiClient;
214 230
215 -#define MQTT_ID "EasyOpenTherm"  
216 -static MqttClient *mqtt = NULL; 231 +PubSubClient *mqtt = NULL;
217 232
218 233
219 // GPIO where the DS18B20 is connected to, set to '-1' if not used 234 // GPIO where the DS18B20 is connected to, set to '-1' if not used
@@ -282,12 +297,32 @@ float roomTemperature; @@ -282,12 +297,32 @@ float roomTemperature;
282 297
283 298
284 // ============== Subscription callbacks ======================================== 299 // ============== Subscription callbacks ========================================
285 -void processRoomTemperatureMessage(MqttClient::MessageData& md) { 300 +void PubSubClientCallback(char* topic, byte* payload, unsigned int length) {
  301 + // handle message arrived
  302 + char payloadReceived[length + 1];
  303 + memcpy(payloadReceived, payload, length);
  304 + payloadReceived[length] = '\0';
  305 +
  306 + Serial.printf("Received topic '%s' with payload '%s'\n", topic, payloadReceived);
  307 +
  308 + const char * subscriptionTopic = topicByReference("temperature_command_topic", EASYOPENTHERM_MQTT_DISCOVERY_MSG_CLIMATE);
  309 + if(subscriptionTopic == nullptr) return;
  310 + if(strcmp(topic, subscriptionTopic) == 0) return processSetpointTemperatureMessage(payloadReceived);
  311 +
  312 + subscriptionTopic = topicByReference("mode_command_topic", EASYOPENTHERM_MQTT_DISCOVERY_MSG_CLIMATE);
  313 + if(subscriptionTopic == nullptr) return;
  314 + if(strcmp(topic, subscriptionTopic) == 0) return processClimateMessage(payloadReceived);
  315 +
  316 + subscriptionTopic = topicByReference("current_temperature_topic", EASYOPENTHERM_MQTT_DISCOVERY_MSG_CLIMATE);
  317 + if(subscriptionTopic == nullptr) return;
  318 + if(strcmp(topic, subscriptionTopic) == 0) return processRoomTemperatureMessage(payloadReceived);
  319 +
  320 + Serial.printf("Received unhandled topic '%s' with payload '%s'\n", topic, payloadReceived);
  321 +}
  322 +
  323 +
  324 +void processRoomTemperatureMessage(const char * payload) {
286 Serial.println("processRoomTemperatureMessage"); 325 Serial.println("processRoomTemperatureMessage");
287 - const MqttClient::Message& msg = md.message;  
288 - char payload[msg.payloadLen + 1];  
289 - memcpy(payload, msg.payload, msg.payloadLen);  
290 - payload[msg.payloadLen] = '\0';  
291 326
292 StaticJsonDocument<32> roomTemperatureMsgDoc; 327 StaticJsonDocument<32> roomTemperatureMsgDoc;
293 DeserializationError error = deserializeJson(roomTemperatureMsgDoc, payload); 328 DeserializationError error = deserializeJson(roomTemperatureMsgDoc, payload);
@@ -314,12 +349,7 @@ Serial.printf(&quot;Received room temperature %.01f ºC\n&quot;, roomTemperature); @@ -314,12 +349,7 @@ Serial.printf(&quot;Received room temperature %.01f ºC\n&quot;, roomTemperature);
314 } 349 }
315 350
316 351
317 -void processSetpointTemperatureMessage(MqttClient::MessageData& md) {  
318 - const MqttClient::Message& msg = md.message;  
319 - char payload[msg.payloadLen + 1];  
320 - memcpy(payload, msg.payload, msg.payloadLen);  
321 - payload[msg.payloadLen] = '\0';  
322 - 352 +void processSetpointTemperatureMessage(const char * payload) {
323 float setpoint; 353 float setpoint;
324 if(sscanf(payload, "%f", &setpoint) != 1) { 354 if(sscanf(payload, "%f", &setpoint) != 1) {
325 ESP_LOGE(TAG, "Payload is not a float: '%s'", payload); 355 ESP_LOGE(TAG, "Payload is not a float: '%s'", payload);
@@ -333,11 +363,7 @@ Serial.printf(&quot;Received room temperature setpoint &#39;%s&#39;\n&quot;, payload); @@ -333,11 +363,7 @@ Serial.printf(&quot;Received room temperature setpoint &#39;%s&#39;\n&quot;, payload);
333 } 363 }
334 364
335 365
336 -void processClimateMessage(MqttClient::MessageData& md) {  
337 - const MqttClient::Message& msg = md.message;  
338 - char payload[msg.payloadLen + 1];  
339 - memcpy(payload, msg.payload, msg.payloadLen);  
340 - payload[msg.payloadLen] = '\0'; 366 +void processClimateMessage(const char * payload) {
341 ESP_LOGI(TAG, 367 ESP_LOGI(TAG,
342 "Message arrived: qos %d, retained %d, dup %d, packetid %d, payload:[%s]", 368 "Message arrived: qos %d, retained %d, dup %d, packetid %d, payload:[%s]",
343 msg.qos, msg.retained, msg.dup, msg.id, payload 369 msg.qos, msg.retained, msg.dup, msg.id, payload
@@ -362,27 +388,28 @@ void setup() { @@ -362,27 +388,28 @@ void setup() {
362 delay(5000); // For debug only: give the Serial Monitor some time to connect to the native USB of the MCU for output 388 delay(5000); // For debug only: give the Serial Monitor some time to connect to the native USB of the MCU for output
363 Serial.println("\n\nStarted"); 389 Serial.println("\n\nStarted");
364 Serial.printf("Chip ID is %s\n", chipID()); 390 Serial.printf("Chip ID is %s\n", chipID());
365 - ESP_LOGI(TAG, "Chip ID is %s", chipID()); 391 + Serial.printf("Short ID is %s\n", shortID());
366 392
367 Serial.printf("OpenTherm RX pin is %d, TX pin is %d\n", OT_RX_PIN, OT_TX_PIN); 393 Serial.printf("OpenTherm RX pin is %d, TX pin is %d\n", OT_RX_PIN, OT_TX_PIN);
368 394
369 - pinMode(LED_BUILTIN, OUTPUT); 395 +// pinMode(LED_BUILTIN, OUTPUT);
370 396
371 // Connect WiFi 397 // Connect WiFi
372 WiFi.mode(WIFI_STA); 398 WiFi.mode(WIFI_STA);
373 WiFi.setAutoReconnect(true); 399 WiFi.setAutoReconnect(true);
  400 + WiFi.setAutoReconnect(false);
374 WiFi.begin(ssid, password); 401 WiFi.begin(ssid, password);
375 402
376 uint32_t startMillis = millis(); 403 uint32_t startMillis = millis();
377 while (WiFi.status() != WL_CONNECTED) { 404 while (WiFi.status() != WL_CONNECTED) {
378 delay(500); 405 delay(500);
379 - digitalWrite(LED_BUILTIN, digitalRead(LED_BUILTIN) == LOW ? HIGH : LOW); 406 +// digitalWrite(LED_BUILTIN, digitalRead(LED_BUILTIN) == LOW ? HIGH : LOW);
380 407
381 if(millis() - startMillis > 15000) ESP.restart(); 408 if(millis() - startMillis > 15000) ESP.restart();
382 } 409 }
383 410
384 - ESP_LOGI(TAG, "WiFi connected to %s", ssid);  
385 - ESP_LOGI(TAG, "IP address: %s", WiFi.localIP().toString().c_str()); 411 + Serial.printf("WiFi connected to %s\n", ssid);
  412 + Serial.printf("IP address: %s\n", WiFi.localIP().toString().c_str());
386 413
387 // Set time and date, necessary for HTTPS certificate validation 414 // Set time and date, necessary for HTTPS certificate validation
388 configTzTime(TIME_ZONE, "pool.ntp.org", "time.nist.gov"); 415 configTzTime(TIME_ZONE, "pool.ntp.org", "time.nist.gov");
@@ -400,7 +427,11 @@ void setup() { @@ -400,7 +427,11 @@ void setup() {
400 427
401 if(millis() - startMillis > 15000) ESP.restart(); 428 if(millis() - startMillis > 15000) ESP.restart();
402 } 429 }
  430 +#if defined(ESP8266)
  431 + digitalWrite(LED_BUILTIN, HIGH);
  432 +#else
403 digitalWrite(LED_BUILTIN, LOW); 433 digitalWrite(LED_BUILTIN, LOW);
  434 +#endif
404 Serial.println(); 435 Serial.println();
405 436
406 const struct tm * timeinfo = localtime(&now); 437 const struct tm * timeinfo = localtime(&now);
@@ -417,32 +448,8 @@ void setup() { @@ -417,32 +448,8 @@ void setup() {
417 448
418 } 449 }
419 450
420 - // Setup MQTT client  
421 - MqttClient::System *mqttSystem = new System;  
422 -#if defined(ARDUINO_LOLIN_S2_MINI)  
423 - MqttClient::Logger *mqttLogger = new MqttClient::LoggerImpl<USBCDC>(Serial);  
424 -#elif defined(ARDUINO_LOLIN_C3_MINI)  
425 - MqttClient::Logger *mqttLogger = new MqttClient::LoggerImpl<HWCDC>(Serial);  
426 -#else  
427 - MqttClient::Logger *mqttLogger = new MqttClient::LoggerImpl<HardwareSerial>(Serial);  
428 -#endif  
429 - MqttClient::Network * mqttNetwork = new MqttClient::NetworkClientImpl<WiFiClient>(wiFiClient, *mqttSystem);  
430 - //// Make MSG_BUFFER_SIZE bytes send buffer  
431 - MqttClient::Buffer *mqttSendBuffer = new MqttClient::ArrayBuffer<MSG_BUFFER_SEND_SIZE>();  
432 - //// Make MSG_BUFFER_SIZE bytes receive buffer  
433 - MqttClient::Buffer *mqttRecvBuffer = new MqttClient::ArrayBuffer<MSG_BUFFER_RECV_SIZE>();  
434 - //// Allow up to 4 subscriptions simultaneously  
435 - MqttClient::MessageHandlers *mqttMessageHandlers = new MqttClient::MessageHandlersDynamicImpl<4>();  
436 - // Note: the MessageHandlersDynamicImpl does not copy the topic string. The second parameter to MessageHandlersStaticImpl is the maximum topic size  
437 - // NOT TRUE: https://github.com/monstrenyatko/ArduinoMqtt/blob/15091f0b8c05f843f93b73db9a98f7b59ffb4dfa/src/MqttClient.h#L390  
438 -// MqttClient::MessageHandlers *mqttMessageHandlers = new MqttClient::MessageHandlersStaticImpl<4, 128>();  
439 - //// Configure client options  
440 - MqttClient::Options mqttOptions;  
441 - ////// Set command timeout to 10 seconds  
442 - mqttOptions.commandTimeoutMs = 10000;  
443 - //// Make client object  
444 - mqtt = new MqttClient(mqttOptions, *mqttLogger, *mqttSystem, *mqttNetwork, *mqttSendBuffer, *mqttRecvBuffer, *mqttMessageHandlers);  
445 - 451 + // Do not let PubSubClient handle the TCP/IP socket connection, because of connection issues
  452 + Serial.println("Connect to MQTT broker...");
446 wiFiClient.connect(mqtt_server, 1883); 453 wiFiClient.connect(mqtt_server, 1883);
447 if(!wiFiClient.connected()) { 454 if(!wiFiClient.connected()) {
448 ESP_LOGE(TAG, "Can't establish the TCP connection"); 455 ESP_LOGE(TAG, "Can't establish the TCP connection");
@@ -450,7 +457,10 @@ void setup() { @@ -450,7 +457,10 @@ void setup() {
450 ESP.restart(); 457 ESP.restart();
451 } 458 }
452 459
453 - Serial.println("Connect MQTT client..."); 460 + // Setup MQTT client
  461 + mqtt = new PubSubClient(wiFiClient);
  462 + mqtt->setBufferSize(1024);
  463 + mqtt->setCallback(PubSubClientCallback);
454 464
455 bool MQTTConnected = connectMQTT(*mqtt, MQTT_ID, mqtt_user, mqtt_password); 465 bool MQTTConnected = connectMQTT(*mqtt, MQTT_ID, mqtt_user, mqtt_password);
456 if(MQTTConnected) { 466 if(MQTTConnected) {
@@ -487,14 +497,14 @@ Serial.println(&quot;MQTT NOT connected&quot;); @@ -487,14 +497,14 @@ Serial.println(&quot;MQTT NOT connected&quot;);
487 if(!validJson(EASYOPENTHERM_MQTT_DISCOVERY_MSG_DHW_BINARY_SENSOR)) Serial.printf("Invalid JSON '%s'\n", EASYOPENTHERM_MQTT_DISCOVERY_MSG_DHW_BINARY_SENSOR); 497 if(!validJson(EASYOPENTHERM_MQTT_DISCOVERY_MSG_DHW_BINARY_SENSOR)) Serial.printf("Invalid JSON '%s'\n", EASYOPENTHERM_MQTT_DISCOVERY_MSG_DHW_BINARY_SENSOR);
488 498
489 // Add all 'always present' entities (the other entities are added only if supported by the boiler) 499 // Add all 'always present' entities (the other entities are added only if supported by the boiler)
  500 + Serial.println("Add default entities to Home Assistant by publishing discovery messages to MQTT broker...");
490 addEntity(*mqtt, "climate", "climate", EASYOPENTHERM_MQTT_DISCOVERY_MSG_CLIMATE); 501 addEntity(*mqtt, "climate", "climate", EASYOPENTHERM_MQTT_DISCOVERY_MSG_CLIMATE);
491 addEntity(*mqtt, "sensor", "boiler_setpoint", EASYOPENTHERM_MQTT_DISCOVERY_MSG_SETPOINT_SENSOR); 502 addEntity(*mqtt, "sensor", "boiler_setpoint", EASYOPENTHERM_MQTT_DISCOVERY_MSG_SETPOINT_SENSOR);
492 addEntity(*mqtt, "sensor", "thermostat_rssi", EASYOPENTHERM_MQTT_DISCOVERY_MSG_RSSI_SENSOR); 503 addEntity(*mqtt, "sensor", "thermostat_rssi", EASYOPENTHERM_MQTT_DISCOVERY_MSG_RSSI_SENSOR);
493 addEntity(*mqtt, "binary_sensor", "boiler_flame", EASYOPENTHERM_MQTT_DISCOVERY_MSG_FLAME_BINARY_SENSOR); 504 addEntity(*mqtt, "binary_sensor", "boiler_flame", EASYOPENTHERM_MQTT_DISCOVERY_MSG_FLAME_BINARY_SENSOR);
494 505
495 - char payload[16];  
496 - snprintf(payload, sizeof payload, "%.01f", ROOM_TEMPERATURE_MIN_SETPOINT);  
497 // Set all sensors, except those that are directly updated, to 'None', the binary sensor to 'Unknown' 506 // Set all sensors, except those that are directly updated, to 'None', the binary sensor to 'Unknown'
  507 + Serial.println("Set sensor values to 'none' by publishing to MQTT sensor state topics...");
498 publish(*mqtt, topicByReference("state_topic", EASYOPENTHERM_MQTT_DISCOVERY_MSG_SETPOINT_SENSOR), "{\"ch_setpoint\":\"None\"}"); 508 publish(*mqtt, topicByReference("state_topic", EASYOPENTHERM_MQTT_DISCOVERY_MSG_SETPOINT_SENSOR), "{\"ch_setpoint\":\"None\"}");
499 publish(*mqtt, topicByReference("state_topic", EASYOPENTHERM_MQTT_DISCOVERY_MSG_FLOW_TEMPERATURE_SENSOR), "{\"flow_temperature\":\"None\"}"); 509 publish(*mqtt, topicByReference("state_topic", EASYOPENTHERM_MQTT_DISCOVERY_MSG_FLOW_TEMPERATURE_SENSOR), "{\"flow_temperature\":\"None\"}");
500 publish(*mqtt, topicByReference("state_topic", EASYOPENTHERM_MQTT_DISCOVERY_MSG_RETURN_TEMPERATURE_SENSOR), "{\"return_temperature\":\"None\"}"); 510 publish(*mqtt, topicByReference("state_topic", EASYOPENTHERM_MQTT_DISCOVERY_MSG_RETURN_TEMPERATURE_SENSOR), "{\"return_temperature\":\"None\"}");
@@ -511,7 +521,8 @@ Serial.println(&quot;MQTT NOT connected&quot;); @@ -511,7 +521,8 @@ Serial.println(&quot;MQTT NOT connected&quot;);
511 // If the boikler can both heat and cool adds 'auto', 'off', 'cool' and 'heat' to the thermmostat modes 521 // If the boikler can both heat and cool adds 'auto', 'off', 'cool' and 'heat' to the thermmostat modes
512 // If the boikler can only heat adds 'off' and 'heat' to the thermmostat modes 522 // If the boikler can only heat adds 'off' and 'heat' to the thermmostat modes
513 void updateClimateEntity(bool canCool) { 523 void updateClimateEntity(bool canCool) {
514 - StaticJsonDocument<fullJsonDocSize> discoveryMsgDoc; 524 +// StaticJsonDocument<fullJsonDocSize> discoveryMsgDoc;
  525 + DynamicJsonDocument discoveryMsgDoc(fullJsonDocSize);
515 526
516 if(discoveryMsgToJsonDoc(discoveryMsgDoc, EASYOPENTHERM_MQTT_DISCOVERY_MSG_CLIMATE)) { 527 if(discoveryMsgToJsonDoc(discoveryMsgDoc, EASYOPENTHERM_MQTT_DISCOVERY_MSG_CLIMATE)) {
517 JsonArray modesArray = discoveryMsgDoc["modes"]; 528 JsonArray modesArray = discoveryMsgDoc["modes"];
@@ -552,7 +563,6 @@ void updateDHWEntity(bool enableDHW) { @@ -552,7 +563,6 @@ void updateDHWEntity(bool enableDHW) {
552 void updateFlameSensor(uint8_t statusFlags) { 563 void updateFlameSensor(uint8_t statusFlags) {
553 static bool flameSensorInitialised = false; 564 static bool flameSensorInitialised = false;
554 static uint8_t previousStatusFlags = 0; 565 static uint8_t previousStatusFlags = 0;
555 -Serial.printf("Secondary status flags is 0x%02x\n", statusFlags);  
556 566
557 if(!flameSensorInitialised || (statusFlags & uint8_t(OpenTherm::STATUS_FLAGS::SECONDARY_FLAME_STATUS)) != (previousStatusFlags & uint8_t(OpenTherm::STATUS_FLAGS::SECONDARY_FLAME_STATUS))) { 567 if(!flameSensorInitialised || (statusFlags & uint8_t(OpenTherm::STATUS_FLAGS::SECONDARY_FLAME_STATUS)) != (previousStatusFlags & uint8_t(OpenTherm::STATUS_FLAGS::SECONDARY_FLAME_STATUS))) {
558 const char * topic = topicByReference("state_topic", EASYOPENTHERM_MQTT_DISCOVERY_MSG_FLAME_BINARY_SENSOR); 568 const char * topic = topicByReference("state_topic", EASYOPENTHERM_MQTT_DISCOVERY_MSG_FLAME_BINARY_SENSOR);
@@ -591,10 +601,11 @@ void updateDHWSensor(uint8_t statusFlags) { @@ -591,10 +601,11 @@ void updateDHWSensor(uint8_t statusFlags) {
591 // updateSensors 601 // updateSensors
592 // Update the values of all 'interval' sensors in Home Assistant by sending the value in the right format to the topic looked up in the discovery JSON. OpenTherm sensor can be read and do not have 602 // Update the values of all 'interval' sensors in Home Assistant by sending the value in the right format to the topic looked up in the discovery JSON. OpenTherm sensor can be read and do not have
593 // an entity yet in Home Assistant are created by sending the discovery JSON to the right '/config' topic. 603 // an entity yet in Home Assistant are created by sending the discovery JSON to the right '/config' topic.
594 -void updateSensors(MqttClient & client, 604 +void updateSensors(PubSubClient & client,
  605 + bool isConnectedOT,
595 float CHSetpoint, 606 float CHSetpoint,
596 uint32_t & previousOTCommunicationMs) { 607 uint32_t & previousOTCommunicationMs) {
597 - if(client.isConnected()) { 608 + if(client.connected()) {
598 char payload[64]; 609 char payload[64];
599 const char * topic; 610 const char * topic;
600 611
@@ -611,74 +622,80 @@ void updateSensors(MqttClient &amp; client, @@ -611,74 +622,80 @@ void updateSensors(MqttClient &amp; client,
611 publish(client, topic, payload); 622 publish(client, topic, payload);
612 } 623 }
613 624
614 - // Publish the Central Heating setpoint temperature  
615 - snprintf(payload, sizeof payload, "{\"ch_setpoint\":%.01f}", CHSetpoint);  
616 - topic = topicByReference("state_topic", EASYOPENTHERM_MQTT_DISCOVERY_MSG_SETPOINT_SENSOR);  
617 - publish(client, topic, payload); 625 + if(isConnectedOT) {
  626 + // Publish the Central Heating setpoint temperature
  627 + snprintf(payload, sizeof payload, "{\"ch_setpoint\":%.01f}", CHSetpoint);
  628 + topic = topicByReference("state_topic", EASYOPENTHERM_MQTT_DISCOVERY_MSG_SETPOINT_SENSOR);
  629 + publish(client, topic, payload);
618 630
619 - float value;  
620 - // Test if Relative Modulation Level can be read from the boiler  
621 - if(readSensor(thermostat, OpenTherm::READ_DATA_ID::RELATIVE_MODULATION_LEVEL, value, previousOTCommunicationMs)) {  
622 - Serial.printf("Relative Modulation level is %.01f %\n", value);  
623 - // Use a static variable to keep track of the entity already being created  
624 - static bool relativeModulationLevelDiscoveryPublished = false;  
625 - if(!relativeModulationLevelDiscoveryPublished) {  
626 - // Create the entity  
627 - addEntity(client, "sensor", "relative_modulation", EASYOPENTHERM_MQTT_DISCOVERY_MSG_RELATIVE_MODULATION_SENSOR);  
628 - relativeModulationLevelDiscoveryPublished = true; 631 + float value;
  632 + // Test if Relative Modulation Level can be read from the boiler
  633 + if(readSensor(thermostat, OpenTherm::READ_DATA_ID::RELATIVE_MODULATION_LEVEL, value, previousOTCommunicationMs)) {
  634 + Serial.printf("Relative Modulation level is %.01f %\n", value);
  635 + // Use a static variable to keep track of the entity already being created
  636 + static bool relativeModulationLevelDiscoveryPublished = false;
  637 + if(!relativeModulationLevelDiscoveryPublished) {
  638 + // Create the entity
  639 + addEntity(client, "sensor", "relative_modulation", EASYOPENTHERM_MQTT_DISCOVERY_MSG_RELATIVE_MODULATION_SENSOR);
  640 + relativeModulationLevelDiscoveryPublished = true;
  641 + }
  642 + // Publish the Relative Modulation Level
  643 + snprintf(payload, sizeof payload, "{\"relative_modulation\":%.01f}", value);
  644 + topic = topicByReference("state_topic", EASYOPENTHERM_MQTT_DISCOVERY_MSG_RELATIVE_MODULATION_SENSOR);
  645 + publish(client, topic, payload);
629 } 646 }
630 - // Publish the Relative Modulation Level  
631 - snprintf(payload, sizeof payload, "{\"relative_modulation\":%.01f}", value);  
632 - topic = topicByReference("state_topic", EASYOPENTHERM_MQTT_DISCOVERY_MSG_RELATIVE_MODULATION_SENSOR);  
633 - publish(client, topic, payload);  
634 - }  
635 - // Test if Relative Central Heating Water Pressure can be read from the boiler  
636 - if(readSensor(thermostat, OpenTherm::READ_DATA_ID::CH_WATER_PRESSURE, value, previousOTCommunicationMs)) {  
637 - Serial.printf("Central Heating water pressure is %.01f bar\n", value);  
638 - // Use a static variable to keep track of the entity already being created  
639 - static bool waterPressureEntityAdded = false;  
640 - // Create the entity  
641 - if(!waterPressureEntityAdded) {  
642 - addEntity(client, "sensor", "water_pressure", EASYOPENTHERM_MQTT_DISCOVERY_MSG_WATER_PRESSURE_SENSOR);  
643 - waterPressureEntityAdded = true; 647 +
  648 + // Test if Relative Central Heating Water Pressure can be read from the boiler
  649 + if(readSensor(thermostat, OpenTherm::READ_DATA_ID::CH_WATER_PRESSURE, value, previousOTCommunicationMs)) {
  650 + Serial.printf("Central Heating water pressure is %.01f bar\n", value);
  651 + // Use a static variable to keep track of the entity already being created
  652 + static bool waterPressureEntityAdded = false;
  653 + // Create the entity
  654 + if(!waterPressureEntityAdded) {
  655 + addEntity(client, "sensor", "water_pressure", EASYOPENTHERM_MQTT_DISCOVERY_MSG_WATER_PRESSURE_SENSOR);
  656 + waterPressureEntityAdded = true;
  657 + }
  658 + // Publish the Central Heating Water Pressure
  659 + snprintf(payload, sizeof payload, "{\"water_pressure\":%.01f}", value);
  660 + topic = topicByReference("state_topic", EASYOPENTHERM_MQTT_DISCOVERY_MSG_WATER_PRESSURE_SENSOR);
  661 + publish(client, topic, payload);
644 } 662 }
645 - // Publish the Central Heating Water Pressure  
646 - snprintf(payload, sizeof payload, "{\"water_pressure\":%.01f}", value);  
647 - topic = topicByReference("state_topic", EASYOPENTHERM_MQTT_DISCOVERY_MSG_WATER_PRESSURE_SENSOR);  
648 - publish(client, topic, payload);  
649 - }  
650 - // Test if Flow Temperature can be read from the boiler  
651 - if(readSensor(thermostat, OpenTherm::READ_DATA_ID::BOILER_WATER_TEMP, value, previousOTCommunicationMs)) {  
652 - Serial.printf("Flow water temperature from boiler is %.01f %\n", value);  
653 - // Use a static variable to keep track of the entity already being created  
654 - static bool flowTemperatureEntityAdded = false;  
655 - // Create the entity  
656 - if(!flowTemperatureEntityAdded) {  
657 - addEntity(client, "sensor", "flow_temperature", EASYOPENTHERM_MQTT_DISCOVERY_MSG_FLOW_TEMPERATURE_SENSOR);  
658 - flowTemperatureEntityAdded = true; 663 +
  664 + // Test if Flow Temperature can be read from the boiler
  665 + if(readSensor(thermostat, OpenTherm::READ_DATA_ID::BOILER_WATER_TEMP, value, previousOTCommunicationMs)) {
  666 + Serial.printf("Flow water temperature from boiler is %.01f %\n", value);
  667 + // Use a static variable to keep track of the entity already being created
  668 + static bool flowTemperatureEntityAdded = false;
  669 + // Create the entity
  670 + if(!flowTemperatureEntityAdded) {
  671 + addEntity(client, "sensor", "flow_temperature", EASYOPENTHERM_MQTT_DISCOVERY_MSG_FLOW_TEMPERATURE_SENSOR);
  672 + flowTemperatureEntityAdded = true;
  673 + }
  674 + // Publish the Flow Temperature
  675 + snprintf(payload, sizeof payload, "{\"flow_temperature\":%.01f}", value);
  676 + topic = topicByReference("state_topic", EASYOPENTHERM_MQTT_DISCOVERY_MSG_FLOW_TEMPERATURE_SENSOR);
  677 + publish(client, topic, payload);
659 } 678 }
660 - // Publish the Flow Temperature  
661 - snprintf(payload, sizeof payload, "{\"flow_temperature\":%.01f}", value);  
662 - topic = topicByReference("state_topic", EASYOPENTHERM_MQTT_DISCOVERY_MSG_FLOW_TEMPERATURE_SENSOR);  
663 - publish(client, topic, payload);  
664 - }  
665 - // Test if Return Temperature can be read from the boiler  
666 - if(readSensor(thermostat, OpenTherm::READ_DATA_ID::RETURN_WATER_TEMPERATURE, value, previousOTCommunicationMs)) {  
667 - Serial.printf("Return water temperature to boiler is %.01f %\n", value);  
668 - // Use a static variable to keep track of the entity already being created  
669 - static bool returnTemperatureEntityAdded = false;  
670 - // Create the entity  
671 - if(!returnTemperatureEntityAdded) {  
672 - addEntity(client, "sensor", "return_temperature", EASYOPENTHERM_MQTT_DISCOVERY_MSG_RETURN_TEMPERATURE_SENSOR);  
673 - returnTemperatureEntityAdded = true; 679 +
  680 + // Test if Return Temperature can be read from the boiler
  681 + if(readSensor(thermostat, OpenTherm::READ_DATA_ID::RETURN_WATER_TEMPERATURE, value, previousOTCommunicationMs)) {
  682 + Serial.printf("Return water temperature to boiler is %.01f %\n", value);
  683 + // Use a static variable to keep track of the entity already being created
  684 + static bool returnTemperatureEntityAdded = false;
  685 + // Create the entity
  686 + if(!returnTemperatureEntityAdded) {
  687 + addEntity(client, "sensor", "return_temperature", EASYOPENTHERM_MQTT_DISCOVERY_MSG_RETURN_TEMPERATURE_SENSOR);
  688 + returnTemperatureEntityAdded = true;
  689 + }
  690 + // Publish the Return Temperature
  691 + snprintf(payload, sizeof payload, "{\"return_temperature\":%.01f}", value);
  692 + topic = topicByReference("state_topic", EASYOPENTHERM_MQTT_DISCOVERY_MSG_RETURN_TEMPERATURE_SENSOR);
  693 + publish(client, topic, payload);
674 } 694 }
675 - // Publish the Return Temperature  
676 - snprintf(payload, sizeof payload, "{\"return_temperature\":%.01f}", value);  
677 - topic = topicByReference("state_topic", EASYOPENTHERM_MQTT_DISCOVERY_MSG_RETURN_TEMPERATURE_SENSOR);  
678 - publish(client, topic, payload); 695 +
  696 + // Any other OpenTherm sensor that returns a float (f8.8) can be added in the same way as above by using the correct OpenTherm::READ_DATA_ID::, adding a dicovery JSON in JSONs.h, calling
  697 + // addEntity with the according parameters and publishing the value to the right topic in the right format
679 } 698 }
680 - // Any other OpenTherm sensor that returns a float (f8.8) can be added in the same way as above by using the correct OpenTherm::READ_DATA_ID::, adding a dicovery JSON in JSONs.h, calling  
681 - // addEntity with the according parameters and publishing the value to the right topic in the right format  
682 } 699 }
683 } 700 }
684 701
@@ -701,17 +718,15 @@ void updateRoomTemperatureStale(bool stale) { @@ -701,17 +718,15 @@ void updateRoomTemperatureStale(bool stale) {
701 // subscribeAll() 718 // subscribeAll()
702 // Subsbribe to all needed topics: one for the room temperature setpoint, one for the mode (OFF / HEAT / COOL / AUTO) and one for 719 // Subsbribe to all needed topics: one for the room temperature setpoint, one for the mode (OFF / HEAT / COOL / AUTO) and one for
703 // the actual room temperature 720 // the actual room temperature
704 -bool subscribeAll(MqttClient & client) {  
705 - if(client.isConnected()) {  
706 - subscribe(client, processSetpointTemperatureMessage, EASYOPENTHERM_MQTT_DISCOVERY_MSG_CLIMATE, "temperature_command_topic");  
707 - subscribe(client, processClimateMessage, EASYOPENTHERM_MQTT_DISCOVERY_MSG_CLIMATE, "mode_command_topic");  
708 - subscribe(client, processRoomTemperatureMessage, EASYOPENTHERM_MQTT_DISCOVERY_MSG_CLIMATE, "current_temperature_topic"); 721 +bool subscribeAll(PubSubClient & client) {
  722 + if(client.connected()) {
  723 + subscribe(client, EASYOPENTHERM_MQTT_DISCOVERY_MSG_CLIMATE, "temperature_command_topic");
  724 + subscribe(client, EASYOPENTHERM_MQTT_DISCOVERY_MSG_CLIMATE, "mode_command_topic");
  725 + subscribe(client, EASYOPENTHERM_MQTT_DISCOVERY_MSG_CLIMATE, "current_temperature_topic");
709 // subscribe(client, processClimateMessage, EASYOPENTHERM_MQTT_DISCOVERY_MSG_CLIMATE, "preset_mode_command_topic"); // Not implemented yet 726 // subscribe(client, processClimateMessage, EASYOPENTHERM_MQTT_DISCOVERY_MSG_CLIMATE, "preset_mode_command_topic"); // Not implemented yet
710 -  
711 - return true;  
712 } 727 }
713 728
714 - return false; 729 + return client.connected();
715 } 730 }
716 731
717 732
@@ -743,35 +758,49 @@ void loop() { @@ -743,35 +758,49 @@ void loop() {
743 758
744 759
745 // Check if we are still connected to the MQTT broker. If not, reconnect and resubscribe to the topics we are interested in 760 // Check if we are still connected to the MQTT broker. If not, reconnect and resubscribe to the topics we are interested in
746 - if(!mqtt->isConnected()) {  
747 - ESP_LOGI(TAG, "MQTT disconnected");  
748 -Serial.println("MQTT disconnected");  
749 - wiFiClient.stop();  
750 - wiFiClient.connect(mqtt_server, 1883);  
751 - connectMQTT(*mqtt, MQTT_ID, mqtt_user, mqtt_password);  
752 - subscribed = false; 761 + if(!mqtt->connected()) {
  762 + Serial.println("MQTT disconnected");
  763 + // handle the TCP/IP socket connection outside of PubSubClient because of connection issues
  764 + if(!wiFiClient.connected()) {
  765 + Serial.printf("WiFi client disconnected, WiFi status = %d\n", WiFi.status());
  766 +// wiFiClient.stop();
  767 + wiFiClient.connect(mqtt_server, 1883);
  768 + if(wiFiClient.connected()) {
  769 + Serial.println("WiFi client reconnected");
  770 + }
  771 + }
  772 + if(wiFiClient.connected()) {
  773 + Serial.printf("WiFi client is connected, WiFi status = %d\n", WiFi.status());
  774 + bool MQTTConnected = connectMQTT(*mqtt, MQTT_ID, mqtt_user, mqtt_password);
  775 + Serial.printf("MQTT reconnected is %s\n", MQTTConnected ? "true" : "false");
  776 + subscribed = false;
  777 + } else {
  778 + Serial.println("WiFi client did not reconnected");
  779 + }
753 } 780 }
754 781
755 // Subscribe to all relevant topics after a new connection 782 // Subscribe to all relevant topics after a new connection
756 if(!subscribed) { 783 if(!subscribed) {
  784 + Serial.println("Subscribe to all relevant MQTT topics...");
757 if(subscribeAll(*mqtt)) { 785 if(subscribeAll(*mqtt)) {
758 subscribed = true; 786 subscribed = true;
759 } 787 }
760 } 788 }
761 789
762 - uint32_t thermostatStateTimestampAligned = ldiv(time(nullptr), PUBLISH_STATE_UPDATE_INTERVAL).quot * PUBLISH_STATE_UPDATE_INTERVAL; // Align to exact intervals 790 + uint32_t thermostatStateTimestampAligned = (time(nullptr) / PUBLISH_STATE_UPDATE_INTERVAL) * PUBLISH_STATE_UPDATE_INTERVAL; // Align to exact intervals
763 uint32_t thermostatStateTimestampMs = millis(); 791 uint32_t thermostatStateTimestampMs = millis();
764 -  
765 if(millis() - previousOTCommunicationMs >= 1000) { // OpenTherm specifies that primary to secondary communication should take place at least every second 792 if(millis() - previousOTCommunicationMs >= 1000) { // OpenTherm specifies that primary to secondary communication should take place at least every second
766 // connected() becomes 'true' after communication has taken place between thermostat and boiler and the boiler's configuration flags are read 793 // connected() becomes 'true' after communication has taken place between thermostat and boiler and the boiler's configuration flags are read
  794 +
767 if(!thermoStateMachine.connected()) { 795 if(!thermoStateMachine.connected()) {
768 // Try to contact the boiler and construct the thermostat's primary flags from the boiler's capabilities 796 // Try to contact the boiler and construct the thermostat's primary flags from the boiler's capabilities
769 OpenTherm::CONFIGURATION_FLAGS configurationFlags; 797 OpenTherm::CONFIGURATION_FLAGS configurationFlags;
770 if(readSecondaryConfiguration(thermostat, configurationFlags, previousOTCommunicationMs)) { 798 if(readSecondaryConfiguration(thermostat, configurationFlags, previousOTCommunicationMs)) {
  799 + Serial.println("OpenTherm secondary connected");
771 thermoStateMachine.initPrimaryFlags(configurationFlags); 800 thermoStateMachine.initPrimaryFlags(configurationFlags);
772 801
773 - updateClimateEntity((uint8_t(configurationFlags) & uint8_t(OpenTherm::CONFIGURATION_FLAGS::SECONDARY_COOLING)) != 0);  
774 - updateDHWEntity((uint8_t(configurationFlags) & uint8_t(OpenTherm::CONFIGURATION_FLAGS::SECONDARY_DHW_PRESENT)) != 0); 802 + updateClimateEntity(((uint8_t) configurationFlags & (uint8_t) OpenTherm::CONFIGURATION_FLAGS::SECONDARY_COOLING) != 0);
  803 + updateDHWEntity(((uint8_t) configurationFlags & (uint8_t) OpenTherm::CONFIGURATION_FLAGS::SECONDARY_DHW_PRESENT) != 0);
775 } 804 }
776 } 805 }
777 806
@@ -829,7 +858,7 @@ Serial.println(&quot;MQTT disconnected&quot;); @@ -829,7 +858,7 @@ Serial.println(&quot;MQTT disconnected&quot;);
829 // Read status in every loop, to meet the 'communication each second' requirement 858 // Read status in every loop, to meet the 'communication each second' requirement
830 if(readStatus(thermostat, primaryFlags, statusFlags, previousOTCommunicationMs)) { 859 if(readStatus(thermostat, primaryFlags, statusFlags, previousOTCommunicationMs)) {
831 // Inform Home Assitant directly about the status; is automatically ignored if the flame status or CH / DHW status did not change 860 // Inform Home Assitant directly about the status; is automatically ignored if the flame status or CH / DHW status did not change
832 - updateFlameSensor(statusFlags); 861 + updateFlameSensor(statusFlags);
833 updateDHWSensor(statusFlags); 862 updateDHWSensor(statusFlags);
834 } 863 }
835 } 864 }
@@ -837,9 +866,10 @@ Serial.println(&quot;MQTT disconnected&quot;); @@ -837,9 +866,10 @@ Serial.println(&quot;MQTT disconnected&quot;);
837 866
838 // Publish the 'interval' sensors' at exact 'PUBLISH_STATE_UPDATE_INTERVAL' intervals 867 // Publish the 'interval' sensors' at exact 'PUBLISH_STATE_UPDATE_INTERVAL' intervals
839 if(thermostatStateTimestampAligned - publishedThermostatStateTimestamp >= PUBLISH_STATE_UPDATE_INTERVAL) { 868 if(thermostatStateTimestampAligned - publishedThermostatStateTimestamp >= PUBLISH_STATE_UPDATE_INTERVAL) {
840 - updateSensors(*mqtt, CHSetpoint, previousOTCommunicationMs); 869 + Serial.println("Update sensors by publishing sensor values to MQTT broker...");
  870 + updateSensors(*mqtt, thermoStateMachine.connected(), CHSetpoint, previousOTCommunicationMs);
841 publishedThermostatStateTimestamp = thermostatStateTimestampAligned; 871 publishedThermostatStateTimestamp = thermostatStateTimestampAligned;
842 } 872 }
843 873
844 - mqtt->yield(100); 874 + mqtt->loop();
845 } 875 }
examples/Advanced_Thermostat/MQTTHelpers.cpp
@@ -41,7 +41,7 @@ const char * chipID() { @@ -41,7 +41,7 @@ const char * chipID() {
41 return serialNumber; 41 return serialNumber;
42 } 42 }
43 43
44 -#include <machine/types.h> 44 +//#include <machine/types.h>
45 /* 45 /*
46 * shortID() 46 * shortID()
47 * Store the least significant two bytes of the ESP32 unique ID once. Return a pointer to the stored short ID. The ID is the same as the last two MAC bytes, in the same order 47 * Store the least significant two bytes of the ESP32 unique ID once. Return a pointer to the stored short ID. The ID is the same as the last two MAC bytes, in the same order
@@ -59,7 +59,18 @@ const char * shortID() { @@ -59,7 +59,18 @@ const char * shortID() {
59 #endif 59 #endif
60 60
61 uint8_t * MAC = (uint8_t *) &mac; 61 uint8_t * MAC = (uint8_t *) &mac;
  62 +
  63 +// ESP32 Chip ID is cca4f003f784 => 0x84, 0xf7, 0x03, 0xf0, 0xa4, 0xcc, 0x00, 0x00
  64 +// ESP8266 Chip ID is 00c2005a3cc2 => 0xc2, 0x3c, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x00
  65 +// So for the ESP32 MAC[4] and MAC[5] are the least significant bytes, and for the ESP8266 MAC[1] and MAC[0]
  66 +// Serial.print("Serial number bytes: "); for(size_t index = 0; index < 8; index++) Serial.printf("0x%02x, ", MAC[index]); Serial.println();
  67 +
  68 +// sprintf(shortNumber, "%02X%02X", MAC[4], MAC[5]);
  69 +#if defined(ESP32)
62 sprintf(shortNumber, "%02X%02X", MAC[4], MAC[5]); 70 sprintf(shortNumber, "%02X%02X", MAC[4], MAC[5]);
  71 +#else
  72 + sprintf(shortNumber, "%02X%02X", MAC[1], MAC[0]);
  73 +#endif
63 ESP_LOGI(TAG, "Short ID is %s", shortNumber); 74 ESP_LOGI(TAG, "Short ID is %s", shortNumber);
64 } 75 }
65 76
@@ -123,6 +134,7 @@ const char * topicByReference(const char * key, @@ -123,6 +134,7 @@ const char * topicByReference(const char * key,
123 DeserializationError error = deserializeJson(discoveryJsonPartDoc, discoveryMsgJson, DeserializationOption::Filter(filter)); 134 DeserializationError error = deserializeJson(discoveryJsonPartDoc, discoveryMsgJson, DeserializationOption::Filter(filter));
124 if(error) { 135 if(error) {
125 ESP_LOGE(TAG, "Deserialize error %s for JSON '%s'", error.f_str(), discoveryMsgJson); 136 ESP_LOGE(TAG, "Deserialize error %s for JSON '%s'", error.f_str(), discoveryMsgJson);
  137 +Serial.printf("Deserialize error %s for JSON '%s'\n", error.f_str(), discoveryMsgJson);
126 138
127 return nullptr; 139 return nullptr;
128 } 140 }
@@ -131,12 +143,14 @@ const char * topicByReference(const char * key, @@ -131,12 +143,14 @@ const char * topicByReference(const char * key,
131 const char * topic = discoveryJsonPartDoc[key]; 143 const char * topic = discoveryJsonPartDoc[key];
132 if(topic == nullptr) { 144 if(topic == nullptr) {
133 ESP_LOGE(TAG, "Key '%s' not found in JSON '%s'", key, discoveryMsgJson); 145 ESP_LOGE(TAG, "Key '%s' not found in JSON '%s'", key, discoveryMsgJson);
  146 +Serial.printf("Key '%s' not found in JSON '%s'\n", key, discoveryMsgJson);
134 147
135 return nullptr; 148 return nullptr;
136 } 149 }
137 150
138 if(*topic == '~' && baseTopic == nullptr) { 151 if(*topic == '~' && baseTopic == nullptr) {
139 ESP_LOGE(TAG, "Base topic '~' not found in JSON '%s'", discoveryMsgJson); 152 ESP_LOGE(TAG, "Base topic '~' not found in JSON '%s'", discoveryMsgJson);
  153 +Serial.printf("Base topic '~' not found in JSON '%s'\n", discoveryMsgJson);
140 154
141 return nullptr; 155 return nullptr;
142 } 156 }
@@ -149,6 +163,7 @@ const char * topicByReference(const char * key, @@ -149,6 +163,7 @@ const char * topicByReference(const char * key,
149 if(topicBuffer == nullptr) { 163 if(topicBuffer == nullptr) {
150 topicBufferSize = 0; 164 topicBufferSize = 0;
151 ESP_LOGE(TAG, "topicByReference out of memory"); 165 ESP_LOGE(TAG, "topicByReference out of memory");
  166 +Serial.printf("topicByReference out of memory\n");
152 167
153 return nullptr; 168 return nullptr;
154 } 169 }
@@ -179,24 +194,18 @@ bool validJson(const char * discoveryMsgJson) { @@ -179,24 +194,18 @@ bool validJson(const char * discoveryMsgJson) {
179 } 194 }
180 195
181 196
182 -bool connectMQTT(MqttClient & client, 197 +bool connectMQTT(PubSubClient & client,
183 const char * clientID, 198 const char * clientID,
184 const char * user, 199 const char * user,
185 const char * password) { 200 const char * password) {
186 // Start new MQTT connection 201 // Start new MQTT connection
187 - ESP_LOGI(TAG, "Connecting");  
188 - MqttClient::ConnectResult connectResult; 202 + ESP_LOGI(TAG, "Connecting...");
  203 +
189 // Connect 204 // Connect
190 - MQTTPacket_connectData options = MQTTPacket_connectData_initializer;  
191 - options.MQTTVersion = 4;  
192 - options.clientID.cstring = (char *)clientID;  
193 - options.username.cstring = (char *)user;  
194 - options.password.cstring = (char *)password;  
195 - options.cleansession = true;  
196 - options.keepAliveInterval = 15; // 15 seconds  
197 - MqttClient::Error::type rc = client.connect(options, connectResult);  
198 - if (rc != MqttClient::Error::SUCCESS) {  
199 - ESP_LOGE(TAG, "Connection error: %i", rc); 205 + bool connectionResult = client.connect(clientID, user, password);
  206 +
  207 + if(!connectionResult) {
  208 + ESP_LOGE(TAG, "Connection error: %d", client.state());
200 209
201 return false; 210 return false;
202 } 211 }
@@ -205,25 +214,29 @@ bool connectMQTT(MqttClient &amp; client, @@ -205,25 +214,29 @@ bool connectMQTT(MqttClient &amp; client,
205 } 214 }
206 215
207 216
208 -bool publish(MqttClient & client, 217 +bool publish(PubSubClient & client,
209 const char * topic, 218 const char * topic,
210 - const char * payload, 219 + const char * payload,
  220 + size_t payloadLength,
211 bool retained) { 221 bool retained) {
212 - if(!client.isConnected()) return false; 222 + if(payloadLength == 0) return true;
213 223
214 - MqttClient::Message message;  
215 - message.qos = MqttClient::QOS0;  
216 - message.retained = retained;  
217 - message.dup = true;  
218 - message.payload = (void *)payload;  
219 - message.payloadLen = strlen(payload);  
220 - client.publish(topic, message);  
221 -Serial.printf("PUBLISH topic is '%s' message is '%s'\n", topic, message.payload); 224 + if(!client.beginPublish(topic, payloadLength, retained)) return false;
  225 + size_t bytesWritten = client.write((const unsigned char *) payload, payloadLength);
222 226
223 - return true; 227 + return client.endPublish() && bytesWritten == payloadLength;
224 } 228 }
225 229
226 230
  231 +bool publish(PubSubClient & client,
  232 + const char * topic,
  233 + const char * payload,
  234 + bool retained) {
  235 + size_t payloadLength = strlen(payload);
  236 +
  237 + return publish(client, topic, payload, payloadLength, retained);
  238 +}
  239 +
227 bool discoveryMsgToJsonDoc(JsonDocument & discoveryMsgDoc, 240 bool discoveryMsgToJsonDoc(JsonDocument & discoveryMsgDoc,
228 const char * discoveryMsgJson) { 241 const char * discoveryMsgJson) {
229 DeserializationError error = deserializeJson(discoveryMsgDoc, discoveryMsgJson); 242 DeserializationError error = deserializeJson(discoveryMsgDoc, discoveryMsgJson);
@@ -243,35 +256,30 @@ bool discoveryMsgToJsonDoc(JsonDocument &amp; discoveryMsgDoc, @@ -243,35 +256,30 @@ bool discoveryMsgToJsonDoc(JsonDocument &amp; discoveryMsgDoc,
243 * Home Assistant MQTT components like 'climate', 'sensor' or 'binary_sensor' and object a unique ID to differentiate between e.g. two sensors in the same 256 * Home Assistant MQTT components like 'climate', 'sensor' or 'binary_sensor' and object a unique ID to differentiate between e.g. two sensors in the same
244 * device. 257 * device.
245 */ 258 */
246 -bool addEntity(MqttClient & client, 259 +bool addEntity(PubSubClient & client,
247 const char * component, 260 const char * component,
248 const char * object, 261 const char * object,
249 JsonDocument & discoveryMsgDoc) { 262 JsonDocument & discoveryMsgDoc) {
250 - if(!client.isConnected()) return false; 263 + if(!client.connected()) return false;
251 264
252 size_t jsonSize = measureJson(discoveryMsgDoc); 265 size_t jsonSize = measureJson(discoveryMsgDoc);
253 char discoveryMsgJson[jsonSize + 1]; 266 char discoveryMsgJson[jsonSize + 1];
254 serializeJson(discoveryMsgDoc, discoveryMsgJson, sizeof discoveryMsgJson); 267 serializeJson(discoveryMsgDoc, discoveryMsgJson, sizeof discoveryMsgJson);
255 268
256 - ESP_LOGI(TAG, "Discovery topic is '%s'\n", (String("homeassistant/") + String(component) + String("/") + String(chipID()) + String("/") + String(object) + String("/config")).c_str());  
257 - ESP_LOGI(TAG, "Discovery message is '%s'", discoveryMsgJson); 269 + size_t topicSize = sizeof("homeassistant/") - 1 + strlen(component) + sizeof("/") - 1 + strlen(chipID()) + sizeof("/") - 1 + strlen(object) + sizeof("/config") - 1 + 1;
  270 + char topic[topicSize];
  271 + sprintf(topic, "homeassistant/%s/%s/%s/config", component, chipID(), object);
258 272
259 - MqttClient::Message message;  
260 - message.qos = MqttClient::QOS0;  
261 - message.retained = false;  
262 - message.dup = false;  
263 - message.payload = (void*) discoveryMsgJson;  
264 - message.payloadLen = jsonSize;  
265 - client.publish((String("homeassistant/") + String(component) + String("/") + String(chipID()) + String("/") + String(object) + String("/config")).c_str(), message);  
266 -Serial.printf("PUBLISH topic is '%s' message is '%s'\n", (String("homeassistant/") + String(component) + String("/") + String(chipID()) + String("/") + String(object) + String("/config")).c_str(), message.payload); 273 + ESP_LOGI(TAG, "Discovery topic is '%s'\n", topic);
  274 + ESP_LOGI(TAG, "Discovery message is '%s'", discoveryMsgJson);
267 275
268 - return true; 276 + return publish(client, topic, discoveryMsgJson);
269 } 277 }
270 278
271 279
272 // addEntity() 280 // addEntity()
273 // Same as above but providing the discovery message as a const char * JSON 281 // Same as above but providing the discovery message as a const char * JSON
274 -bool addEntity(MqttClient & client, 282 +bool addEntity(PubSubClient & client,
275 const char * component, 283 const char * component,
276 const char * object, 284 const char * object,
277 const char * discoveryMsgJson) { 285 const char * discoveryMsgJson) {
@@ -287,23 +295,14 @@ bool addEntity(MqttClient &amp; client, @@ -287,23 +295,14 @@ bool addEntity(MqttClient &amp; client,
287 * Subscribe to the topic at at the given key in the discovery message. Before subscription the topic in the discovery 295 * Subscribe to the topic at at the given key in the discovery message. Before subscription the topic in the discovery
288 * message is expanded to the full topic by prefixing the "~" path. 296 * message is expanded to the full topic by prefixing the "~" path.
289 */ 297 */
290 -bool subscribe(MqttClient & client,  
291 - MqttClient::MessageHandlerCbk cbk, 298 +bool subscribe(PubSubClient & client,
292 const char * discoveryMessage, 299 const char * discoveryMessage,
293 const char * key) { 300 const char * key) {
294 const char * topic = topicByReference(key, discoveryMessage); 301 const char * topic = topicByReference(key, discoveryMessage);
295 if(topic == nullptr) return false; 302 if(topic == nullptr) return false;
296 303
297 - MqttClient::Error::type rc = client.subscribe(topic, MqttClient::QOS0, cbk);  
298 - if (rc != MqttClient::Error::SUCCESS) {  
299 - ESP_LOGE(TAG, "Subscribe error: %i for topic '%s'", rc, topic);  
300 - ESP_LOGE(TAG, "Drop connection");  
301 - client.disconnect(); 304 + ESP_LOGI(TAG, "Subscribe to '%s'", topic);
  305 +Serial.printf("Subscribe to '%s'\n", topic);
302 306
303 - return false;  
304 - }  
305 - ESP_LOGI(TAG, "Subscribed to '%s'", topic);  
306 -Serial.printf("Subscribed to '%s'\n", topic);  
307 -  
308 - return true;  
309 -}  
310 \ No newline at end of file 307 \ No newline at end of file
  308 + return client.subscribe(topic);
  309 +}
examples/Advanced_Thermostat/MQTTHelpers.h
@@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
2 2
3 3
4 #include <Arduino.h> 4 #include <Arduino.h>
5 -#include <MqttClient.h> 5 +#include <PubSubClient.h>
6 #include <ArduinoJson.h> 6 #include <ArduinoJson.h>
7 7
8 8
@@ -12,20 +12,6 @@ const char * chipID(); @@ -12,20 +12,6 @@ const char * chipID();
12 12
13 const char * shortID(); 13 const char * shortID();
14 14
15 -  
16 -// ============== Object to supply system functions ============================  
17 -class System: public MqttClient::System {  
18 -public:  
19 -  
20 - unsigned long millis() const {  
21 - return ::millis();  
22 - }  
23 -  
24 - void yield(void) {  
25 - ::yield();  
26 - }  
27 -};  
28 -  
29 void discoveryMessageSetIDs(char * discoveryMsgJson); 15 void discoveryMessageSetIDs(char * discoveryMsgJson);
30 16
31 const char * topicByReference(const char * key, 17 const char * topicByReference(const char * key,
@@ -33,12 +19,18 @@ const char * topicByReference(const char * key, @@ -33,12 +19,18 @@ const char * topicByReference(const char * key,
33 19
34 bool validJson(const char * discoveryMsgJson); 20 bool validJson(const char * discoveryMsgJson);
35 21
36 -bool connectMQTT(MqttClient & client, 22 +bool connectMQTT(PubSubClient & client,
37 const char * clientID, 23 const char * clientID,
38 const char * user, 24 const char * user,
39 const char * password); 25 const char * password);
40 26
41 -bool publish(MqttClient & client, 27 +bool publish(PubSubClient & client,
  28 + const char * topic,
  29 + const char * payload,
  30 + size_t payloadLength,
  31 + bool retained = false);
  32 +
  33 +bool publish(PubSubClient & client,
42 const char * topic, 34 const char * topic,
43 const char * payload, 35 const char * payload,
44 bool retained = false); 36 bool retained = false);
@@ -46,17 +38,16 @@ bool publish(MqttClient &amp; client, @@ -46,17 +38,16 @@ bool publish(MqttClient &amp; client,
46 bool discoveryMsgToJsonDoc(JsonDocument & discoveryMsgDoc, 38 bool discoveryMsgToJsonDoc(JsonDocument & discoveryMsgDoc,
47 const char * discoveryMsgJson); 39 const char * discoveryMsgJson);
48 40
49 -bool addEntity(MqttClient & client, 41 +bool addEntity(PubSubClient & client,
50 const char * component, 42 const char * component,
51 const char * object, 43 const char * object,
52 JsonDocument & discoveryMsgDoc); 44 JsonDocument & discoveryMsgDoc);
53 45
54 -bool addEntity(MqttClient & client, 46 +bool addEntity(PubSubClient & client,
55 const char * component, 47 const char * component,
56 const char * object, 48 const char * object,
57 const char * discoveryMsgJson); 49 const char * discoveryMsgJson);
58 50
59 -bool subscribe(MqttClient & client,  
60 - MqttClient::MessageHandlerCbk cbk, 51 +bool subscribe(PubSubClient & client,
61 const char * discoveryMessage, 52 const char * discoveryMessage,
62 const char * key); 53 const char * key);
63 \ No newline at end of file 54 \ No newline at end of file
examples/Advanced_Thermostat/OpenThermHelpers.cpp
@@ -134,7 +134,7 @@ bool readSecondaryConfiguration(OpenTherm &amp; thermostat, @@ -134,7 +134,7 @@ bool readSecondaryConfiguration(OpenTherm &amp; thermostat,
134 return true; 134 return true;
135 } 135 }
136 136
137 - configurationFlags = OpenTherm::CONFIGURATION_FLAGS(flags); 137 + configurationFlags = (OpenTherm::CONFIGURATION_FLAGS) flags;
138 138
139 return true; 139 return true;
140 } else { 140 } else {
examples/Advanced_Thermostat/ThermoStateMachine.cpp
@@ -25,19 +25,20 @@ bool ThermoStateMachine::connected() { @@ -25,19 +25,20 @@ bool ThermoStateMachine::connected() {
25 25
26 26
27 void ThermoStateMachine::initPrimaryFlags(OpenTherm::CONFIGURATION_FLAGS configurationFlags) { 27 void ThermoStateMachine::initPrimaryFlags(OpenTherm::CONFIGURATION_FLAGS configurationFlags) {
28 - _configurationFlags = uint8_t(configurationFlags); 28 + _configurationFlags = (uint8_t) configurationFlags;
29 _primaryFlags = 0; 29 _primaryFlags = 0;
30 // Enable DHW if boiler is capable of DHW 30 // Enable DHW if boiler is capable of DHW
31 - if(_configurationFlags & uint8_t(OpenTherm::CONFIGURATION_FLAGS::SECONDARY_DHW_PRESENT)) {  
32 - _primaryFlags |= uint8_t(OpenTherm::STATUS_FLAGS::PRIMARY_DHW_ENABLE); 31 + if(_configurationFlags & ((uint8_t) OpenTherm::CONFIGURATION_FLAGS::SECONDARY_DHW_PRESENT)) {
  32 + _primaryFlags |= (uint8_t) OpenTherm::STATUS_FLAGS::PRIMARY_DHW_ENABLE;
33 } 33 }
34 // If boiler is capable of cooling, signal this by setting 'canCool' 34 // If boiler is capable of cooling, signal this by setting 'canCool'
35 - _canCool = (_configurationFlags & uint8_t(OpenTherm::CONFIGURATION_FLAGS::SECONDARY_COOLING)) != 0; 35 + _canCool = (_configurationFlags & (uint8_t) OpenTherm::CONFIGURATION_FLAGS::SECONDARY_COOLING) != 0;
36 36
37 // Do not enable OTC 37 // Do not enable OTC
38 - // _primaryFlags |= uint8_t(OpenTherm::STATUS_FLAGS::PRIMARY_OTC_ENABLE); 38 + // _primaryFlags |= (uint8_t) OpenTherm::STATUS_FLAGS::PRIMARY_OTC_ENABLE;
39 39
40 _state = ThermostatState::OFF; 40 _state = ThermostatState::OFF;
  41 + _state_c_str = "Off";
41 } 42 }
42 43
43 44
@@ -195,8 +196,11 @@ bool ThermoStateMachine::update(float ro @@ -195,8 +196,11 @@ bool ThermoStateMachine::update(float ro
195 _previousRoomTemperatureSetpoint = roomTemperatureSetpoint; 196 _previousRoomTemperatureSetpoint = roomTemperatureSetpoint;
196 197
197 return false; 198 return false;
  199 + } else if(_state == ThermostatState::OFF) {
  200 +
  201 + return false;
198 } else { 202 } else {
199 - Serial.println("Unknown state"); 203 + Serial.printf("Unknown state %d\n", _state);
200 204
201 return false; 205 return false;
202 } 206 }
@@ -263,5 +267,3 @@ bool ThermoStateMachine::getRoomTemperatureStale() { @@ -263,5 +267,3 @@ bool ThermoStateMachine::getRoomTemperatureStale() {
263 const char * ThermoStateMachine::c_str() { 267 const char * ThermoStateMachine::c_str() {
264 return _state_c_str; 268 return _state_c_str;
265 } 269 }
266 -  
267 -