Increased stability. Higher radio speeds (2mbps). Soft-acks implemeted to avoid the unreliable hw-acks. No trash messages found during test runs.
This commit is contained in:
@@ -47,6 +47,8 @@ void setup()
|
||||
// reserve bytes for the inputString. RadioId;ChildId;VAR=VALUE
|
||||
inputString.reserve(MAX_RECEIVE_LENGTH);
|
||||
gw.begin();
|
||||
gw.setPALevel(RF24_PA_HIGH); //Adjust PA-level: MIN, LOW, HIGH, MAX (MAX can sometimes cause problems when using amplified NRF24L01)
|
||||
|
||||
|
||||
// C++ classes and interrupts really sucks. Need to attach interrupt
|
||||
// outside thw Gateway class due to language shortcomings! Gah!
|
||||
|
||||
@@ -13,11 +13,13 @@
|
||||
|
||||
Gateway::Gateway(uint8_t _cepin, uint8_t _cspin, uint8_t _inclusion_time) : Relay(_cepin, _cspin) {
|
||||
ledMode = false;
|
||||
isRelay = true;
|
||||
inclusionTime = _inclusion_time;
|
||||
}
|
||||
|
||||
Gateway::Gateway(uint8_t _cepin, uint8_t _cspin, uint8_t _inclusion, uint8_t _inclusion_time, uint8_t _rx, uint8_t _tx, uint8_t _er) : Relay(_cepin, _cspin) {
|
||||
ledMode = true;
|
||||
isRelay = true;
|
||||
pinInclusion = _inclusion;
|
||||
inclusionTime = _inclusion_time;
|
||||
pinRx = _rx;
|
||||
@@ -169,8 +171,8 @@ void Gateway::setInclusionMode(boolean newMode) {
|
||||
|
||||
|
||||
// Override normal validate to add error blink if crc check fails
|
||||
uint8_t Gateway::validate() {
|
||||
uint8_t res = Sensor::validate();
|
||||
uint8_t Gateway::validate(uint8_t length) {
|
||||
uint8_t res = Sensor::validate(length);
|
||||
if (res == VALIDATE_BAD_CRC) {
|
||||
errBlink(1);
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ class Gateway : public Relay
|
||||
uint8_t pinEr;
|
||||
|
||||
void serial(const char *fmt, ... );
|
||||
uint8_t validate();
|
||||
uint8_t validate(uint8_t length);
|
||||
void serial(message_s msg);
|
||||
void interruptStartInclusion();
|
||||
void checkButtonTriggeredInclusion();
|
||||
|
||||
@@ -23,9 +23,6 @@ void Relay::begin(uint8_t _radioId) {
|
||||
for (int i=0;i<sizeof(childNodeTable);i++) {
|
||||
childNodeTable[i] = EEPROM.read(EEPROM_ROUTES_ADDRESS+i);
|
||||
}
|
||||
|
||||
// Start listening for incoming messages
|
||||
RF24::startListening();
|
||||
}
|
||||
|
||||
|
||||
@@ -91,14 +88,11 @@ boolean Relay::messageAvailable() {
|
||||
|
||||
if (available && pipe<7) {
|
||||
uint8_t len = RF24::getDynamicPayloadSize()-sizeof(msg.header);
|
||||
readMessage();
|
||||
boolean ok = validate() == VALIDATE_OK;
|
||||
debug(PSTR("Message crc %s.\n"),ok?"ok":"error");
|
||||
boolean ok = readMessage();
|
||||
if (ok) {
|
||||
if (msg.header.messageType == M_INTERNAL &&
|
||||
msg.header.type == I_PING) {
|
||||
// We always answer ping messages
|
||||
debug(PSTR("Answer ping message.\n"));
|
||||
// Wait a random delay of 0-2 seconds to minimize collision
|
||||
// between ping ack messages from other relaying nodes
|
||||
randomSeed(millis());
|
||||
|
||||
+105
-117
@@ -23,16 +23,17 @@ void Sensor::setupRadio() {
|
||||
// Start up the radio library
|
||||
RF24::begin();
|
||||
RF24::enableDynamicPayloads();
|
||||
|
||||
RF24::setAutoAck(true);
|
||||
RF24::setRetries(15, 15);
|
||||
|
||||
//RF24::setChannel(70);
|
||||
RF24::setDataRate(RF24_1MBPS);
|
||||
RF24::setAutoAck(false);
|
||||
RF24::setRetries(15, 15);
|
||||
RF24::setPALevel(RF24_PA_MAX);
|
||||
//RF24::setChannel(70); // Default 70
|
||||
RF24::setDataRate(RF24_2MBPS);
|
||||
RF24::setCRCLength(RF24_CRC_16);
|
||||
|
||||
// All (relaying) nodes listen to broadcast pipe (for PING messages)
|
||||
RF24::openReadingPipe(BROADCAST_PIPE, BASE_RADIO_ID + BROADCAST_ADDRESS);
|
||||
// All repeater nodes and gateway listen to broadcast pipe (for PING messages)
|
||||
if (isRelay) {
|
||||
RF24::openReadingPipe(BROADCAST_PIPE, TO_ADDR(BROADCAST_ADDRESS));
|
||||
}
|
||||
}
|
||||
|
||||
void Sensor::begin(uint8_t _radioId) {
|
||||
@@ -55,7 +56,7 @@ void Sensor::begin(uint8_t _radioId) {
|
||||
initializeRadioId();
|
||||
|
||||
// Open reading pipe for messages directed to this node
|
||||
RF24::openReadingPipe(CURRENT_NODE_PIPE, BASE_RADIO_ID+radioId);
|
||||
RF24::openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(radioId));
|
||||
|
||||
// Send presentation for this radio node
|
||||
sendSensorPresentation(NODE_CHILD_ID, isRelay? S_ARDUINO_RELAY : S_ARDUINO_NODE);
|
||||
@@ -85,7 +86,7 @@ void Sensor::initializeRadioId() {
|
||||
// No radio id has been fetched yet ant EEPROM is unwritten.
|
||||
// Request new id from sensor net gateway. Use radioId 4095 temporarily
|
||||
// to be able to receive my correct nodeId.
|
||||
RF24::openReadingPipe(CURRENT_NODE_PIPE, BASE_RADIO_ID+radioId);
|
||||
RF24::openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(radioId));
|
||||
radioId = atoi(getInternal(I_REQUEST_ID));
|
||||
// Write id to EEPROM
|
||||
if (radioId == AUTO) { // sensor net gateway will return max id if all sensor id are taken
|
||||
@@ -112,7 +113,7 @@ void Sensor::findRelay() {
|
||||
|
||||
// Open a reading pipe for current radioId (if it differs from broadcast)
|
||||
if (radioId != BROADCAST_PIPE) {
|
||||
RF24::openReadingPipe(CURRENT_NODE_PIPE, BASE_RADIO_ID+radioId);
|
||||
RF24::openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(radioId));
|
||||
debug(PSTR("Open ping reading pipe: %d\n"), radioId);
|
||||
}
|
||||
distance = 255;
|
||||
@@ -156,10 +157,6 @@ void Sensor::findRelay() {
|
||||
EEPROM.write(EEPROM_RELAY_ID_ADDRESS, relayId);
|
||||
EEPROM.write(EEPROM_DISTANCE_ADDRESS, distance);
|
||||
}
|
||||
|
||||
// Always listen to relay pipe (relay broadcasts messages that might be for this
|
||||
// nodes children)
|
||||
RF24::openReadingPipe(RELAY_PIPE, BASE_RADIO_ID + relayId);
|
||||
}
|
||||
|
||||
|
||||
@@ -205,7 +202,7 @@ boolean Sensor::send(message_s message, int length) {
|
||||
if (!ok && radioId != GATEWAY_ADDRESS) {
|
||||
// Failure when sending to relay node. The relay node might be down and we
|
||||
// need to find another route to gateway. Max 20 retries before giving up.
|
||||
if (failedTransmissions>20) {
|
||||
if (failedTransmissions > FIND_RELAY_RETRIES) {
|
||||
findRelay();
|
||||
}
|
||||
failedTransmissions++;
|
||||
@@ -217,27 +214,45 @@ boolean Sensor::send(message_s message, int length) {
|
||||
|
||||
|
||||
boolean Sensor::sendWrite(uint8_t dest, message_s message, int length) {
|
||||
// Add current radioId to last-field and calculate crc
|
||||
message.header.last = radioId;
|
||||
message.header.crc = crc8Message(message);
|
||||
debug(PSTR("Tx: f=%d,t=%d,p=%d,n=%d,c=%d,mt=%d,t=%d,x=%d: %s\n"),
|
||||
message.header.from,message.header.to, message.header.last, dest, message.header.childId, message.header.messageType, message.header.type, message.header.crc, message.data);
|
||||
|
||||
message.header.last = radioId;
|
||||
message.header.crc = crc8Message(message, length);
|
||||
debug(PSTR("Tx: fr=%d,to=%d,la=%d,ne=%d,ci=%d,mt=%d,ty=%d,cr=%d: %s\n"),
|
||||
message.header.from,message.header.to, message.header.last, dest, message.header.childId, message.header.messageType, message.header.type, message.header.crc, message.data);
|
||||
|
||||
bool ok = true;
|
||||
bool broadcast = message.header.messageType == M_INTERNAL && message.header.type == I_PING;
|
||||
int retry = WRITE_RETRY;
|
||||
RF24::stopListening();
|
||||
bool ok = false;
|
||||
RF24::openWritingPipe(BASE_RADIO_ID+ dest);
|
||||
int retry = 5;
|
||||
do {
|
||||
ok = RF24::write(&message, min(MAX_MESSAGE_LENGTH, sizeof(message.header) + length));
|
||||
}
|
||||
while ( !ok && --retry );
|
||||
RF24::openWritingPipe(TO_ADDR(dest));
|
||||
RF24::write(&message, min(MAX_MESSAGE_LENGTH, sizeof(message.header) + length), broadcast);
|
||||
RF24::startListening();
|
||||
// RF24::openReadingPipe(CURRENT_NODE_PIPE, BASE_RADIO_ID+radioId);
|
||||
RF24::closeReadingPipe(WRITE_PIPE); // Stop listening to write-pipe after transmit
|
||||
|
||||
if (!broadcast) {
|
||||
// ---------------- WAIT FOR ACK ------------------
|
||||
unsigned long startedWaiting = millis();
|
||||
bool timeout = false;
|
||||
// Wait max 100 ms
|
||||
while ( !RF24::available() && !timeout ) {
|
||||
if (millis() - startedWaiting > 100 ) {
|
||||
timeout = true;
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
// Check payload size and content
|
||||
if ( !timeout && RF24::getDynamicPayloadSize()==sizeof(uint8_t)) {
|
||||
uint8_t idest;
|
||||
RF24::read( &idest, sizeof(uint8_t));
|
||||
if (dest != idest) { ok == false; }
|
||||
} else { ok = false; }
|
||||
//--------------------
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
debug(PSTR("Sent successfully\n"));
|
||||
} else {
|
||||
debug(PSTR("Send failed. No ack received.\n"));
|
||||
debug(PSTR("Send failed.\n"));
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
@@ -381,9 +396,7 @@ boolean Sensor::messageAvailable() {
|
||||
|
||||
|
||||
if (available && pipe<7) {
|
||||
readMessage();
|
||||
boolean ok = validate() == VALIDATE_OK;
|
||||
debug(PSTR("Mess crc %s.\n"),ok?"ok":"error");
|
||||
boolean ok = readMessage();
|
||||
if (ok && msg.header.to == radioId) {
|
||||
// This message is addressed to this node
|
||||
debug(PSTR("Message addressed for this node.\n"));
|
||||
@@ -414,106 +427,81 @@ message_s Sensor::getMessage() {
|
||||
}
|
||||
|
||||
|
||||
message_s Sensor::readMessage() {
|
||||
boolean Sensor::readMessage() {
|
||||
uint8_t len = RF24::getDynamicPayloadSize();
|
||||
bool done = RF24::read(&msg, len);
|
||||
// Make sure string gets terminated ok.
|
||||
boolean done = RF24::read(&msg, len);
|
||||
|
||||
if (!(msg.header.messageType==M_INTERNAL && msg.header.type == I_PING_ACK)) {
|
||||
RF24::stopListening();
|
||||
RF24::openWritingPipe(TO_ADDR(msg.header.last));
|
||||
RF24::write(&radioId, sizeof(uint8_t));
|
||||
RF24::startListening();
|
||||
RF24::closeReadingPipe(WRITE_PIPE); // Stop listening to write-pipe after transmit
|
||||
}
|
||||
uint8_t cb = msg.header.crc;
|
||||
uint8_t valid = validate(len-sizeof(header_s));
|
||||
boolean ok = valid == VALIDATE_OK;
|
||||
|
||||
|
||||
// Make sure string gets terminated ok for full sized messages.
|
||||
msg.data[len - sizeof(header_s) ] = '\0';
|
||||
debug(PSTR("Rx: f=%d,t=%d,l=%d,c=%d,mt=%d,t=%d,x=%d: %s\n"),
|
||||
msg.header.from,msg.header.to, msg.header.last, msg.header.childId, msg.header.messageType, msg.header.type,msg.header.crc, msg.data);
|
||||
return msg;
|
||||
debug(PSTR("Rx: fr=%d,to=%d,la=%d,ci=%d,mt=%d,t=%d,cr=%d(%s): %s\n"),
|
||||
msg.header.from,msg.header.to, msg.header.last, msg.header.childId, msg.header.messageType, msg.header.type, msg.header.crc, valid==0?"ok":valid==1?"ec":"ev", msg.data);
|
||||
return ok;
|
||||
}
|
||||
|
||||
/*
|
||||
* raw CRC 8 bit calculation
|
||||
*/
|
||||
uint8_t Sensor::crc8Raw ( uint8_t *data_in, uint8_t number_of_bytes_to_read )
|
||||
{
|
||||
uint8_t crc;
|
||||
uint8_t loop_count;
|
||||
uint8_t bit_counter;
|
||||
uint8_t data;
|
||||
uint8_t feedback_bit;
|
||||
|
||||
crc = CRC8INIT;
|
||||
|
||||
for (loop_count = 0; loop_count != number_of_bytes_to_read; loop_count++)
|
||||
{
|
||||
data = data_in[loop_count];
|
||||
|
||||
bit_counter = 8;
|
||||
do {
|
||||
feedback_bit = (crc ^ data) & 0x01;
|
||||
|
||||
if ( feedback_bit == 0x01 ) {
|
||||
crc = crc ^ CRC8POLY;
|
||||
}
|
||||
crc = (crc >> 1) & 0x7F;
|
||||
if ( feedback_bit == 0x01 ) {
|
||||
crc = crc | 0x80;
|
||||
}
|
||||
|
||||
data = data >> 1;
|
||||
bit_counter--;
|
||||
|
||||
} while (bit_counter > 0);
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
/*
|
||||
* calculate CRC8 on message_s data taking care of data structure and protocol version
|
||||
*/
|
||||
uint8_t Sensor::crc8Message(message_s var_msg) {
|
||||
uint8_t Sensor::crc8Message(message_s var_msg, uint8_t len) {
|
||||
uint8_t crc = 0x00;
|
||||
uint8_t loop_count;
|
||||
uint8_t bit_counter;
|
||||
uint8_t data;
|
||||
uint8_t feedback_bit;
|
||||
uint8_t number_of_bytes_to_read = (uint8_t)sizeof(var_msg);
|
||||
|
||||
// Must set crc to a constant value.
|
||||
var_msg.header.crc = 0;
|
||||
|
||||
// fill unused space by zeroes for string data only
|
||||
if(!var_msg.header.binary) {
|
||||
uint8_t len = strlen(var_msg.data);
|
||||
if(len < sizeof(var_msg.data)-1) {
|
||||
memset(&var_msg.data[len], 0, sizeof(var_msg.data) - 1 - len);
|
||||
}
|
||||
if(len>=0 && len < sizeof(var_msg.data)-1) {
|
||||
memset(&var_msg.data[len], 0, sizeof(var_msg.data) - 1 - len);
|
||||
}
|
||||
return crc8Raw((uint8_t*)&var_msg, (uint8_t)sizeof(var_msg));
|
||||
|
||||
|
||||
for (loop_count = 0; loop_count != number_of_bytes_to_read; loop_count++)
|
||||
{
|
||||
data = ((uint8_t*)&var_msg)[loop_count];
|
||||
|
||||
bit_counter = 8;
|
||||
do {
|
||||
feedback_bit = (crc ^ data) & 0x01;
|
||||
|
||||
if ( feedback_bit == 0x01 ) {
|
||||
crc = crc ^ 0x18; //0X18 = X^8+X^5+X^4+X^0
|
||||
}
|
||||
crc = (crc >> 1) & 0x7F;
|
||||
if ( feedback_bit == 0x01 ) {
|
||||
crc = crc | 0x80;
|
||||
}
|
||||
|
||||
data = data >> 1;
|
||||
bit_counter--;
|
||||
|
||||
} while (bit_counter > 0);
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
uint8_t Sensor::crc8Message(message_s var_msg) {
|
||||
struct {
|
||||
message_s msg;
|
||||
uint8_t protocol_version;
|
||||
} crc_data;
|
||||
memcpy(&crc_data, &var_msg, sizeof(var_msg));
|
||||
crc_data.protocol_version = PROTOCOL_VERSION;
|
||||
// some clean up needed for repeated result
|
||||
crc_data.msg.header.crc = 0;
|
||||
// fill unused space by zeroes for string data only
|
||||
if(!crc_data.msg.header.binary) {
|
||||
uint8_t len = strlen(crc_data.msg.data);
|
||||
if(len < sizeof(crc_data.msg.data)-1) {
|
||||
memset(&crc_data.msg.data[len], 0, sizeof(crc_data.msg.data) - 1 - len);
|
||||
}
|
||||
}
|
||||
return crc8Raw((uint8_t*)&crc_data, (uint8_t)sizeof(crc_data));
|
||||
} */
|
||||
uint8_t Sensor::validate(uint8_t length) {
|
||||
uint8_t oldCrc = msg.header.crc;
|
||||
uint8_t newCrc = crc8Message(msg, length);
|
||||
|
||||
if(!(msg.header.version == PROTOCOL_VERSION)) return VALIDATE_BAD_VERSION;
|
||||
|
||||
/*
|
||||
* true if message is consistent *
|
||||
*/
|
||||
bool Sensor::checkCRC(message_s var_msg) {
|
||||
uint8_t oldCrc = var_msg.header.crc;
|
||||
|
||||
//debug(PSTR("CRC: in %d, calc %d\n"),oldCrc, crc8Message(var_msg));
|
||||
|
||||
return (oldCrc == crc8Message(var_msg))?true:false;
|
||||
}
|
||||
|
||||
uint8_t Sensor::validate() {
|
||||
bool crc_check = checkCRC(msg);
|
||||
bool version_check = (msg.header.version == PROTOCOL_VERSION);
|
||||
if(!version_check) return VALIDATE_BAD_VERSION;
|
||||
if(!crc_check) return VALIDATE_BAD_CRC;
|
||||
if(!(oldCrc == newCrc)) return VALIDATE_BAD_CRC;
|
||||
return VALIDATE_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -45,15 +45,18 @@
|
||||
#define BASE_RADIO_ID ((uint64_t)0xABCDABC000LL)
|
||||
#define GATEWAY_ADDRESS ((uint8_t)0)
|
||||
#define BROADCAST_ADDRESS ((uint8_t)0xFF)
|
||||
#define TO_ADDR(x) (BASE_RADIO_ID + x)
|
||||
|
||||
#define MAX_MESSAGE_LENGTH 32
|
||||
|
||||
#define WRITE_PIPE ((uint8_t)0)
|
||||
#define CURRENT_NODE_PIPE ((uint8_t)1)
|
||||
#define BROADCAST_PIPE ((uint8_t)2)
|
||||
#define RELAY_PIPE ((uint8_t)3)
|
||||
|
||||
#define CRC8INIT 0x00
|
||||
#define CRC8POLY 0x18 //0X18 = X^8+X^5+X^4+X^0
|
||||
|
||||
#define WRITE_RETRY 5
|
||||
#define FIND_RELAY_RETRIES 20
|
||||
|
||||
|
||||
#define MAX_MESSAGE_LENGTH 32
|
||||
|
||||
// Message types
|
||||
typedef enum {
|
||||
@@ -235,7 +238,7 @@ class Sensor : public RF24
|
||||
/**
|
||||
* Validates consistency of the message including CRC and protocol version
|
||||
*/
|
||||
uint8_t validate();
|
||||
uint8_t validate(uint8_t length);
|
||||
|
||||
|
||||
#ifdef DEBUG
|
||||
@@ -257,7 +260,7 @@ class Sensor : public RF24
|
||||
void findRelay();
|
||||
boolean send(message_s message, int length);
|
||||
boolean sendWrite(uint8_t dest, message_s message, int length);
|
||||
message_s readMessage(void);
|
||||
boolean readMessage();
|
||||
void buildMsg(uint8_t from, uint8_t to, uint8_t childId, uint8_t messageType, uint8_t type, const char *data, uint8_t length, boolean binary);
|
||||
void sendInternal(uint8_t variableType, const char *value);
|
||||
boolean sendVariableAck();
|
||||
@@ -269,9 +272,7 @@ class Sensor : public RF24
|
||||
message_s ack; // Buffer for ack messages.
|
||||
|
||||
void initializeRadioId();
|
||||
uint8_t crc8Raw(uint8_t *data_in, uint8_t number_of_bytes_to_read);
|
||||
uint8_t crc8Message(message_s);
|
||||
bool checkCRC(message_s);
|
||||
uint8_t crc8Message(message_s, uint8_t length);
|
||||
char* get(uint8_t nodeId, uint8_t childId, uint8_t sendType, uint8_t receiveType, uint8_t variableType);
|
||||
char *getInternal(uint8_t variableType);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user