EnigmaIOT  0.9.3
Secure sensor and gateway platform based on ESP8266 and ESP32
GwOutput_mqtt.cpp
Go to the documentation of this file.
1 
11 #include <Arduino.h>
12 #include "GwOutput_mqtt.h"
13 #include <ArduinoJson.h>
14 #include <ESPAsyncWebServer.h>
15 #include <helperFunctions.h>
16 #include <debug.h>
17 #include <PubSubClient.h>
18 
19 #ifdef ESP32
20 #include <WiFi.h>
21 #include <AsyncTCP.h>
22 #include <SPIFFS.h>
23 #include "esp_system.h"
24 #include "esp_event.h"
25 #include "mqtt_client.h"
26 #include "esp_tls.h"
27 #elif defined(ESP8266)
28 #include <ESP8266WiFi.h>
29 #include <ESPAsyncTCP.h>
30 #include <Hash.h>
31 #include <SPI.h>
32 #ifdef SECURE_MQTT
33 #include <WiFiClientSecure.h>
34 #else
35 #include <WiFiClient.h>
36 #endif // SECURE_MQTT
37 #endif // ESP32
38 
39 #include <FS.h>
40 
41 
43 
45  enigmaIotGateway = enigmaIotGw;
46  mqttServerParam = new AsyncWiFiManagerParameter ("mqttserver", "MQTT Server", mqttgw_config.mqtt_server, 41, "required type=\"text\" maxlength=40");
47  char port[10];
48  itoa (mqttgw_config.mqtt_port, port, 10);
49  mqttPortParam = new AsyncWiFiManagerParameter ("mqttport", "MQTT Port", port, 6, "required type=\"number\" min=\"0\" max=\"65535\" step=\"1\"");
50  mqttUserParam = new AsyncWiFiManagerParameter ("mqttuser", "MQTT User", mqttgw_config.mqtt_user, 21, "required type=\"text\" maxlength=20");
51  mqttPassParam = new AsyncWiFiManagerParameter ("mqttpass", "MQTT Password", "", 41, "type=\"password\" maxlength=40");
52 
57 
58 }
59 
61  if (!SPIFFS.begin ()) {
62  DEBUG_WARN ("Error opening filesystem");
63  }
64  DEBUG_DBG ("Filesystem opened");
65 
66  File configFile = SPIFFS.open (CONFIG_FILE, "w");
67  if (!configFile) {
68  DEBUG_WARN ("Failed to open config file %s for writing", CONFIG_FILE);
69  return false;
70  } else {
71  DEBUG_DBG ("%s opened for writting", CONFIG_FILE);
72  }
73 
74  const size_t capacity = JSON_OBJECT_SIZE (4) + 110;
75  DynamicJsonDocument doc (capacity);
76 
77  doc["mqtt_server"] = mqttgw_config.mqtt_server;
78  doc["mqtt_port"] = mqttgw_config.mqtt_port;
79  doc["mqtt_user"] = mqttgw_config.mqtt_user;
80  doc["mqtt_pass"] = mqttgw_config.mqtt_pass;
81 
82  if (serializeJson (doc, configFile) == 0) {
83  DEBUG_ERROR ("Failed to write to file");
84  configFile.close ();
85  //SPIFFS.remove (CONFIG_FILE); // Testing only
86  return false;
87  }
88 
89  String output;
90  serializeJsonPretty (doc, output);
91 
92  DEBUG_DBG ("%s", output.c_str ());
93 
94  configFile.flush ();
95  size_t size = configFile.size ();
96 
97  configFile.close ();
98  DEBUG_DBG ("Gateway configuration saved to flash. %u bytes", size);
99  return true;
100 }
101 
103  //SPIFFS.remove (CONFIG_FILE); // Only for testing
104  bool json_correct = false;
105 
106  if (!SPIFFS.begin ()) {
107  DEBUG_WARN ("Error starting filesystem. Formatting");
108  SPIFFS.format ();
109  WiFi.disconnect ();
110  }
111 
112  if (SPIFFS.exists (CONFIG_FILE)) {
113 
114  DEBUG_DBG ("Opening %s file", CONFIG_FILE);
115  File configFile = SPIFFS.open (CONFIG_FILE, "r");
116  if (configFile) {
117  size_t size = configFile.size ();
118  DEBUG_DBG ("%s opened. %u bytes", CONFIG_FILE, size);
119 
120  const size_t capacity = JSON_OBJECT_SIZE (4) + 110;
121  DynamicJsonDocument doc (capacity);
122 
123  DeserializationError error = deserializeJson (doc, configFile);
124 
125  if (error) {
126  DEBUG_ERROR ("Failed to parse file");
127  } else {
128  DEBUG_DBG ("JSON file parsed");
129  }
130 
131  if (doc.containsKey ("mqtt_server") && doc.containsKey ("mqtt_port")
132  && doc.containsKey ("mqtt_user") && doc.containsKey ("mqtt_pass")) {
133  json_correct = true;
134  }
135 
136  strncpy (mqttgw_config.mqtt_server, doc["mqtt_server"] | "", sizeof (mqttgw_config.mqtt_server));
137  mqttgw_config.mqtt_port = doc["mqtt_port"].as<int> ();
138  strncpy (mqttgw_config.mqtt_user, doc["mqtt_user"] | "", sizeof (mqttgw_config.mqtt_user));
139  strncpy (mqttgw_config.mqtt_pass, doc["mqtt_pass"] | "", sizeof (mqttgw_config.mqtt_pass));
140 
141  configFile.close ();
142  if (json_correct) {
143  DEBUG_INFO ("MQTT output module configuration successfuly read");
144  }
145  DEBUG_DBG ("==== MQTT Configuration ====");
146  DEBUG_DBG ("MQTT server: %s", mqttgw_config.mqtt_server);
147  DEBUG_DBG ("MQTT port: %d", mqttgw_config.mqtt_port);
148  DEBUG_DBG ("MQTT user: %s", mqttgw_config.mqtt_user);
149  DEBUG_VERBOSE ("MQTT password: %s", mqttgw_config.mqtt_pass);
150 
151  String output;
152  serializeJsonPretty (doc, output);
153 
154  DEBUG_DBG ("JSON file %s", output.c_str ());
155 
156  } else {
157  DEBUG_WARN ("Error opening %s", CONFIG_FILE);
158  }
159  } else {
160  DEBUG_WARN ("%s do not exist", CONFIG_FILE);
161  }
162 
163  return json_correct;
164 }
165 
166 
168  DEBUG_INFO ("==== Config Portal MQTTGW result ====");
169  DEBUG_INFO ("MQTT server: %s", mqttServerParam->getValue ());
170  DEBUG_INFO ("MQTT port: %s", mqttPortParam->getValue ());
171  DEBUG_INFO ("MQTT user: %s", mqttUserParam->getValue ());
172  DEBUG_INFO ("MQTT password: %s", mqttPassParam->getValue ());
173  DEBUG_INFO ("Status: %s", status ? "true" : "false");
174 
176  memcpy (mqttgw_config.mqtt_server, mqttServerParam->getValue (), mqttServerParam->getValueLength ());
177  mqttgw_config.mqtt_server[mqttServerParam->getValueLength ()] = '\0';
178  DEBUG_DBG ("MQTT Server: %s", mqttgw_config.mqtt_server);
179  mqttgw_config.mqtt_port = atoi (mqttPortParam->getValue ());
180  memcpy (mqttgw_config.mqtt_user, mqttUserParam->getValue (), mqttUserParam->getValueLength ());
181  const char* mqtt_pass = mqttPassParam->getValue ();
182  if (mqtt_pass && (mqtt_pass[0] != '\0')) {// If password is empty, keep the old one
183  memcpy (mqttgw_config.mqtt_pass, mqtt_pass, mqttPassParam->getValueLength ());
184  mqttgw_config.mqtt_pass[mqttPassParam->getValueLength ()] = '\0';
185  } else {
186  DEBUG_INFO ("MQTT password field empty. Keeping the old one");
187  }
188  DEBUG_DBG ("MQTT pass: %s", mqttgw_config.mqtt_pass);
189  if (!saveConfig ()) {
190  DEBUG_ERROR ("Error writting MQTT config to filesystem.");
191  } else {
192  DEBUG_INFO ("Configuration stored");
193  }
194  } else {
195  DEBUG_DBG ("Configuration does not need to be saved");
196  }
197 
198  delete (mqttServerParam);
199  delete (mqttPortParam);
200  delete (mqttUserParam);
201  delete (mqttPassParam);
202 }
203 
205 
206 #ifdef SECURE_MQTT
207  randomSeed (micros ());
208 #ifdef ESP32
209  espClient.setCACert (DSTroot_CA);
210 #elif defined(ESP8266)
211  espClient.setTrustAnchors (&certificate);
212 #endif // ESP32
213  DEBUG_INFO ("CA store set");
214 #endif // SECURE_MQTT
216  DEBUG_INFO ("Set MQTT server %s - port %d", mqttgw_config.mqtt_server, mqttgw_config.mqtt_port);
217 
219 
220 #ifdef ESP32
221  uint64_t chipid = ESP.getEfuseMac ();
222  clientId = netName + String ((uint32_t)chipid, HEX);
223 #elif defined(ESP8266)
224  clientId = netName + String (ESP.getChipId (), HEX);
225 #endif // ESP32
227  reconnect ();
228  return true;
229 }
230 
232  // Loop until we're reconnected
233  while (!mqtt_client.connected ()) {
234  // TODO: startConnectionFlash (500);
235  DEBUG_INFO ("Attempting MQTT connection...");
236  // Create a random client ID
237  // Attempt to connect
238 #ifdef SECURE_MQTT
239  setClock ();
240 #endif
241  DEBUG_DBG ("Clock set.");
242  DEBUG_DBG ("Connect to MQTT server: user %s, pass %s, topic %s",
244  //client.setServer (mqttgw_config.mqtt_server, mqttgw_config.mqtt_port);
245  if (mqtt_client.connect (clientId.c_str (), mqttgw_config.mqtt_user, mqttgw_config.mqtt_pass, gwTopic.c_str (), 0, true, "0", true)) {
246  DEBUG_WARN ("MQTT connected");
247  // Once connected, publish an announcement...
248  publishMQTT (gwTopic.c_str (), "1", 1, true);
249  // ... and resubscribe
250  String dlTopic = netName + String ("/+/set/#");
251  mqtt_client.subscribe (dlTopic.c_str ());
252  dlTopic = netName + String ("/+/get/#");
253  mqtt_client.subscribe (dlTopic.c_str ());
254  mqtt_client.setCallback (onDlData);
255  // TODO: stopConnectionFlash ();
256  } else {
257  mqtt_client.disconnect ();
258  DEBUG_ERROR ("failed, rc=%d try again in 5 seconds", mqtt_client.state ());
259 #ifdef SECURE_MQTT
260  char error[100];
261 #ifdef ESP8266
262  int errorCode = espClient.getLastSSLError (error, 100);
263 #elif defined ESP32
264  int errorCode = espClient.lastError (error, 100);
265 #endif
266  DEBUG_ERROR ("Connect error %d: %s", errorCode, error);
267 #endif
268  // Wait 5 seconds before retrying
269 #ifdef ESP32
270  const TickType_t xDelay = 5000 / portTICK_PERIOD_MS;
271  vTaskDelay (xDelay);
272 #else
273  delay (5000);
274 #endif
275  }
276  }
277 }
278 
279 char* getTopicAddress (char* topic, unsigned int& len) {
280  if (!topic)
281  return NULL;
282 
283  char* start = strchr (topic, '/') + 1;
284  char* end;
285 
286  if (start) {
287  end = strchr (start, '/');
288  } else {
289  return NULL;
290  }
291  //DEBUG_INFO ("Start %p : %d", start, start - topic);
292  //DEBUG_INFO ("End %p : %d", end, end - topic);
293  if (end) {
294  len = end - start;
295  } else {
296  len = strlen (topic) - (start - topic);
297  }
298 
299  return start;
300 }
301 
303  if (data == GET_VERSION) {
305  } else if (data == GET_SLEEP) {
307  } else if (data == SET_SLEEP) {
309  } else if (data == SET_OTA) {
311  } else if (data == SET_IDENTIFY) {
312  DEBUG_WARN ("IDENTIFY MESSAGE %s", data.c_str ());
314  } else if (data == SET_RESET_CONFIG) {
315  DEBUG_WARN ("RESET CONFIG MESSAGE %s", data.c_str ());
317  } else if (data == GET_RSSI) {
318  DEBUG_INFO ("GET RSSI MESSAGE %s", data.c_str ());
320  } else if (data == SET_USER_DATA) {
321  DEBUG_INFO ("USER DATA %s", data.c_str ());
323  } else if (data == GET_USER_DATA) {
324  DEBUG_INFO ("USER DATA %s", data.c_str ());
326  } else if (data == GET_NAME) {
327  DEBUG_INFO ("GET NODE NAME AND ADDRESS");
329  } else if (data == SET_NAME) {
330  DEBUG_INFO ("SET NODE NAME %", data.c_str ());
332  } else
334 }
335 
336 control_message_type_t getTopicType (char* topic, char*& userCommand) {
337  if (!topic)
339 
340  String command;
341 
342  //Discard address
343  char* start = strchr (topic, '/') + 1;
344  if (start)
345  start = strchr (start, '/') + 1;
346  else
348  //DEBUG_INFO ("Second Start %p", start);
349  if ((int)start > 0x01) { // TODO: Why this condition ????
350  command = String (start);
351  userCommand = start;
352  } else {
354  }
355  //DEBUG_INFO ("Start %p : %d", start, start - topic);
356  //DEBUG_INFO ("Command %s", command.c_str());
357 
358  control_message_type_t msgType = checkMsgType (command);
359 
360  return msgType;
361 }
362 
363 
364 void GwOutput_MQTT::onDlData (char* topic, uint8_t* data, unsigned int len) {
365  uint8_t addr[ENIGMAIOT_ADDR_LEN];
366  char* addressStr;
367  control_message_type_t msgType;
368  char* userCommand;
369  char* nodeName = NULL;
370 
371 
372  DEBUG_DBG ("Topic %s", topic);
373 
374  unsigned int addressLen;
375 
376  addressStr = getTopicAddress (topic, addressLen);
377 
378  if (addressStr) {
379  //DEBUG_INFO ("Len: %u", addressLen);
380  DEBUG_DBG ("Address %.*s", addressLen, addressStr);
381  if (!str2mac (addressStr, addr)) {
382  DEBUG_INFO ("Not a mac address. Treating it as a node name");
383  if (addressLen) {
384  nodeName = (char*)calloc (addressLen + 1, sizeof (char));
385  memcpy (nodeName, addressStr, addressLen);
386  } else {
387  DEBUG_WARN ("Invalid address");
388  return;
389  }
390  } else {
391  DEBUG_DBG ("Hex Address = %s", printHexBuffer (addr, 6));
392  }
393  } else
394  return;
395 
396  msgType = getTopicType (topic, userCommand);
397 
398 
399  DEBUG_DBG ("User command: %s", userCommand);
400  DEBUG_DBG ("MsgType 0x%02X", msgType);
401  DEBUG_DBG ("Data: %.*s\n", len, data);
402 
403  if (msgType != control_message_type_t::INVALID)
404  GwOutput.downlinkCb (addr, nodeName, msgType, (char*)data, len);
405  else
406  DEBUG_WARN ("Invalid message");
407 
408  if (nodeName) {
409  free (nodeName);
410  nodeName = NULL;
411  }
412 }
413 
415  mqtt_queue_item_t* message;
416 
417  mqtt_client.loop ();
418  if (!mqtt_client.connected ()) {
419  reconnect ();
420  } else {
421  if (!mqtt_queue.empty ()) {
422  message = getMQTTqueue ();
423  if (publishMQTT (message->topic, message->payload, message->payload_len, message->retain)) {
424  DEBUG_DBG ("MQTT published. %s %.*s", message->topic, message->payload_len, message->payload);
425  popMQTTqueue ();
426  }
427  }
428  }
429 }
430 
431 bool GwOutput_MQTT::publishMQTT (const char* topic, const char* payload, size_t len, bool retain) {
432  DEBUG_INFO ("Publish MQTT. %s : %.*s", topic, len, payload);
433  if (mqtt_client.connected ()) {
434  return mqtt_client.publish (topic, (uint8_t*)payload, len, retain);
435  } else {
436  DEBUG_WARN ("MQTT client not connected");
437  return false;
438  }
439 }
440 
441 #ifdef SECURE_MQTT
442 void GwOutput_MQTT::setClock () {
443  configTime (1 * 3600, 0, "pool.ntp.org", "time.nist.gov");
444 #if DEBUG_LEVEL >= INFO
445  DEBUG_INFO ("\nWaiting for NTP time sync: ");
446  time_t now = time (nullptr);
447  while (now < 8 * 3600 * 2) {
448  delay (500);
449  Serial.print (".");
450  now = time (nullptr);
451  }
452  //Serial.println ("");
453  struct tm timeinfo;
454  gmtime_r (&now, &timeinfo);
455  DEBUG_INFO ("Current time: %s", asctime (&timeinfo));
456 #endif
457 }
458 #endif
459 
460 bool GwOutput_MQTT::addMQTTqueue (const char* topic, char* payload, size_t len, bool retain) {
461  mqtt_queue_item_t* message = new mqtt_queue_item_t;
462 
463  if (mqtt_queue.size () >= MAX_MQTT_QUEUE_SIZE) {
464  mqtt_queue.pop ();
465  }
466 
467  message->topic = (char*)malloc (strlen (topic) + 1);
468  strcpy (message->topic, topic);
469  message->payload_len = len;
470  message->payload = (char*)malloc (len);
471  memcpy (message->payload, payload, len);
472  message->retain = retain;
473 
474  mqtt_queue.push (message);
475  DEBUG_DBG ("%d MQTT messages queued Len:%d %s %.*s", mqtt_queue.size (),
476  len,
477  message->topic,
478  message->payload_len, message->payload);
479 
480  return true;
481 }
482 
484  if (mqtt_queue.size ()) {
485  DEBUG_DBG ("MQTT message got from queue");
486  return mqtt_queue.front ();
487  }
488  return NULL;
489 }
490 
492  mqtt_queue_item_t* message;
493 
494  if (mqtt_queue.size ()) {
495  message = mqtt_queue.front ();
496  if (message) {
497  if (message->topic) {
498  delete(message->topic);
499  }
500  if (message->payload) {
501  delete(message->payload);
502  }
503  delete message;
504  }
505  mqtt_queue.pop ();
506  DEBUG_DBG ("MQTT message pop. Size %d", mqtt_queue.size ());
507  }
508 }
509 
510 bool GwOutput_MQTT::outputDataSend (char* address, char* data, size_t length, GwOutput_data_type_t type) {
511  const int TOPIC_SIZE = 64;
512  char topic[TOPIC_SIZE];
513  bool result;
514  switch (type) {
516  snprintf (topic, TOPIC_SIZE, "%s/%s/%s", netName.c_str (), address, NODE_DATA);
517  break;
519  snprintf (topic, TOPIC_SIZE, "%s/%s/%s", netName.c_str (), address, LOST_MESSAGES);
520  break;
522  snprintf (topic, TOPIC_SIZE, "%s/%s/%s", netName.c_str (), address, NODE_STATUS);
523  break;
524  }
525  if ((result = addMQTTqueue (topic, data, length))) {
526  DEBUG_INFO ("MQTT queued %s. Length %d", topic, length);
527  } else {
528  DEBUG_WARN ("Error queuing MQTT %s", topic);
529  }
530  return result;
531 }
532 
533 bool GwOutput_MQTT::outputControlSend (char* address, uint8_t* data, size_t length) {
534  const int TOPIC_SIZE = 64;
535  const int PAYLOAD_SIZE = 512;
536  char topic[TOPIC_SIZE];
537  char payload[PAYLOAD_SIZE];
538  size_t pld_size = 0;
539  bool result = false;
540 
541  switch (data[0]) {
543  snprintf (topic, TOPIC_SIZE, "%s/%s/%s", netName.c_str (), address, GET_VERSION_ANS);
544  pld_size = snprintf (payload, PAYLOAD_SIZE, "{\"version\":\"%.*s\"}", length - 1, data + 1);
545  if (addMQTTqueue (topic, payload, pld_size)) {
546  DEBUG_INFO ("Published MQTT %s %s", topic, payload);
547  result = true;
548  }
549  break;
551  uint32_t sleepTime;
552  memcpy (&sleepTime, data + 1, sizeof (sleepTime));
553  snprintf (topic, TOPIC_SIZE, "%s/%s/%s", netName.c_str (), address, GET_SLEEP_ANS);
554  pld_size = snprintf (payload, PAYLOAD_SIZE, "{\"sleeptime\":%d}", sleepTime);
555  if (addMQTTqueue (topic, payload, pld_size)) {
556  DEBUG_INFO ("Published MQTT %s %s", topic, payload);
557  result = true;
558  }
559  break;
561  snprintf (topic, TOPIC_SIZE, "%s/%s/%s", netName.c_str (), address, SET_RESET_ANS);
562  pld_size = snprintf (payload, PAYLOAD_SIZE, "{}");
563  if (addMQTTqueue (topic, payload, pld_size)) {
564  DEBUG_INFO ("Published MQTT %s %s", topic, payload);
565  result = true;
566  }
567  break;
569  snprintf (topic, TOPIC_SIZE, "%s/%s/%s", netName.c_str (), address, GET_RSSI_ANS);
570  pld_size = snprintf (payload, PAYLOAD_SIZE, "{\"rssi\":%d,\"channel\":%u}", (int8_t)data[1], data[2]);
571  if (addMQTTqueue (topic, payload, pld_size)) {
572  DEBUG_INFO ("Published MQTT %s %s", topic, payload);
573  result = true;
574  }
575  break;
577  snprintf (topic, TOPIC_SIZE, "%s/%s/%s", netName.c_str (), address, GET_NAME_ANS);
578  char addrStr[ENIGMAIOT_ADDR_LEN * 3];
579  pld_size = snprintf (payload, PAYLOAD_SIZE, "{\"name\":\"%.*s\",\"address\":\"%s\"}", length - ENIGMAIOT_ADDR_LEN - 1, (char*)(data + 1 + ENIGMAIOT_ADDR_LEN), mac2str (data + 1, addrStr));
580  if (addMQTTqueue (topic, payload, pld_size)) {
581  DEBUG_INFO ("Published MQTT %s %s", topic, payload);
582  result = true;
583  }
584  break;
586  snprintf (topic, TOPIC_SIZE, "%s/%s/%s", netName.c_str (), address, SET_OTA_ANS);
587  switch (data[1]) {
589  pld_size = snprintf (payload, PAYLOAD_SIZE, "{\"result\":\"OTA Started\",\"status\":%u}\n", data[1]);
590  break;
592  pld_size = snprintf (payload, PAYLOAD_SIZE, "{\"result\":\"OTA Start error\",\"status\":%u}\n", data[1]);
593  break;
595  uint16_t lastGoodIdx;
596  memcpy ((uint8_t*)&lastGoodIdx, data + 2, sizeof (uint16_t));
597  pld_size = snprintf (payload, PAYLOAD_SIZE, "{\"last_chunk\":%d,\"result\":\"OTA out of sequence error\",\"status\":%u}\n", lastGoodIdx, data[1]);
598  break;
600  pld_size = snprintf (payload, PAYLOAD_SIZE, "{\"result\":\"OTA check OK\",\"status\":%u}\n", data[1]);
601  break;
603  pld_size = snprintf (payload, PAYLOAD_SIZE, "{\"result\":\"OTA check failed\",\"status\":%u}\n", data[1]);
604  break;
606  pld_size = snprintf (payload, PAYLOAD_SIZE, "{\"result\":\"OTA timeout\",\"status\":%u}\n", data[1]);
607  break;
609  pld_size = snprintf (payload, PAYLOAD_SIZE, "{\"result\":\"OTA finished OK\",\"status\":%u}\n", data[1]);
610  break;
611  }
612  if (addMQTTqueue (topic, payload, pld_size)) {
613  DEBUG_INFO ("Published MQTT %s %s", topic, payload);
614  result = true;
615  }
616  break;
617  default:
618  DEBUG_WARN ("Unknown control message. Code: 0x%02X", data[0]);
619  }
620 
621  return result;
622 }
623 
624 bool GwOutput_MQTT::newNodeSend (char* address, uint16_t node_id) {
625  const int TOPIC_SIZE = 64;
626 
627  char topic[TOPIC_SIZE];
628 
629  uint8_t* nodeAddress = enigmaIotGateway->getNodes ()->getNodeFromID (node_id)->getMacAddress ();
630  char addrStr[ENIGMAIOT_ADDR_LEN * 3];
631 
632  char payload[ENIGMAIOT_ADDR_LEN * 3 + 14];
633 
634  snprintf (payload, ENIGMAIOT_ADDR_LEN * 3 + 14, "{\"address\":\"%s\"}", mac2str (nodeAddress, addrStr));
635 
636  snprintf (topic, TOPIC_SIZE, "%s/%s/hello", netName.c_str (), address);
637  bool result = addMQTTqueue (topic, payload, ENIGMAIOT_ADDR_LEN * 3 + 14);
638  DEBUG_INFO ("Published MQTT %s", topic);
639  return result;
640 }
641 
643  const int TOPIC_SIZE = 64;
644  const int PAYLOAD_SIZE = 64;
645 
646  char topic[TOPIC_SIZE];
647  char payload[PAYLOAD_SIZE];
648  size_t pld_size;
649 
650  snprintf (topic, TOPIC_SIZE, "%s/%s/bye", netName.c_str (), address);
651  pld_size = snprintf (payload, PAYLOAD_SIZE, "{\"reason\":%u}", reason);
652  bool result = addMQTTqueue (topic, payload, pld_size);
653  DEBUG_INFO ("Published MQTT %s result = %s", topic, result ? "OK" : "Fail");
654  return result;
655 }
OTA_CHECK_OK
@ OTA_CHECK_OK
Definition: NodeList.h:66
mqtt_queue_item_t::payload
char * payload
Definition: GwOutput_mqtt.h:71
GwOutput_MQTT::nodeDisconnectedSend
bool nodeDisconnectedSend(char *address, gwInvalidateReason_t reason)
Send node disconnection notification.
Definition: GwOutput_mqtt.cpp:642
VERSION_ANS
@ VERSION_ANS
Definition: NodeList.h:43
IDENTIFY
@ IDENTIFY
Definition: NodeList.h:47
getTopicType
control_message_type_t getTopicType(char *topic, char *&userCommand)
Definition: GwOutput_mqtt.cpp:336
GET_VERSION_ANS
#define GET_VERSION_ANS
Definition: GwOutput_mqtt.h:35
GwOutput_MQTT::configManagerStart
void configManagerStart(EnigmaIOTGatewayClass *enigmaIotGw)
Called when wifi manager starts config portal.
Definition: GwOutput_mqtt.cpp:44
GwOutput_MQTT::mqttPassParam
AsyncWiFiManagerParameter * mqttPassParam
Configuration field for MQTT server password.
Definition: GwOutput_mqtt.h:82
mqtt_queue_item_t
Definition: GwOutput_mqtt.h:69
SLEEP_SET
@ SLEEP_SET
Definition: NodeList.h:45
GwOutput_MQTT::newNodeSend
bool newNodeSend(char *address, uint16_t node_id)
Send new node notification.
Definition: GwOutput_mqtt.cpp:624
GET_SLEEP
#define GET_SLEEP
Definition: GwOutput_mqtt.h:36
USERDATA_SET
@ USERDATA_SET
Definition: NodeList.h:58
SET_OTA
#define SET_OTA
Definition: GwOutput_mqtt.h:39
SET_OTA_ANS
#define SET_OTA_ANS
Definition: GwOutput_mqtt.h:40
EnigmaIOTGatewayClass
Main gateway class. Manages communication with nodes and sends data to upper layer.
Definition: EnigmaIOTGateway.h:204
GwOutput_MQTT::reconnect
void reconnect()
This is called anytime MQTT client is disconnected.
Definition: GwOutput_mqtt.cpp:231
GwOutput_MQTT::onDlData
static void onDlData(char *topic, uint8_t *data, unsigned int len)
Function that processes downlink data from network to node.
Definition: GwOutput_mqtt.cpp:364
GET_RSSI
#define GET_RSSI
Definition: GwOutput_mqtt.h:44
GwOutput_MQTT::mqttPortParam
AsyncWiFiManagerParameter * mqttPortParam
Configuration field for MQTT server port.
Definition: GwOutput_mqtt.h:80
GwOutput_MQTT::begin
bool begin()
Starts output module.
Definition: GwOutput_mqtt.cpp:204
GwOutput_MQTT::popMQTTqueue
void popMQTTqueue()
Deletes next item in the queue.
Definition: GwOutput_mqtt.cpp:491
str2mac
uint8_t * str2mac(const char *macAddrString, uint8_t *macBytes)
Debug helper function that creates MAC address byte array from text representation.
Definition: helperFunctions.cpp:94
SET_NAME
#define SET_NAME
Definition: GwOutput_mqtt.h:47
SET_SLEEP
#define SET_SLEEP
Definition: GwOutput_mqtt.h:38
GwOutput_MQTT::mqttServerParam
AsyncWiFiManagerParameter * mqttServerParam
Configuration field for MQTT server address.
Definition: GwOutput_mqtt.h:79
mqttgw_config_t::mqtt_server
char mqtt_server[41]
Definition: GwOutput_mqtt.h:59
ENIGMAIOT_ADDR_LEN
static const size_t ENIGMAIOT_ADDR_LEN
Address size. Mac address = 6 bytes.
Definition: EnigmaIoTconfig.h:14
EnigmaIOTGatewayClass::getNetworkName
char * getNetworkName()
Gets EnigmaIOT network name.
Definition: EnigmaIOTGateway.h:400
OTA
@ OTA
Definition: NodeList.h:55
NODE_DATA
#define NODE_DATA
Definition: GwOutput_mqtt.h:51
GatewayOutput_generic::downlinkCb
onDlData_t downlinkCb
downlink processing function handle
Definition: GwOutput_generic.h:34
RESET
@ RESET
Definition: NodeList.h:48
printHexBuffer
char * printHexBuffer(const uint8_t *buffer, uint16_t len)
Debug helper function that generates a string that represent a buffer hexadecimal values.
Definition: helperFunctions.cpp:17
OTA_TIMEOUT
@ OTA_TIMEOUT
Definition: NodeList.h:69
GET_NAME
#define GET_NAME
Definition: GwOutput_mqtt.h:46
SET_RESET_CONFIG
#define SET_RESET_CONFIG
Definition: GwOutput_mqtt.h:42
RSSI_GET
@ RSSI_GET
Definition: NodeList.h:50
GwOutput_MQTT::loop
void loop()
Should be called regularly for module management.
Definition: GwOutput_mqtt.cpp:414
EnigmaIOTGatewayClass::addWiFiManagerParameter
void addWiFiManagerParameter(AsyncWiFiManagerParameter *p)
Adds a parameter to configuration portal.
Definition: EnigmaIOTGateway.h:419
INVALID
@ INVALID
Definition: NodeList.h:59
OTA_START_ERROR
@ OTA_START_ERROR
Definition: NodeList.h:65
OTA_FINISHED
@ OTA_FINISHED
Definition: NodeList.h:70
GET_USER_DATA
#define GET_USER_DATA
Definition: GwOutput_mqtt.h:50
mqtt_queue_item_t::retain
bool retain
Definition: GwOutput_mqtt.h:73
GW_STATUS
#define GW_STATUS
Definition: GwOutput_mqtt.h:54
RSSI_ANS
@ RSSI_ANS
Definition: NodeList.h:51
GET_SLEEP_ANS
#define GET_SLEEP_ANS
Definition: GwOutput_mqtt.h:37
GwOutput_MQTT::outputDataSend
bool outputDataSend(char *address, char *data, size_t length, GwOutput_data_type_t type=data)
Send data from nodes.
Definition: GwOutput_mqtt.cpp:510
SLEEP_GET
@ SLEEP_GET
Definition: NodeList.h:44
GET_RSSI_ANS
#define GET_RSSI_ANS
Definition: GwOutput_mqtt.h:45
GwOutput_MQTT::mqttgw_config
mqttgw_config_t mqttgw_config
MQTT server configuration data.
Definition: GwOutput_mqtt.h:86
mac2str
char * mac2str(const uint8_t *mac, char *buffer)
Debug helper function that generates a string that represent a MAC address.
Definition: helperFunctions.cpp:84
SET_IDENTIFY
#define SET_IDENTIFY
Definition: GwOutput_mqtt.h:41
USERDATA_GET
@ USERDATA_GET
Definition: NodeList.h:57
CONFIG_FILE
constexpr auto CONFIG_FILE
MQTT outout configuration file name.
Definition: GwOutput_mqtt.h:56
GET_NAME_ANS
#define GET_NAME_ANS
Definition: GwOutput_mqtt.h:48
EnigmaIOTGatewayClass::getShouldSave
bool getShouldSave()
Gets flag that indicates if configuration should be saved.
Definition: EnigmaIOTGateway.cpp:39
GwOutput_MQTT
Definition: GwOutput_mqtt.h:77
OTA_OUT_OF_SEQUENCE
@ OTA_OUT_OF_SEQUENCE
Definition: NodeList.h:68
NAME_ANS
@ NAME_ANS
Definition: NodeList.h:53
EnigmaIOTGatewayClass::getNodes
NodeList * getNodes()
Gets nodes data structure.
Definition: EnigmaIOTGateway.h:632
GwOutput
GwOutput_MQTT GwOutput
Definition: GwOutput_mqtt.cpp:42
EnigmaIOTGateway
EnigmaIOTGatewayClass EnigmaIOTGateway
Definition: EnigmaIOTGateway.cpp:1747
GwOutput_MQTT::configManagerExit
void configManagerExit(bool status)
Called when wifi manager exits config portal.
Definition: GwOutput_mqtt.cpp:167
GwOutput_MQTT::getMQTTqueue
mqtt_queue_item_t * getMQTTqueue()
Gets next item in the queue.
Definition: GwOutput_mqtt.cpp:483
mqtt_queue_item_t::topic
char * topic
Definition: GwOutput_mqtt.h:70
GwOutput_data_type_t
enum GwOutput_data_type GwOutput_data_type_t
GatewayOutput_generic::clientId
String clientId
MQTT clientId.
Definition: GwOutput_generic.h:44
GwOutput_MQTT::outputControlSend
bool outputControlSend(char *address, uint8_t *data, size_t length)
Send control data from nodes.
Definition: GwOutput_mqtt.cpp:533
GwOutput_mqtt.h
MQTT Gateway output module.
GatewayOutput_generic::gwTopic
String gwTopic
MQTT topic for gateway.
Definition: GwOutput_generic.h:45
mqttgw_config_t::mqtt_user
char mqtt_user[21]
Definition: GwOutput_mqtt.h:65
GwOutput_MQTT::mqttUserParam
AsyncWiFiManagerParameter * mqttUserParam
Configuration field for MQTT server user name.
Definition: GwOutput_mqtt.h:81
GwOutput_MQTT::espClient
WiFiClient espClient
TCP client.
Definition: GwOutput_mqtt.h:95
checkMsgType
control_message_type_t checkMsgType(String data)
Definition: GwOutput_mqtt.cpp:302
GatewayOutput_generic::enigmaIotGateway
EnigmaIOTGatewayClass * enigmaIotGateway
Pointer to EnigmaIOT gateway instance.
Definition: GwOutput_generic.h:33
RESET_ANS
@ RESET_ANS
Definition: NodeList.h:49
GET_VERSION
#define GET_VERSION
Definition: GwOutput_mqtt.h:34
Node::getMacAddress
uint8_t * getMacAddress()
Gets address from Node.
Definition: NodeList.h:112
VERSION
@ VERSION
Definition: NodeList.h:42
mqttgw_config_t::mqtt_pass
char mqtt_pass[41]
Definition: GwOutput_mqtt.h:66
OTA_ANS
@ OTA_ANS
Definition: NodeList.h:56
NAME_GET
@ NAME_GET
Definition: NodeList.h:52
GwOutput_MQTT::addMQTTqueue
bool addMQTTqueue(const char *topic, char *payload, size_t len, bool retain=false)
Add MQTT message to queue.
Definition: GwOutput_mqtt.cpp:460
data
@ data
Definition: GwOutput_generic.h:23
GatewayOutput_generic::netName
String netName
EnigmaIOT network name.
Definition: GwOutput_generic.h:43
lostmessages
@ lostmessages
Definition: GwOutput_generic.h:24
mqttgw_config_t::mqtt_port
int mqtt_port
Definition: GwOutput_mqtt.h:63
mqtt_queue_item_t::payload_len
size_t payload_len
Definition: GwOutput_mqtt.h:72
GwOutput_MQTT::mqtt_client
PubSubClient mqtt_client
MQTT client.
Definition: GwOutput_mqtt.h:97
GwOutput_MQTT::saveConfig
bool saveConfig()
Saves output module configuration.
Definition: GwOutput_mqtt.cpp:60
helperFunctions.h
Auxiliary function definition.
NodeList::getNodeFromID
Node * getNodeFromID(uint16_t nodeId)
Gets node that correspond with given nodeId.
Definition: NodeList.cpp:126
OTA_CHECK_FAIL
@ OTA_CHECK_FAIL
Definition: NodeList.h:67
MAX_MQTT_QUEUE_SIZE
static const size_t MAX_MQTT_QUEUE_SIZE
Maximum number of MQTT messages to be sent.
Definition: EnigmaIoTconfig.h:26
NODE_STATUS
#define NODE_STATUS
Definition: GwOutput_mqtt.h:53
gwInvalidateReason_t
gwInvalidateReason_t
Key invalidation reason definition.
Definition: EnigmaIOTGateway.h:64
getTopicAddress
char * getTopicAddress(char *topic, unsigned int &len)
Definition: GwOutput_mqtt.cpp:279
control_message_type_t
enum control_message_type control_message_type_t
NAME_SET
@ NAME_SET
Definition: NodeList.h:54
GwOutput_MQTT::mqtt_queue
std::queue< mqtt_queue_item_t * > mqtt_queue
Output MQTT messages queue. It acts as a FIFO queue.
Definition: GwOutput_mqtt.h:84
GwOutput_MQTT::loadConfig
bool loadConfig()
Loads output module configuration.
Definition: GwOutput_mqtt.cpp:102
SLEEP_ANS
@ SLEEP_ANS
Definition: NodeList.h:46
status
@ status
Definition: GwOutput_generic.h:25
LOST_MESSAGES
#define LOST_MESSAGES
Definition: GwOutput_mqtt.h:52
SET_USER_DATA
#define SET_USER_DATA
Definition: GwOutput_mqtt.h:49
OTA_STARTED
@ OTA_STARTED
Definition: NodeList.h:64
debug.h
Auxiliary functions for debugging over Serial.
GwOutput_MQTT::publishMQTT
bool publishMQTT(const char *topic, const char *payload, size_t len, bool retain=false)
Publishes data over MQTT.
Definition: GwOutput_mqtt.cpp:431
SET_RESET_ANS
#define SET_RESET_ANS
Definition: GwOutput_mqtt.h:43