Fork of the espurna firmware for `mhsw` switches
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

222 lines
6.7 KiB

  1. /*
  2. RFM69 MODULE
  3. Copyright (C) 2016-2017 by Xose Pérez <xose dot perez at gmail dot com>
  4. */
  5. #if RFM69_SUPPORT
  6. #include "RFM69Manager.h"
  7. // -----------------------------------------------------------------------------
  8. // Locals
  9. // -----------------------------------------------------------------------------
  10. RFM69Manager * _rfm69_radio;
  11. struct _node_t {
  12. unsigned long count = 0;
  13. unsigned long missing = 0;
  14. unsigned long duplicates = 0;
  15. unsigned char lastPacketID = 0;
  16. };
  17. _node_t _rfm69_node_info[255];
  18. unsigned char _rfm69_node_count;
  19. unsigned long _rfm69_packet_count;
  20. // -----------------------------------------------------------------------------
  21. // WEB
  22. // -----------------------------------------------------------------------------
  23. #if WEB_SUPPORT
  24. void _rfm69WebSocketOnSend(JsonObject& root) {
  25. root["rfm69Visible"] = 1;
  26. root["rfm69Topic"] = getSetting("rfm69Topic", RFM69_DEFAULT_TOPIC);
  27. root["packetCount"] = _rfm69_packet_count;
  28. root["nodeCount"] = _rfm69_node_count;
  29. JsonArray& mappings = root.createNestedArray("mapping");
  30. for (unsigned char i=0; i<RFM69_MAX_TOPICS; i++) {
  31. unsigned char node = getSetting("node", i, 0).toInt();
  32. if (0 == node) break;
  33. JsonObject& mapping = mappings.createNestedObject();
  34. mapping["node"] = node;
  35. mapping["key"] = getSetting("key", i, "");
  36. mapping["topic"] = getSetting("topic", i, "");
  37. }
  38. }
  39. bool _rfm69WebSocketOnReceive(const char * key, JsonVariant& value) {
  40. if (strncmp(key, "rfm69", 5) == 0) return true;
  41. if (strncmp(key, "node", 4) == 0) return true;
  42. if (strncmp(key, "key", 3) == 0) return true;
  43. if (strncmp(key, "topic", 5) == 0) return true;
  44. return false;
  45. }
  46. void _rfm69WebSocketOnAction(uint32_t client_id, const char * action, JsonObject& data) {
  47. if (strcmp(action, "clear-counts") == 0) _rfm69Clear();
  48. }
  49. #endif // WEB_SUPPORT
  50. void _rfm69CleanNodes(unsigned char num) {
  51. // Look for the last defined node
  52. int i = 0;
  53. while (i < num) {
  54. if (getSetting("node", i, 0).toInt() == 0) break;
  55. if (getSetting("key", i, "").length() == 0) break;
  56. if (getSetting("topic", i, "").length() == 0) break;
  57. ++i;
  58. }
  59. // Delete all other settings
  60. while (i < WIFI_MAX_NETWORKS) {
  61. delSetting("node", i);
  62. delSetting("key", i);
  63. delSetting("topic", i);
  64. ++i;
  65. }
  66. }
  67. void _rfm69Configure() {
  68. _rfm69CleanNodes(RFM69_MAX_TOPICS);
  69. }
  70. // -----------------------------------------------------------------------------
  71. // Radio
  72. // -----------------------------------------------------------------------------
  73. void _rfm69Debug(const char * level, packet_t * data) {
  74. DEBUG_MSG_P(
  75. PSTR("[RFM69] %s: messageID:%05d senderID:%03d targetID:%03d packetID:%03d rssi:%-04d name:%s value:%s\n"),
  76. level,
  77. data->messageID,
  78. data->senderID,
  79. data->targetID,
  80. data->packetID,
  81. data->rssi,
  82. data->name,
  83. data->value
  84. );
  85. }
  86. void _rfm69Process(packet_t * data) {
  87. // Count seen nodes and packets
  88. if (_rfm69_node_info[data->senderID].count == 0) ++_rfm69_node_count;
  89. ++_rfm69_packet_count;
  90. // Detect duplicates and missing packets
  91. // packetID==0 means device is not sending packetID info
  92. if (data->packetID > 0) {
  93. if (_rfm69_node_info[data->senderID].count > 0) {
  94. unsigned char gap = data->packetID - _rfm69_node_info[data->senderID].lastPacketID;
  95. if (gap == 0) {
  96. _rfm69_node_info[data->senderID].duplicates = _rfm69_node_info[data->senderID].duplicates + 1;
  97. //_rfm69Debug("DUP", data);
  98. return;
  99. }
  100. if ((gap > 1) && (data->packetID > 1)) {
  101. _rfm69_node_info[data->senderID].missing = _rfm69_node_info[data->senderID].missing + gap - 1;
  102. DEBUG_MSG_P(PSTR("[RFM69] %u missing packets detected\n"), gap - 1);
  103. }
  104. }
  105. }
  106. _rfm69Debug("OK ", data);
  107. _rfm69_node_info[data->senderID].lastPacketID = data->packetID;
  108. _rfm69_node_info[data->senderID].count = _rfm69_node_info[data->senderID].count + 1;
  109. // Send info to websocket clients
  110. {
  111. char buffer[200];
  112. snprintf_P(
  113. buffer,
  114. sizeof(buffer) - 1,
  115. PSTR("{\"nodeCount\": %d, \"packetCount\": %lu, \"packet\": {\"senderID\": %u, \"targetID\": %u, \"packetID\": %u, \"name\": \"%s\", \"value\": \"%s\", \"rssi\": %d, \"duplicates\": %d, \"missing\": %d}}"),
  116. _rfm69_node_count, _rfm69_packet_count,
  117. data->senderID, data->targetID, data->packetID, data->name, data->value, data->rssi,
  118. _rfm69_node_info[data->senderID].duplicates , _rfm69_node_info[data->senderID].missing);
  119. wsSend(buffer);
  120. }
  121. // If we are the target of the message, forward it via MQTT, otherwise quit
  122. if (!RFM69_PROMISCUOUS_SENDS && (RFM69_GATEWAY_ID != data->targetID)) return;
  123. // Try to find a matching mapping
  124. for (unsigned int i=0; i<RFM69_MAX_TOPICS; i++) {
  125. unsigned char node = getSetting("node", i, 0).toInt();
  126. if (0 == node) break;
  127. if ((node == data->senderID) && (getSetting("key", i, "").equals(data->name))) {
  128. mqttSendRaw((char *) getSetting("topic", i, "").c_str(), (char *) String(data->value).c_str());
  129. return;
  130. }
  131. }
  132. // Mapping not found, use default topic
  133. String topic = getSetting("rfm69Topic", RFM69_DEFAULT_TOPIC);
  134. if (topic.length() > 0) {
  135. topic.replace("{node}", String(data->senderID));
  136. topic.replace("{key}", String(data->name));
  137. mqttSendRaw((char *) topic.c_str(), (char *) String(data->value).c_str());
  138. }
  139. }
  140. void _rfm69Loop() {
  141. _rfm69_radio->loop();
  142. }
  143. void _rfm69Clear() {
  144. for(unsigned int i=0; i<255; i++) {
  145. _rfm69_node_info[i].duplicates = 0;
  146. _rfm69_node_info[i].missing = 0;
  147. }
  148. _rfm69_node_count = 0;
  149. _rfm69_packet_count = 0;
  150. }
  151. // -----------------------------------------------------------------------------
  152. // RFM69
  153. // -----------------------------------------------------------------------------
  154. void rfm69Setup() {
  155. delay(10);
  156. _rfm69Configure();
  157. _rfm69_radio = new RFM69Manager(RFM69_CS_PIN, RFM69_IRQ_PIN, RFM69_IS_RFM69HW, digitalPinToInterrupt(RFM69_IRQ_PIN));
  158. _rfm69_radio->initialize(RFM69_FREQUENCY, RFM69_NODE_ID, RFM69_NETWORK_ID, RFM69_ENCRYPTKEY);
  159. _rfm69_radio->promiscuous(RFM69_PROMISCUOUS);
  160. _rfm69_radio->onMessage(_rfm69Process);
  161. #if WEB_SUPPORT
  162. wsOnSendRegister(_rfm69WebSocketOnSend);
  163. wsOnReceiveRegister(_rfm69WebSocketOnReceive);
  164. wsOnAfterParseRegister(_rfm69Configure);
  165. wsOnActionRegister(_rfm69WebSocketOnAction);
  166. #endif
  167. // Register loop
  168. espurnaRegisterLoop(_rfm69Loop);
  169. }
  170. #endif // RFM69_SUPPORT