1414 limitations under the License.
1515*/
1616#if MQTT
17+ #include < ArduinoOTA.h>
18+
1719#include " mqtt_task.h"
1820#include " secrets.h"
1921
22+ using namespace json11 ;
23+
24+ // This MQTT demo assumes a home assistant MQTT instance, and will do automatic registration
25+ // via the home assistant config topic when it comes online and connects to MQTT. You shouldn't
26+ // need to change any of this to get it to work with home assistant.
27+ #define MQTT_CONFIG_TOPIC " homeassistant/text/" DEVICE_INSTANCE_NAME " /config"
28+ #define MQTT_COMMAND_TOPIC " home/" DEVICE_INSTANCE_NAME " /command"
29+ #define MQTT_STATE_TOPIC " home/" DEVICE_INSTANCE_NAME " /state"
30+ #define MQTT_AVAILABILITY_TOPIC " home/" DEVICE_INSTANCE_NAME " /availability"
2031
21- MQTTTask::MQTTTask (SplitflapTask& splitflap_task, Logger& logger, const uint8_t task_core) :
32+ MQTTTask::MQTTTask (SplitflapTask& splitflap_task, DisplayTask& display_task, Logger& logger, const uint8_t task_core) :
2233 Task(" MQTT" , 8192 , 1 , task_core),
2334 splitflap_task_(splitflap_task),
35+ display_task_(display_task),
2436 logger_(logger),
2537 wifi_client_(),
2638 mqtt_client_(wifi_client_) {
@@ -30,56 +42,173 @@ MQTTTask::MQTTTask(SplitflapTask& splitflap_task, Logger& logger, const uint8_t
3042
3143void MQTTTask::connectWifi () {
3244 WiFi.begin (WIFI_SSID, WIFI_PASSWORD);
33-
3445 // Disable WiFi sleep as it causes glitches on pin 39; see https://github.com/espressif/arduino-esp32/issues/4903#issuecomment-793187707
3546 WiFi.setSleep (WIFI_PS_NONE);
3647
48+ char buf[256 ];
49+
50+ snprintf (buf, sizeof (buf), " Wifi connecting to %s" , WIFI_SSID);
51+ display_task_.setMessage (0 , String (buf));
52+
3753 while (WiFi.status () != WL_CONNECTED) {
3854 delay (1000 );
3955 logger_.log (" Establishing connection to WiFi.." );
4056 }
4157
42- char buf[256 ];
4358 snprintf (buf, sizeof (buf), " Connected to network %s" , WIFI_SSID);
4459 logger_.log (buf);
60+
61+ snprintf (buf, sizeof (buf), " Wifi IP: %s" , WiFi.localIP ().toString ().c_str ());
62+ display_task_.setMessage (0 , String (buf));
4563}
4664
4765void MQTTTask::mqttCallback (char *topic, byte *payload, unsigned int length) {
4866 char buf[256 ];
4967 snprintf (buf, sizeof (buf), " Received mqtt callback for topic %s, length %u" , topic, length);
5068 logger_.log (buf);
51- splitflap_task_.showString ((const char *)payload, length);
69+
70+
71+ splitflap_task_.showString ((const char *)payload, length, false , true );
5272}
5373
5474void MQTTTask::connectMQTT () {
55- char buf[256 ];
56- mqtt_client_.setServer (MQTT_SERVER, 1883 );
75+ char buf[400 ];
76+ mqtt_client_.setServer (MQTT_SERVER, MQTT_PORT );
5777 logger_.log (" Attempting MQTT connection..." );
58- if (mqtt_client_.connect (HOSTNAME " -" MQTT_USER, MQTT_USER, MQTT_PASSWORD)) {
78+ snprintf (buf, sizeof (buf), " MQTT connecting to %s:%d" , MQTT_SERVER, MQTT_PORT);
79+ display_task_.setMessage (1 , String (buf));
80+
81+ if (mqtt_client_.connect (DEVICE_INSTANCE_NAME, MQTT_USER, MQTT_PASSWORD, MQTT_AVAILABILITY_TOPIC, 1 , true , " offline" )) {
5982 logger_.log (" MQTT connected" );
6083 mqtt_client_.subscribe (MQTT_COMMAND_TOPIC);
61- char buf[256 ];
62- snprintf (buf, sizeof (buf), " {\" name\" : \" %s\" , \" command_topic\" : \" %s\" , \" state_topic\" : \" %s\" , \" unique_id\" : \" %s\" }" , HOSTNAME, MQTT_COMMAND_TOPIC, MQTT_COMMAND_TOPIC, HOSTNAME);
63- mqtt_client_.publish (" homeassistant/text/splitflap/config" , buf);
84+
85+ // TODO: I believe it's possible to do more complex config to register as a device with multiple
86+ // entities; it'd be great to explore additional entities like a display backlight control,
87+ // detailed module status info, etc. Though at a certain point it may be preferable to make the
88+ // splitflap library an ESPHome external component for even more options, easier programming,
89+ // and more customization than MQTT offers...
90+ Json config = Json::object {
91+ { " name" , DEVICE_INSTANCE_NAME },
92+ { " command_topic" , MQTT_COMMAND_TOPIC },
93+ { " state_topic" , MQTT_STATE_TOPIC },
94+ { " availability_topic" , MQTT_AVAILABILITY_TOPIC },
95+ { " payload_available" , " online" },
96+ { " payload_not_available" , " offline" },
97+ { " unique_id" , DEVICE_INSTANCE_NAME },
98+ { " max" , NUM_MODULES },
99+ };
100+ std::string json_str = config.dump ();
101+ boolean result = mqtt_client_.publish (MQTT_CONFIG_TOPIC, json_str.c_str ());
102+ snprintf (buf, sizeof (buf), " Result of publish: %d" , result);
103+ logger_.log (buf);
64104 logger_.log (" Published MQTT discovery message" );
105+ logger_.log (json_str.c_str ());
106+
107+ mqtt_client_.publish (MQTT_AVAILABILITY_TOPIC, " online" , true );
108+
109+ snprintf (buf, sizeof (buf), " MQTT connected! (%s:%d)" , MQTT_SERVER, MQTT_PORT);
110+ display_task_.setMessage (1 , String (buf));
65111 } else {
66112 snprintf (buf, sizeof (buf), " MQTT failed rc=%d will try again in 5 seconds" , mqtt_client_.state ());
67113 logger_.log (buf);
114+
115+ snprintf (buf, sizeof (buf), " MQTT failed rc=%d" , mqtt_client_.state ());
116+ display_task_.setMessage (1 , String (buf));
68117 }
69118}
70119
71120void MQTTTask::run () {
121+ char buf[256 ];
122+ display_task_.setMessage (0 , " " );
123+ display_task_.setMessage (1 , " " );
72124 connectWifi ();
73125 connectMQTT ();
74126
127+ ArduinoOTA
128+ .onStart ([this ]() {
129+ if (ArduinoOTA.getCommand () == U_FLASH) {
130+ logger_.log (" Start OTA (flash)" );
131+ } else { // U_SPIFFS
132+ logger_.log (" Start OTA (filesystem)" );
133+ // NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
134+ }
135+ })
136+ .onEnd ([this ]() {
137+ logger_.log (" OTA End" );
138+ })
139+ .onProgress ([this ](unsigned int progress, unsigned int total) {
140+ char buf2[256 ];
141+ static uint32_t last_progress;
142+ if (millis () - last_progress > 1000 ) {
143+ snprintf (buf2, sizeof (buf2), " OTA Progress: %d%%" , (int )(progress * 100 / total));
144+ logger_.log (buf2);
145+ last_progress = millis ();
146+ }
147+ })
148+ .onError ([this ](ota_error_t error) {
149+ char buf2[256 ];
150+ snprintf (buf2, sizeof (buf2), " OTA Error: %u" , error);
151+ logger_.log (buf2);
152+ if (error == OTA_AUTH_ERROR) logger_.log (" Auth Failed" );
153+ else if (error == OTA_BEGIN_ERROR) logger_.log (" Begin Failed" );
154+ else if (error == OTA_CONNECT_ERROR) logger_.log (" Connect Failed" );
155+ else if (error == OTA_RECEIVE_ERROR) logger_.log (" Receive Failed" );
156+ else if (error == OTA_END_ERROR) logger_.log (" End Failed" );
157+ })
158+ .setHostname (DEVICE_INSTANCE_NAME)
159+ .setPassword (OTA_PASSWORD)
160+ .begin ();
161+
162+ wl_status_t wifi_last_status = WL_DISCONNECTED;
163+ uint32_t last_state_publish = 0 ;
164+ SplitflapState last_state = {};
165+ uint32_t last_availability_publish = 0 ;
75166 while (1 ) {
76167 long now = millis ();
168+ wl_status_t wifi_new_status = WiFi.status ();
169+ if (wifi_new_status != wifi_last_status) {
170+ if (wifi_new_status == WL_CONNECTED) {
171+ snprintf (buf, sizeof (buf), " Wifi IP: %s" , WiFi.localIP ().toString ().c_str ());
172+ display_task_.setMessage (0 , String (buf));
173+ } else {
174+ snprintf (buf, sizeof (buf), " Wifi connecting to %s" , WIFI_SSID);
175+ display_task_.setMessage (0 , String (buf));
176+ }
177+ wifi_last_status = wifi_new_status;
178+ }
77179 if (!mqtt_client_.connected () && (now - mqtt_last_connect_time_) > 5000 ) {
78180 logger_.log (" Reconnecting MQTT" );
79181 mqtt_last_connect_time_ = now;
80182 connectMQTT ();
81183 }
184+ if (mqtt_client_.connected ()) {
185+ SplitflapState state = splitflap_task_.getState ();
186+ if (state != last_state) {
187+ char flap_buf[NUM_MODULES+1 ];
188+ bool all_idle = true ;
189+ for (uint8_t i = 0 ; i < NUM_MODULES; i++) {
190+ flap_buf[i] = flaps[state.modules [i].flap_index ];
191+ if (state.modules [i].moving ) {
192+ all_idle = false ;
193+ }
194+ }
195+ flap_buf[NUM_MODULES] = 0 ;
196+
197+ if (all_idle && (now - last_state_publish) > 200 ) {
198+ last_state = state;
199+ last_state_publish = now;
200+ snprintf (buf, sizeof (buf), " Publishing state: %s" , flap_buf);
201+ logger_.log (buf);
202+ mqtt_client_.publish (MQTT_STATE_TOPIC, flap_buf);
203+ }
204+ }
205+ if (now > last_availability_publish + 1800000 ) {
206+ mqtt_client_.publish (MQTT_AVAILABILITY_TOPIC, " online" , true );
207+ last_availability_publish = now;
208+ }
209+ }
82210 mqtt_client_.loop ();
211+ ArduinoOTA.handle ();
83212 delay (1 );
84213 }
85214}
0 commit comments