Skip to content

Commit e5bd891

Browse files
author
Yveaux
committed
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. To not break existing functionality parent search can be started when node ID is still unknown. In this case node ID is set to broadcast ID, which can cause funky stuff to happen...
1 parent ae9332b commit e5bd891

File tree

2 files changed

+86
-59
lines changed

2 files changed

+86
-59
lines changed

libraries/MySensors/MySensor.cpp

Lines changed: 85 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@
1414
#include "utility/RF24.h"
1515
#include "utility/RF24_config.h"
1616

17+
#define DISTANCE_INVALID (0xFF)
18+
1719

1820
// Inline function and macros
19-
inline MyMessage& build (MyMessage &msg, uint8_t sender, uint8_t destination, uint8_t sensor, uint8_t command, uint8_t type, bool enableAck) {
21+
static inline MyMessage& build (MyMessage &msg, uint8_t sender, uint8_t destination, uint8_t sensor, uint8_t command, uint8_t type, bool enableAck) {
2022
msg.sender = sender;
2123
msg.destination = destination;
2224
msg.sensor = sensor;
@@ -27,6 +29,13 @@ inline MyMessage& build (MyMessage &msg, uint8_t sender, uint8_t destination, ui
2729
return msg;
2830
}
2931

32+
static inline bool isValidParent( const uint8_t parent ) {
33+
return parent != AUTO;
34+
}
35+
static inline bool isValidDistance( const uint8_t distance ) {
36+
return distance != DISTANCE_INVALID;
37+
}
38+
3039

3140
MySensor::MySensor(uint8_t _cepin, uint8_t _cspin) : RF24(_cepin, _cspin) {
3241
}
@@ -37,6 +46,7 @@ void MySensor::begin(void (*_msgCallback)(const MyMessage &), uint8_t _nodeId, b
3746
isGateway = false;
3847
repeaterMode = _repeaterMode;
3948
msgCallback = _msgCallback;
49+
failedTransmissions = 0;
4050

4151
if (repeaterMode) {
4252
setupRepeaterMode();
@@ -52,29 +62,26 @@ void MySensor::begin(void (*_msgCallback)(const MyMessage &), uint8_t _nodeId, b
5262
cc.isMetric = 0x01;
5363
}
5464

55-
if (_parentNodeId != AUTO) {
65+
autoFindParent = _parentNodeId == AUTO;
66+
if (!autoFindParent) {
5667
nc.parentNodeId = _parentNodeId;
57-
autoFindParent = false;
58-
} else {
59-
autoFindParent = true;
68+
nc.distance = 0;
69+
} else if (!isValidParent(nc.parentNodeId)) {
70+
// Auto find parent, but parent in eeprom is invalid. Force parent search on first transmit.
71+
nc.distance = DISTANCE_INVALID;
6072
}
6173

6274
if (_nodeId != AUTO) {
6375
// Set static id
6476
nc.nodeId = _nodeId;
6577
}
6678

67-
// If no parent was found in eeprom. Try to find one.
68-
if (autoFindParent && nc.parentNodeId == 0xff) {
69-
findParentNode();
70-
}
71-
7279
// Try to fetch node-id from gateway
7380
if (nc.nodeId == AUTO) {
7481
requestNodeId();
7582
}
7683

77-
debug(PSTR("%s started, id %d\n"), repeaterMode?"repeater":"sensor", nc.nodeId);
84+
debug(PSTR("%s started, id=%d, parent=%d, distance=%d\n"), repeaterMode?"repeater":"sensor", nc.nodeId, nc.parentNodeId, nc.distance);
7885

7986
// Open reading pipe for messages directed to this node (set write pipe to same)
8087
RF24::openReadingPipe(WRITE_PIPE, TO_ADDR(nc.nodeId));
@@ -93,8 +100,6 @@ void MySensor::begin(void (*_msgCallback)(const MyMessage &), uint8_t _nodeId, b
93100
}
94101

95102
void MySensor::setupRadio(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e dataRate) {
96-
failedTransmissions = 0;
97-
98103
// Start up the radio library
99104
RF24::begin();
100105

@@ -104,13 +109,13 @@ void MySensor::setupRadio(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_
104109
}
105110
RF24::setAutoAck(1);
106111
RF24::setAutoAck(BROADCAST_PIPE,false); // Turn off auto ack for broadcast
107-
RF24::enableAckPayload();
108112
RF24::setChannel(channel);
109113
RF24::setPALevel(paLevel);
110114
RF24::setDataRate(dataRate);
111115
RF24::setRetries(5,15);
112116
RF24::setCRCLength(RF24_CRC_16);
113117
RF24::enableDynamicPayloads();
118+
RF24::enableDynamicAck(); // Required to disable ack-sending for broadcast messages
114119

115120
// All nodes listen to broadcast pipe (for FIND_PARENT_RESPONSE messages)
116121
RF24::openReadingPipe(BROADCAST_PIPE, TO_ADDR(BROADCAST_ADDRESS));
@@ -139,14 +144,12 @@ void MySensor::requestNodeId() {
139144

140145

141146
void MySensor::findParentNode() {
142-
failedTransmissions = 0;
143-
144-
// Set distance to max
145-
nc.distance = 255;
146-
147147
// Send ping message to BROADCAST_ADDRESS (to which all relaying nodes and gateway listens and should reply to)
148+
debug(PSTR("find parent\n"));
149+
148150
build(msg, nc.nodeId, BROADCAST_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_FIND_PARENT, false).set("");
149-
sendWrite(BROADCAST_ADDRESS, msg, true);
151+
// Write msg, but suppress recursive parent search
152+
sendWrite(BROADCAST_ADDRESS, msg, false);
150153

151154
// Wait for ping response.
152155
waitForReply();
@@ -182,44 +185,64 @@ boolean MySensor::sendRoute(MyMessage &message) {
182185
} else if (isInternal && message.type == I_ID_RESPONSE && dest==BROADCAST_ADDRESS) {
183186
// Node has not yet received any id. We need to send it
184187
// by doing a broadcast sending,
185-
return sendWrite(BROADCAST_ADDRESS, message, true);
188+
return sendWrite(BROADCAST_ADDRESS, message);
186189
}
187190
}
188191

189192
if (!isGateway) {
190-
// --- debug(PSTR("route parent\n"));
191193
// Should be routed back to gateway.
192-
bool ok = sendWrite(nc.parentNodeId, message);
193-
194-
if (!ok) {
195-
// Failure when sending to parent node. The parent node might be down and we
196-
// need to find another route to gateway.
197-
if (autoFindParent && failedTransmissions > SEARCH_FAILURES) {
198-
findParentNode();
199-
}
200-
failedTransmissions++;
201-
} else {
202-
failedTransmissions = 0;
203-
}
204-
return ok;
194+
return sendWrite(nc.parentNodeId, message);
205195
}
206196
return false;
207197
}
208198

209-
boolean MySensor::sendWrite(uint8_t next, MyMessage &message, bool broadcast) {
210-
uint8_t length = mGetLength(message);
211-
message.last = nc.nodeId;
212-
mSetVersion(message, PROTOCOL_VERSION);
213-
// Make sure radio has powered up
214-
RF24::powerUp();
215-
RF24::stopListening();
216-
RF24::openWritingPipe(TO_ADDR(next));
217-
bool ok = RF24::write(&message, min(MAX_MESSAGE_LENGTH, HEADER_SIZE + length), broadcast);
218-
RF24::startListening();
219-
220-
debug(PSTR("send: %d-%d-%d-%d s=%d,c=%d,t=%d,pt=%d,l=%d,st=%s:%s\n"),
221-
message.sender,message.last, next, message.destination, message.sensor, mGetCommand(message), message.type, mGetPayloadType(message), mGetLength(message), ok?"ok":"fail", message.getString(convBuf));
199+
boolean MySensor::sendWrite(uint8_t next, MyMessage &message, const bool allowFindParent) {
200+
bool ok = true;
201+
const bool broadcast = next == BROADCAST_ADDRESS;
202+
const bool toParent = next == nc.parentNodeId;
203+
// With current implementation parent node Id can equal the broadcast address when
204+
// starting with empty eeprom and AUTO node Id is active.
205+
// This behavior is undesired, as possible parents will report back to broadcast address.
206+
// debug(PSTR("sendWrite next=%d, parent=%d, distance=%d\n"), next, nc.parentNodeId, nc.distance);
207+
// If sending directly to parent node and distance is not set, then try to find parent now.
208+
if ( allowFindParent && toParent && !isValidDistance(nc.distance) ) {
209+
findParentNode();
210+
// Known distance indicates parent has been found
211+
ok = isValidDistance(nc.distance);
212+
}
222213

214+
if (ok) {
215+
uint8_t length = mGetLength(message);
216+
message.last = nc.nodeId;
217+
mSetVersion(message, PROTOCOL_VERSION);
218+
// Make sure radio has powered up
219+
RF24::powerUp();
220+
RF24::stopListening();
221+
RF24::openWritingPipe(TO_ADDR(next));
222+
// Send message. Disable auto-ack for broadcasts.
223+
ok = RF24::write(&message, min(MAX_MESSAGE_LENGTH, HEADER_SIZE + length), broadcast);
224+
RF24::startListening();
225+
226+
debug(PSTR("send: %d-%d-%d-%d s=%d,c=%d,t=%d,pt=%d,l=%d,st=%s:%s\n"),
227+
message.sender,message.last, next, message.destination, message.sensor, mGetCommand(message), message.type,
228+
mGetPayloadType(message), mGetLength(message), broadcast ? "bc" : (ok ? "ok":"fail"), message.getString(convBuf));
229+
230+
// If many successive transmissions to parent failed, the parent node might be down and we
231+
// need to find another route to gateway.
232+
if (toParent) {
233+
if (ok) {
234+
failedTransmissions = 0;
235+
} else {
236+
failedTransmissions++;
237+
if ( autoFindParent && (failedTransmissions >= SEARCH_FAILURES)) {
238+
debug(PSTR("lost parent\n"));
239+
// Set distance invalid to trigger parent search on next write.
240+
nc.distance = DISTANCE_INVALID;
241+
failedTransmissions = 0;
242+
}
243+
}
244+
}
245+
}
223246
return ok;
224247
}
225248

@@ -267,7 +290,6 @@ boolean MySensor::process() {
267290

268291
uint8_t len = RF24::getDynamicPayloadSize();
269292
RF24::read(&msg, len);
270-
RF24::writeAckPayload(pipe,&pipe, 1 );
271293

272294
// Add string termination, good if we later would want to print it.
273295
msg.data[mGetLength(msg)] = '\0';
@@ -287,10 +309,10 @@ boolean MySensor::process() {
287309

288310
if (repeaterMode && command == C_INTERNAL && type == I_FIND_PARENT) {
289311
// Relaying nodes should always answer ping messages
290-
// Wait a random delay of 0-2 seconds to minimize collision
312+
// Wait a random delay of 0-1.023 seconds to minimize collision
291313
// between ping ack messages from other relaying nodes
292314
delay(millis() & 0x3ff);
293-
sendWrite(sender, build(msg, nc.nodeId, sender, NODE_SENSOR_ID, C_INTERNAL, I_FIND_PARENT_RESPONSE, false).set(nc.distance), true);
315+
sendWrite(sender, build(msg, nc.nodeId, sender, NODE_SENSOR_ID, C_INTERNAL, I_FIND_PARENT_RESPONSE, false).set(nc.distance));
294316
return false;
295317
} else if (destination == nc.nodeId) {
296318
// Check if sender requests an ack back.
@@ -315,13 +337,18 @@ boolean MySensor::process() {
315337
// We've received a reply to a FIND_PARENT message. Check if the distance is
316338
// shorter than we already have.
317339
uint8_t distance = msg.getByte();
318-
if (distance<nc.distance-1) {
319-
// Found a neighbor closer to GW than previously found
320-
nc.distance = distance + 1;
321-
nc.parentNodeId = msg.sender;
322-
eeprom_write_byte((uint8_t*)EEPROM_PARENT_NODE_ID_ADDRESS, nc.parentNodeId);
323-
eeprom_write_byte((uint8_t*)EEPROM_DISTANCE_ADDRESS, nc.distance);
324-
debug(PSTR("new parent=%d, d=%d\n"), nc.parentNodeId, nc.distance);
340+
if (isValidDistance(distance))
341+
{
342+
// Distance to gateway is one more for us w.r.t. parent
343+
distance++;
344+
if (isValidDistance(distance) && (distance < nc.distance)) {
345+
// Found a neighbor closer to GW than previously found
346+
nc.distance = distance;
347+
nc.parentNodeId = msg.sender;
348+
eeprom_write_byte((uint8_t*)EEPROM_PARENT_NODE_ID_ADDRESS, nc.parentNodeId);
349+
eeprom_write_byte((uint8_t*)EEPROM_DISTANCE_ADDRESS, nc.distance);
350+
debug(PSTR("new parent=%d, d=%d\n"), nc.parentNodeId, nc.distance);
351+
}
325352
}
326353
return false;
327354
} else if (sender == GATEWAY_ADDRESS) {

libraries/MySensors/MySensor.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@ class MySensor : public RF24
264264
void setupRepeaterMode();
265265
void setupRadio(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e dataRate);
266266
boolean sendRoute(MyMessage &message);
267-
boolean sendWrite(uint8_t dest, MyMessage &message, bool broadcast=false);
267+
boolean sendWrite(uint8_t dest, MyMessage &message, const bool allowFindParent = true );
268268

269269
private:
270270
#ifdef DEBUG

0 commit comments

Comments
 (0)