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:
Henrik Ekblad
2014-02-05 00:30:32 +01:00
parent 25c7f386db
commit 1ae8353d13
6 changed files with 124 additions and 137 deletions
+2
View File
@@ -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!
+4 -2
View File
@@ -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);
}
+1 -1
View File
@@ -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();
+1 -7
View File
@@ -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
View File
@@ -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;
}
+11 -10
View File
@@ -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);
};