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.

288 lines
8.7 KiB

  1. /*
  2. RFM69 MODULE
  3. Copyright (C) 2016-2017 by Xose Pérez <xose dot perez at gmail dot com>
  4. Module key prefix: rfm69 (node, key, topic)
  5. */
  6. #if RFM69_SUPPORT
  7. #include "libs/RFM69Wrap.h"
  8. #define RFM69_PACKET_SEPARATOR ':'
  9. // -----------------------------------------------------------------------------
  10. // Locals
  11. // -----------------------------------------------------------------------------
  12. RFM69Wrap * _rfm69_radio;
  13. struct _node_t {
  14. unsigned long count = 0;
  15. unsigned long missing = 0;
  16. unsigned long duplicates = 0;
  17. unsigned char lastPacketID = 0;
  18. };
  19. _node_t _rfm69_node_info[RFM69_MAX_NODES];
  20. unsigned char _rfm69_node_count;
  21. unsigned long _rfm69_packet_count;
  22. // -----------------------------------------------------------------------------
  23. // WEB
  24. // -----------------------------------------------------------------------------
  25. #if WEB_SUPPORT
  26. void _rfm69WebSocketOnSend(JsonObject& root) {
  27. root["rfm69Visible"] = 1;
  28. root["rfm69Topic"] = getSetting("rfm69Topic", RFM69_DEFAULT_TOPIC);
  29. root["packetCount"] = _rfm69_packet_count;
  30. root["nodeCount"] = _rfm69_node_count;
  31. JsonArray& mappings = root.createNestedArray("mapping");
  32. for (unsigned char i=0; i<RFM69_MAX_TOPICS; i++) {
  33. unsigned char node = getSetting("node", i, 0).toInt();
  34. if (0 == node) break;
  35. JsonObject& mapping = mappings.createNestedObject();
  36. mapping["node"] = node;
  37. mapping["key"] = getSetting("key", i, "");
  38. mapping["topic"] = getSetting("topic", i, "");
  39. }
  40. }
  41. void _rfm69WebSocketOnAction(uint32_t client_id, const char * action, JsonObject& data) {
  42. if (strcmp(action, "clear-counts") == 0) _rfm69Clear();
  43. }
  44. #endif // WEB_SUPPORT
  45. bool _rfm69KeyCheck(const char * key) {
  46. if (strncmp(key, "rfm69", 5) == 0) return true;
  47. if (strncmp(key, "node" , 4) == 0) return true;
  48. if (strncmp(key, "key" , 3) == 0) return true;
  49. if (strncmp(key, "topic", 5) == 0) return true;
  50. return false;
  51. }
  52. void _rfm69CleanNodes(unsigned char num) {
  53. // Look for the last defined node
  54. int i = 0;
  55. while (i < num) {
  56. if (getSetting("node", i, 0).toInt() == 0) break;
  57. if (getSetting("key", i, "").length() == 0) break;
  58. if (getSetting("topic", i, "").length() == 0) break;
  59. ++i;
  60. }
  61. // Delete all other settings
  62. while (i < WIFI_MAX_NETWORKS) {
  63. delSetting("node", i);
  64. delSetting("key", i);
  65. delSetting("topic", i);
  66. ++i;
  67. }
  68. }
  69. void _rfm69Configure() {
  70. _rfm69CleanNodes(RFM69_MAX_TOPICS);
  71. }
  72. // -----------------------------------------------------------------------------
  73. // Radio
  74. // -----------------------------------------------------------------------------
  75. void _rfm69Debug(const char * level, packet_t * data) {
  76. DEBUG_MSG_P(
  77. PSTR("[RFM69] %s: messageID:%05d senderID:%03d targetID:%03d packetID:%03d rssi:%-04d key:%s value:%s\n"),
  78. level,
  79. data->messageID,
  80. data->senderID,
  81. data->targetID,
  82. data->packetID,
  83. data->rssi,
  84. data->key,
  85. data->value
  86. );
  87. }
  88. void _rfm69Process(packet_t * data) {
  89. // Is node beyond RFM69_MAX_NODES?
  90. if (data->senderID >= RFM69_MAX_NODES) return;
  91. // Count seen nodes and packets
  92. if (_rfm69_node_info[data->senderID].count == 0) ++_rfm69_node_count;
  93. ++_rfm69_packet_count;
  94. // Detect duplicates and missing packets
  95. // packetID==0 means device is not sending packetID info
  96. if (data->packetID > 0) {
  97. if (_rfm69_node_info[data->senderID].count > 0) {
  98. unsigned char gap = data->packetID - _rfm69_node_info[data->senderID].lastPacketID;
  99. if (gap == 0) {
  100. _rfm69_node_info[data->senderID].duplicates = _rfm69_node_info[data->senderID].duplicates + 1;
  101. //_rfm69Debug("DUP", data);
  102. return;
  103. }
  104. if ((gap > 1) && (data->packetID > 1)) {
  105. _rfm69_node_info[data->senderID].missing = _rfm69_node_info[data->senderID].missing + gap - 1;
  106. DEBUG_MSG_P(PSTR("[RFM69] %u missing packets detected\n"), gap - 1);
  107. }
  108. }
  109. }
  110. _rfm69Debug("OK ", data);
  111. _rfm69_node_info[data->senderID].lastPacketID = data->packetID;
  112. _rfm69_node_info[data->senderID].count = _rfm69_node_info[data->senderID].count + 1;
  113. // Send info to websocket clients
  114. {
  115. char buffer[200];
  116. snprintf_P(
  117. buffer,
  118. sizeof(buffer) - 1,
  119. PSTR("{\"nodeCount\": %d, \"packetCount\": %lu, \"packet\": {\"senderID\": %u, \"targetID\": %u, \"packetID\": %u, \"key\": \"%s\", \"value\": \"%s\", \"rssi\": %d, \"duplicates\": %d, \"missing\": %d}}"),
  120. _rfm69_node_count, _rfm69_packet_count,
  121. data->senderID, data->targetID, data->packetID, data->key, data->value, data->rssi,
  122. _rfm69_node_info[data->senderID].duplicates , _rfm69_node_info[data->senderID].missing);
  123. wsSend(buffer);
  124. }
  125. // If we are the target of the message, forward it via MQTT, otherwise quit
  126. if (!RFM69_PROMISCUOUS_SENDS && (RFM69_GATEWAY_ID != data->targetID)) return;
  127. // Try to find a matching mapping
  128. for (unsigned int i=0; i<RFM69_MAX_TOPICS; i++) {
  129. unsigned char node = getSetting("node", i, 0).toInt();
  130. if (0 == node) break;
  131. if ((node == data->senderID) && (getSetting("key", i, "").equals(data->key))) {
  132. mqttSendRaw((char *) getSetting("topic", i, "").c_str(), (char *) String(data->value).c_str());
  133. return;
  134. }
  135. }
  136. // Mapping not found, use default topic
  137. String topic = getSetting("rfm69Topic", RFM69_DEFAULT_TOPIC);
  138. if (topic.length() > 0) {
  139. topic.replace("{node}", String(data->senderID));
  140. topic.replace("{key}", String(data->key));
  141. mqttSendRaw((char *) topic.c_str(), (char *) String(data->value).c_str());
  142. }
  143. }
  144. void _rfm69Loop() {
  145. if (_rfm69_radio->receiveDone()) {
  146. uint8_t senderID = _rfm69_radio->SENDERID;
  147. uint8_t targetID = _rfm69_radio->TARGETID;
  148. int16_t rssi = _rfm69_radio->RSSI;
  149. uint8_t length = _rfm69_radio->DATALEN;
  150. char buffer[length + 1];
  151. strncpy(buffer, (const char *) _rfm69_radio->DATA, length);
  152. buffer[length] = 0;
  153. // Do not send ACKs in promiscuous mode,
  154. // we want to listen without being heard
  155. if (!RFM69_PROMISCUOUS) {
  156. if (_rfm69_radio->ACKRequested()) _rfm69_radio->sendACK();
  157. }
  158. uint8_t parts = 1;
  159. for (uint8_t i=0; i<length; i++) {
  160. if (buffer[i] == RFM69_PACKET_SEPARATOR) ++parts;
  161. }
  162. if (parts > 1) {
  163. char sep[2] = {RFM69_PACKET_SEPARATOR, 0};
  164. uint8_t packetID = 0;
  165. char * key = strtok(buffer, sep);
  166. char * value = strtok(NULL, sep);
  167. if (parts > 2) {
  168. char * packet = strtok(NULL, sep);
  169. packetID = atoi(packet);
  170. }
  171. packet_t message;
  172. message.messageID = ++_rfm69_packet_count;
  173. message.packetID = packetID;
  174. message.senderID = senderID;
  175. message.targetID = targetID;
  176. message.key = key;
  177. message.value = value;
  178. message.rssi = rssi;
  179. _rfm69Process(&message);
  180. }
  181. }
  182. }
  183. void _rfm69Clear() {
  184. for(unsigned int i=0; i<RFM69_MAX_NODES; i++) {
  185. _rfm69_node_info[i].duplicates = 0;
  186. _rfm69_node_info[i].missing = 0;
  187. _rfm69_node_info[i].count = 0;
  188. }
  189. _rfm69_node_count = 0;
  190. _rfm69_packet_count = 0;
  191. }
  192. // -----------------------------------------------------------------------------
  193. // RFM69
  194. // -----------------------------------------------------------------------------
  195. void rfm69Setup() {
  196. delay(10);
  197. _rfm69Configure();
  198. _rfm69_radio = new RFM69Wrap(RFM69_CS_PIN, RFM69_IRQ_PIN, RFM69_IS_RFM69HW, digitalPinToInterrupt(RFM69_IRQ_PIN));
  199. _rfm69_radio->initialize(RFM69_FREQUENCY, RFM69_NODE_ID, RFM69_NETWORK_ID);
  200. _rfm69_radio->encrypt(RFM69_ENCRYPTKEY);
  201. _rfm69_radio->promiscuous(RFM69_PROMISCUOUS);
  202. _rfm69_radio->enableAutoPower(0);
  203. if (RFM69_IS_RFM69HW) _rfm69_radio->setHighPower();
  204. DEBUG_MSG_P(PSTR("[RFM69] Worning at %u MHz\n"), RFM69_FREQUENCY == RF69_433MHZ ? 433 : RFM69_FREQUENCY == RF69_868MHZ ? 868 : 915);
  205. DEBUG_MSG_P(PSTR("[RFM69] Node %u\n"), RFM69_NODE_ID);
  206. DEBUG_MSG_P(PSTR("[RFM69] Network %u\n"), RFM69_NETWORK_ID);
  207. DEBUG_MSG_P(PSTR("[RFM69] Promiscuous mode %s\n"), RFM69_PROMISCUOUS ? "ON" : "OFF");
  208. #if WEB_SUPPORT
  209. wsOnSendRegister(_rfm69WebSocketOnSend);
  210. wsOnActionRegister(_rfm69WebSocketOnAction);
  211. #endif
  212. settingsRegisterKeyCheck(_rfm69KeyCheck);
  213. // Main callbacks
  214. espurnaRegisterLoop(_rfm69Loop);
  215. espurnaRegisterReload(_rfm69Configure);
  216. }
  217. #endif // RFM69_SUPPORT