Skip to content

Commit 3b86153

Browse files
committed
Merge pull request arduino#35 from Yveaux/development
Improved parent node search
2 parents ae9332b + e5bd891 commit 3b86153

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)