From 0f34bb33be047b7bc99f4a2b0ac74c11a0d4b28a Mon Sep 17 00:00:00 2001 From: Yveaux Date: Tue, 9 Sep 2014 19:43:08 +0200 Subject: [PATCH] Improved parent node search Retry parent node search with each transmit when parent has not been found. Transmit does not block on transmit when no parent has been found as this depletes batteries on battery powered nodes. Fix nRF24 configuration for dynamic ACK's. This bug caused broadcasts to be sent with auto-ACK's. --- libraries/MySensors/MySensor.cpp | 134 ++++++++++++++++++------------- libraries/MySensors/MySensor.h | 4 +- 2 files changed, 80 insertions(+), 58 deletions(-) diff --git a/libraries/MySensors/MySensor.cpp b/libraries/MySensors/MySensor.cpp index 0f2399b..7527c2a 100755 --- a/libraries/MySensors/MySensor.cpp +++ b/libraries/MySensors/MySensor.cpp @@ -14,9 +14,11 @@ #include "utility/RF24.h" #include "utility/RF24_config.h" +#define DISTANCE_INVALID (0xFF) + // Inline function and macros -inline MyMessage& build (MyMessage &msg, uint8_t sender, uint8_t destination, uint8_t sensor, uint8_t command, uint8_t type, bool enableAck) { +static inline MyMessage& build (MyMessage &msg, uint8_t sender, uint8_t destination, uint8_t sensor, uint8_t command, uint8_t type, bool enableAck) { msg.sender = sender; msg.destination = destination; msg.sensor = sensor; @@ -27,6 +29,13 @@ inline MyMessage& build (MyMessage &msg, uint8_t sender, uint8_t destination, ui return msg; } +static inline bool isValidParent( const uint8_t parent ) { + return parent != AUTO; +} +static inline bool isValidDistance( const uint8_t distance ) { + return distance != DISTANCE_INVALID; +} + MySensor::MySensor(uint8_t _cepin, uint8_t _cspin) : RF24(_cepin, _cspin) { } @@ -37,6 +46,7 @@ void MySensor::begin(void (*_msgCallback)(const MyMessage &), uint8_t _nodeId, b isGateway = false; repeaterMode = _repeaterMode; msgCallback = _msgCallback; + failedTransmissions = 0; if (repeaterMode) { setupRepeaterMode(); @@ -52,11 +62,13 @@ void MySensor::begin(void (*_msgCallback)(const MyMessage &), uint8_t _nodeId, b cc.isMetric = 0x01; } - if (_parentNodeId != AUTO) { + autoFindParent = _parentNodeId == AUTO; + if (!autoFindParent) { nc.parentNodeId = _parentNodeId; - autoFindParent = false; - } else { - autoFindParent = true; + nc.distance = 0; + } else if (!isValidParent(nc.parentNodeId)) { + // Auto find parent, but parent in eeprom is invalid. Force parent search on first transmit. + nc.distance = DISTANCE_INVALID; } if (_nodeId != AUTO) { @@ -64,11 +76,6 @@ void MySensor::begin(void (*_msgCallback)(const MyMessage &), uint8_t _nodeId, b nc.nodeId = _nodeId; } - // If no parent was found in eeprom. Try to find one. - if (autoFindParent && nc.parentNodeId == 0xff) { - findParentNode(); - } - // Try to fetch node-id from gateway if (nc.nodeId == AUTO) { requestNodeId(); @@ -93,8 +100,6 @@ void MySensor::begin(void (*_msgCallback)(const MyMessage &), uint8_t _nodeId, b } void MySensor::setupRadio(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e dataRate) { - failedTransmissions = 0; - // Start up the radio library RF24::begin(); @@ -104,13 +109,13 @@ void MySensor::setupRadio(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_ } RF24::setAutoAck(1); RF24::setAutoAck(BROADCAST_PIPE,false); // Turn off auto ack for broadcast - RF24::enableAckPayload(); RF24::setChannel(channel); RF24::setPALevel(paLevel); RF24::setDataRate(dataRate); RF24::setRetries(5,15); RF24::setCRCLength(RF24_CRC_16); RF24::enableDynamicPayloads(); + RF24::enableDynamicAck();// Required to disable ack-sending for broadcast messages // All nodes listen to broadcast pipe (for FIND_PARENT_RESPONSE messages) RF24::openReadingPipe(BROADCAST_PIPE, TO_ADDR(BROADCAST_ADDRESS)); @@ -139,14 +144,11 @@ void MySensor::requestNodeId() { void MySensor::findParentNode() { - failedTransmissions = 0; - - // Set distance to max - nc.distance = 255; - // Send ping message to BROADCAST_ADDRESS (to which all relaying nodes and gateway listens and should reply to) + debug(PSTR("find parent\n")); + build(msg, nc.nodeId, BROADCAST_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_FIND_PARENT, false).set(""); - sendWrite(BROADCAST_ADDRESS, msg, true); + sendWrite(BROADCAST_ADDRESS, msg); // Wait for ping response. waitForReply(); @@ -182,44 +184,59 @@ boolean MySensor::sendRoute(MyMessage &message) { } else if (isInternal && message.type == I_ID_RESPONSE && dest==BROADCAST_ADDRESS) { // Node has not yet received any id. We need to send it // by doing a broadcast sending, - return sendWrite(BROADCAST_ADDRESS, message, true); + return sendWrite(BROADCAST_ADDRESS, message); } } if (!isGateway) { - // --- debug(PSTR("route parent\n")); // Should be routed back to gateway. - bool ok = sendWrite(nc.parentNodeId, message); - - if (!ok) { - // Failure when sending to parent node. The parent node might be down and we - // need to find another route to gateway. - if (autoFindParent && failedTransmissions > SEARCH_FAILURES) { - findParentNode(); - } - failedTransmissions++; - } else { - failedTransmissions = 0; - } - return ok; + return sendWrite(nc.parentNodeId, message); } return false; } -boolean MySensor::sendWrite(uint8_t next, MyMessage &message, bool broadcast) { - uint8_t length = mGetLength(message); - message.last = nc.nodeId; - mSetVersion(message, PROTOCOL_VERSION); - // Make sure radio has powered up - RF24::powerUp(); - RF24::stopListening(); - RF24::openWritingPipe(TO_ADDR(next)); - bool ok = RF24::write(&message, min(MAX_MESSAGE_LENGTH, HEADER_SIZE + length), broadcast); - RF24::startListening(); +boolean MySensor::sendWrite(uint8_t next, MyMessage &message) { + bool ok = true; + const bool broadcast = next == BROADCAST_ADDRESS; + const bool toParent = !broadcast && (next == nc.parentNodeId); + // If sending directly to parent node and distance is not set, then try to find parent now. + if ( toParent && !isValidDistance(nc.distance) ) { + findParentNode(); + // Known distance indicates parent has been found + ok = isValidDistance(nc.distance); + } - debug(PSTR("send: %d-%d-%d-%d s=%d,c=%d,t=%d,pt=%d,l=%d,st=%s:%s\n"), - message.sender,message.last, next, message.destination, message.sensor, mGetCommand(message), message.type, mGetPayloadType(message), mGetLength(message), ok?"ok":"fail", message.getString(convBuf)); + if (ok) { + uint8_t length = mGetLength(message); + message.last = nc.nodeId; + mSetVersion(message, PROTOCOL_VERSION); + // Make sure radio has powered up + RF24::powerUp(); + RF24::stopListening(); + RF24::openWritingPipe(TO_ADDR(next)); + // Send message. Disable auto-ack for broadcasts. + ok = RF24::write(&message, min(MAX_MESSAGE_LENGTH, HEADER_SIZE + length), broadcast); + RF24::startListening(); + debug(PSTR("send: %d-%d-%d-%d s=%d,c=%d,t=%d,pt=%d,l=%d,st=%s:%s\n"), + message.sender,message.last, next, message.destination, message.sensor, mGetCommand(message), message.type, mGetPayloadType(message), mGetLength(message), ok?"ok":"fail", message.getString(convBuf)); + + // If many successive transmissions to parent failed, the parent node might be down and we + // need to find another route to gateway. + if (toParent) { + if (ok) { + failedTransmissions = 0; + } else { + failedTransmissions++; + if ( autoFindParent && (failedTransmissions >= SEARCH_FAILURES)) { + debug(PSTR("lost parent\n")); + // Set distance invalid to trigger parent search on next write. + nc.distance = DISTANCE_INVALID; + failedTransmissions = 0; + } + } + } + } return ok; } @@ -267,7 +284,7 @@ boolean MySensor::process() { uint8_t len = RF24::getDynamicPayloadSize(); RF24::read(&msg, len); - RF24::writeAckPayload(pipe,&pipe, 1 ); +// RF24::writeAckPayload(pipe,&pipe, 1 ); // Add string termination, good if we later would want to print it. msg.data[mGetLength(msg)] = '\0'; @@ -287,10 +304,10 @@ boolean MySensor::process() { if (repeaterMode && command == C_INTERNAL && type == I_FIND_PARENT) { // Relaying nodes should always answer ping messages - // Wait a random delay of 0-2 seconds to minimize collision + // Wait a random delay of 0-1.023 seconds to minimize collision // between ping ack messages from other relaying nodes delay(millis() & 0x3ff); - sendWrite(sender, build(msg, nc.nodeId, sender, NODE_SENSOR_ID, C_INTERNAL, I_FIND_PARENT_RESPONSE, false).set(nc.distance), true); + sendWrite(sender, build(msg, nc.nodeId, sender, NODE_SENSOR_ID, C_INTERNAL, I_FIND_PARENT_RESPONSE, false).set(nc.distance)); return false; } else if (destination == nc.nodeId) { // Check if sender requests an ack back. @@ -315,13 +332,18 @@ boolean MySensor::process() { // We've received a reply to a FIND_PARENT message. Check if the distance is // shorter than we already have. uint8_t distance = msg.getByte(); - if (distance