/* RFM69 MODULE Copyright (C) 2016-2017 by Xose PĂ©rez Module key prefix: rfm69 (node, key, topic) */ #if RFM69_SUPPORT #include "libs/RFM69Wrap.h" #define RFM69_PACKET_SEPARATOR ':' // ----------------------------------------------------------------------------- // Locals // ----------------------------------------------------------------------------- RFM69Wrap * _rfm69_radio; struct _node_t { unsigned long count = 0; unsigned long missing = 0; unsigned long duplicates = 0; unsigned char lastPacketID = 0; }; _node_t _rfm69_node_info[255]; unsigned char _rfm69_node_count; unsigned long _rfm69_packet_count; // ----------------------------------------------------------------------------- // WEB // ----------------------------------------------------------------------------- #if WEB_SUPPORT void _rfm69WebSocketOnSend(JsonObject& root) { root["rfm69Visible"] = 1; root["rfm69Topic"] = getSetting("rfm69Topic", RFM69_DEFAULT_TOPIC); root["packetCount"] = _rfm69_packet_count; root["nodeCount"] = _rfm69_node_count; JsonArray& mappings = root.createNestedArray("mapping"); for (unsigned char i=0; imessageID, data->senderID, data->targetID, data->packetID, data->rssi, data->key, data->value ); } void _rfm69Process(packet_t * data) { // Count seen nodes and packets if (_rfm69_node_info[data->senderID].count == 0) ++_rfm69_node_count; ++_rfm69_packet_count; // Detect duplicates and missing packets // packetID==0 means device is not sending packetID info if (data->packetID > 0) { if (_rfm69_node_info[data->senderID].count > 0) { unsigned char gap = data->packetID - _rfm69_node_info[data->senderID].lastPacketID; if (gap == 0) { _rfm69_node_info[data->senderID].duplicates = _rfm69_node_info[data->senderID].duplicates + 1; //_rfm69Debug("DUP", data); return; } if ((gap > 1) && (data->packetID > 1)) { _rfm69_node_info[data->senderID].missing = _rfm69_node_info[data->senderID].missing + gap - 1; DEBUG_MSG_P(PSTR("[RFM69] %u missing packets detected\n"), gap - 1); } } } _rfm69Debug("OK ", data); _rfm69_node_info[data->senderID].lastPacketID = data->packetID; _rfm69_node_info[data->senderID].count = _rfm69_node_info[data->senderID].count + 1; // Send info to websocket clients { char buffer[200]; snprintf_P( buffer, sizeof(buffer) - 1, PSTR("{\"nodeCount\": %d, \"packetCount\": %lu, \"packet\": {\"senderID\": %u, \"targetID\": %u, \"packetID\": %u, \"key\": \"%s\", \"value\": \"%s\", \"rssi\": %d, \"duplicates\": %d, \"missing\": %d}}"), _rfm69_node_count, _rfm69_packet_count, data->senderID, data->targetID, data->packetID, data->key, data->value, data->rssi, _rfm69_node_info[data->senderID].duplicates , _rfm69_node_info[data->senderID].missing); wsSend(buffer); } // If we are the target of the message, forward it via MQTT, otherwise quit if (!RFM69_PROMISCUOUS_SENDS && (RFM69_GATEWAY_ID != data->targetID)) return; // Try to find a matching mapping for (unsigned int i=0; isenderID) && (getSetting("key", i, "").equals(data->key))) { mqttSendRaw((char *) getSetting("topic", i, "").c_str(), (char *) String(data->value).c_str()); return; } } // Mapping not found, use default topic String topic = getSetting("rfm69Topic", RFM69_DEFAULT_TOPIC); if (topic.length() > 0) { topic.replace("{node}", String(data->senderID)); topic.replace("{key}", String(data->key)); mqttSendRaw((char *) topic.c_str(), (char *) String(data->value).c_str()); } } void _rfm69Loop() { if (_rfm69_radio->receiveDone()) { uint8_t senderID = _rfm69_radio->SENDERID; uint8_t targetID = _rfm69_radio->TARGETID; int16_t rssi = _rfm69_radio->RSSI; uint8_t length = _rfm69_radio->DATALEN; char buffer[length + 1]; strncpy(buffer, (const char *) _rfm69_radio->DATA, length); buffer[length] = 0; // Do not send ACKs in promiscuous mode, // we want to listen without being heard if (!RFM69_PROMISCUOUS) { if (_rfm69_radio->ACKRequested()) _rfm69_radio->sendACK(); } uint8_t parts = 1; for (uint8_t i=0; i 1) { char sep[2] = {RFM69_PACKET_SEPARATOR, 0}; uint8_t packetID = 0; char * key = strtok(buffer, sep); char * value = strtok(NULL, sep); if (parts > 2) { char * packet = strtok(NULL, sep); packetID = atoi(packet); } packet_t message; message.messageID = ++_rfm69_packet_count; message.packetID = packetID; message.senderID = senderID; message.targetID = targetID; message.key = key; message.value = value; message.rssi = rssi; _rfm69Process(&message); } } } void _rfm69Clear() { for(unsigned int i=0; i<255; i++) { _rfm69_node_info[i].duplicates = 0; _rfm69_node_info[i].missing = 0; } _rfm69_node_count = 0; _rfm69_packet_count = 0; } // ----------------------------------------------------------------------------- // RFM69 // ----------------------------------------------------------------------------- void rfm69Setup() { delay(10); _rfm69Configure(); _rfm69_radio = new RFM69Wrap(RFM69_CS_PIN, RFM69_IRQ_PIN, RFM69_IS_RFM69HW, digitalPinToInterrupt(RFM69_IRQ_PIN)); _rfm69_radio->initialize(RFM69_FREQUENCY, RFM69_NODE_ID, RFM69_NETWORK_ID); _rfm69_radio->encrypt(RFM69_ENCRYPTKEY); _rfm69_radio->promiscuous(RFM69_PROMISCUOUS); _rfm69_radio->enableAutoPower(0); if (RFM69_IS_RFM69HW) _rfm69_radio->setHighPower(); DEBUG_MSG_P(PSTR("[RFM69] Worning at %u MHz\n"), RFM69_FREQUENCY == RF69_433MHZ ? 433 : RFM69_FREQUENCY == RF69_868MHZ ? 868 : 915); DEBUG_MSG_P(PSTR("[RFM69] Node %u\n"), RFM69_NODE_ID); DEBUG_MSG_P(PSTR("[RFM69] Network %u\n"), RFM69_NETWORK_ID); DEBUG_MSG_P(PSTR("[RFM69] Promiscuous mode %s\n"), RFM69_PROMISCUOUS ? "ON" : "OFF"); #if WEB_SUPPORT wsOnSendRegister(_rfm69WebSocketOnSend); wsOnReceiveRegister(_rfm69WebSocketOnReceive); wsOnAfterParseRegister(_rfm69Configure); wsOnActionRegister(_rfm69WebSocketOnAction); #endif // Register loop espurnaRegisterLoop(_rfm69Loop); } #endif // RFM69_SUPPORT