User Tools

Site Tools


digispark:tutorials:nrfmesh

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Next revision
Previous revision
digispark:tutorials:nrfmesh [2015/01/12 21:46]
digistump created
digispark:tutorials:nrfmesh [2016/06/09 12:03] (current)
Line 9: Line 9:
  
 This example implements a simple, easy to understand and change, Mesh network layer over the RF24 library. The goal of this is to be lightweight for relatively small meshes, and relatively easy to set up. While a more complex mesh layer might allow for ad hoc additions to the network - for simplicity this requires the total number of nodes (or max number at least - they don't all have to be on) to be known. That is all - addressing is automatic as long as you change the NODE_ADDRESS to be unique for each node. This example implements a simple, easy to understand and change, Mesh network layer over the RF24 library. The goal of this is to be lightweight for relatively small meshes, and relatively easy to set up. While a more complex mesh layer might allow for ad hoc additions to the network - for simplicity this requires the total number of nodes (or max number at least - they don't all have to be on) to be known. That is all - addressing is automatic as long as you change the NODE_ADDRESS to be unique for each node.
 +
 +Note: NODE_ADDRESS is 0 indexed so your first node is 0 NOT 1
  
 **Data Structure** **Data Structure**
 +
 For simplicity this implements a data structure in the RF24 payload. That structure is defined in the example as: For simplicity this implements a data structure in the RF24 payload. That structure is defined in the example as:
-<​code>​+<​code ​arduino>
 struct ​ struct ​
 { {
Line 26: Line 29:
  
 **Repeating without looping forever** **Repeating without looping forever**
 +
 This mesh layer expects any node the receives a message (they all listen for any message by listening all on the same channel) to re-transmit the message if that node was not the node in the toAddress. In order to ensure that a node doesn'​t keep re-transmitting a message endlessly we could use some sort of ACK system (complex) or more simply (but less robust) we can record the last message we repeated based on the from address and not repeat it again - which is what we do here. This mesh layer expects any node the receives a message (they all listen for any message by listening all on the same channel) to re-transmit the message if that node was not the node in the toAddress. In order to ensure that a node doesn'​t keep re-transmitting a message endlessly we could use some sort of ACK system (complex) or more simply (but less robust) we can record the last message we repeated based on the from address and not repeat it again - which is what we do here.
  
Line 32: Line 36:
 This results in as many duplicates of the message as there are nodes if the nodes are all close to each other (each message would be duplicated once by each node) - but if they are say in a line where each node is just in range of the one in front and behind it then we end up with no more than are necessary to get to the destination. This results in as many duplicates of the message as there are nodes if the nodes are all close to each other (each message would be duplicated once by each node) - but if they are say in a line where each node is just in range of the one in front and behind it then we end up with no more than are necessary to get to the destination.
  
 +This mesh layer also implements a broadcast address at 255 so if you do sendToMesh(255,​ analogRead(A5));​ all nodes that receive the message will repeat it and read it! (This of a mesh of motion detectors with alarms - if one detects motion it sends a message on broadcast to all nodes to turn on their alarms).
 +
 +**The Mesh Functions:​**
 +
 +These two main functions are well commented and cover most of how this works on the code level:
 +
 +<code arduino>
 +void sendToMesh(uint8_t toAddress, long data){
 +  if(sequence[NODE_ADDRESS]<​255)
 +    sequence[NODE_ADDRESS]++;​ //increment sequence count for this device
 +  else
 +    sequence[NODE_ADDRESS] = 0; //set to zero if last was 255 so we don't overflow - logic for read is built to handle this too
 +    ​
 +  msg.toAddress = toAddress; //set the address of the destination node
 +  msg.fromAddress = NODE_ADDRESS;​ //set the from as this node - of course
 +  msg.fromSequence = sequence[NODE_ADDRESS];​ //set it to the sequence number we just implemented which should be greater than any the nodes have received ​
 +  radio.stopListening();​ //turn of recv so we can transmit
 +  radio.write(&​msg,​ sizeof(msg));​
 +  radio.startListening();​ //turn recv back on
 +}
 +</​code>​
 +
 +<code arduino>
 +bool readAndRepeat(){
 +  if(radio.read(&​msg,​ sizeof(msg))){ //if we have incoming data
 +    if(msg.fromAddress!=NODE_ADDRESS){ //make sure this node didn't send it
 +      if(sequence[msg.fromAddress] < msg.fromSequence || (sequence[msg.fromAddress] == 255 && msg.fromSequence == 0)){ //make sure we haven'​t already repeated it or received it
 +          //increment sequence for that address so we don't repeat it twice from this node or receive it twice
 +          sequence[msg.fromAddress] = msg.fromSequence;​
 +          if(msg.toAddress==NODE_ADDRESS){ //is it for this node? if so return true so we can use it!
 +            return true;
 +          }
 +          //otherwise repeat it - send it back out
 +          radio.write(&​msg,​ sizeof(msg));​
 +          if(msg.toAddress == 255){ //it was a broadcast so return true so we do something with it
 +            return true;
 +          }
 +      }
 +    }
 +  }
 +  return false;
 +}
 +</​code>​
 +
 +
 +**Topography:​**
 +
 +This mesh layer can be used with many different topographies - such as:
 +
 +Master and Nodes - One Node is the master connected to a screen/​computer/​etc (think of it as the server) the rest are just nodes sending data back to the master and repeating each others data so that it can get to the master. Example: whole house temperature monitors with the master connected to the internet. This is the most common setup and what our example below uses.
 +
 +Ad hoc Nodes - Each node reports different things addressed to different nodes - say you have node A, B and C. Node A reports the temperature to C. B reports the voltage it sees to C and A and C reports its switch input state to B. Example: Machines in a factory where they need to react to what other machines are doing/​reading,​ but each differently to different values. This is less common but really neat!
 +
 +Multi master - multi node: A mix of the two - say you have A,B,C, and D - maybe all report their temperature to A including D, but they all (including A) report their voltage to D.
 +
 +Obviously the mix of topographies is endless - this should support most of them because it does not apply any limits to topography (at the expense of optimization) ​
 +
 +===== Generic Example Code: =====
 +
 +Note: This is generic example code that must be edited to work with your setup and desired function - see the example below this for a guided working example. ​
 +
 + 
 +<code arduino>
 +#include <​SPI.h>​
 +#include "​nRF24L01.h"​
 +#include "​RF24.h"​
 +
 +//THESE MUST BE SET! CHANGE ADDRESS AS YOU UPLOAD TO EACH NODE!
 +#define TOTAL_NODES 3 //TOTAL NUMBER OF NODES IN MESH
 +#define NODE_ADDRESS 0 //the sero indexed address of this node - nodes must be numbered 0 thru TOTAL_NODES-1 //also note 255 is broadcast address
 +
 +//hold the seuqnce numbers to avoid repeating the same messages
 +uint8_t ​ sequence[TOTAL_NODES];​
 +
 +//setup radio module
 +RF24 radio(9,​12);​
 +
 +//message format - adjust data as needed ​ - the rest is madatory for this mesh to work
 +struct ​
 +{
 + char toAddress;
 + char fromAddress;​
 + char fromSequence;​
 + long data;
 +} msg;
 + 
 +//​don'​t change! read is same for all nodes, write is automatically calculated here
 +const uint64_t writePipe = 0xF0F0F0F00LL + NODE_ADDRESS;​
 +const uint64_t readPipe = 0xF0F0F0F0D2LL;​
 + 
 + 
 +void setup() {
 +  // put your setup code here, to run once:
 +  radio.begin();​
 +  radio.setDataRate(RF24_250KBPS);​ //lowest speed = most range
 +  radio.setAutoAck(false);​ //this is a mesh so we don't want ACKs!
 +  radio.setRetries(15,​ 15);
 +  radio.setPayloadSize(sizeof(msg));​
 +  radio.openWritingPipe(writePipe);​
 +  radio.openReadingPipe(1,​ readPipe);
 +  radio.startListening();​
 +}
 +
 +long now = 0;
 + 
 +void loop() {
 +  ​
 +
 +  ​
 +  if(readAndRepeat()){ //will repeat messages as needed and return false unless there is packet for this node - CALL FREQUENTLY!
 +    //if this does not return false then we have a packet for THIS node!
 +    //​msg.fromAddress is the node that sent it
 +    //msg.data is the data itself
 +    //Do something with it!
 +    //For example display packets coming to this node on a LCD:
 +    /*
 +    NOTE: TO USE THIS ADD THE LCD INCLUDES AND SETUP ROUTINE FROM THE DigisparkLCD example  ​
 +    lcd.clear();​
 +    lcd.print("​From:​ ");
 +    lcd.println(msg.fromAddress);​
 +    lcd.print("​Value:​ ");
 +    lcd.println(msg.data);​
 +    */
 +  }
 +  ​
 +  if(millis() - now > 10000){ //send a packet from this node to the mesh every 10 seconds but wait in a non-blocking way so that we can still run this loop and repeat things
 +    now = millis(); //set now to millis so we wait another 10 seconds before sending again
 +    //​sendToMesh(To_Address,​Data_To_Send);​
 +    //​sendToMesh(0,​ analogRead(A5));​ //send to node 0 the analog read value of pin 5 - could also send a temp sensor value, etc ,etc 
 +    //​sendToMesh(255,​ analogRead(A5));​ //send to all nodes (255 is the broadcast address) the analog read value of pin 5
 +  }
 + 
 +}
 + 
 + 
 +void sendToMesh(uint8_t toAddress, long data){
 +  if(sequence[NODE_ADDRESS]<​255)
 +    sequence[NODE_ADDRESS]++;​ //increment sequence count for this device
 +  else
 +    sequence[NODE_ADDRESS] = 0; //set to zero if last was 255 so we don't overflow - logic for read is built to handle this too
 +    ​
 +  msg.toAddress = toAddress; //set the address of the destination node
 +  msg.fromAddress = NODE_ADDRESS;​ //set the from as this node - of course
 +  msg.fromSequence = sequence[NODE_ADDRESS];​ //set it to the sequence number we just implemented which should be greater than any the nodes have received ​
 +  radio.stopListening();​ //turn of recv so we can transmit
 +  radio.write(&​msg,​ sizeof(msg));​
 +  radio.startListening();​ //turn recv back on
 +}
 +
 +bool readAndRepeat(){
 +  if(radio.read(&​msg,​ sizeof(msg))){ //if we have incoming data
 +    if(msg.fromAddress!=NODE_ADDRESS){ //make sure this node didn't send it
 +      if(sequence[msg.fromAddress] < msg.fromSequence || (sequence[msg.fromAddress] == 255 && msg.fromSequence == 0)){ //make sure we haven'​t already repeated it or received it
 +          //increment sequence for that address so we don't repeat it twice from this node or receive it twice
 +          sequence[msg.fromAddress] = msg.fromSequence;​
 +          if(msg.toAddress==NODE_ADDRESS){ //is it for this node? if so return true so we can use it!
 +            return true;
 +          }
 +          //otherwise repeat it - send it back out
 +          radio.write(&​msg,​ sizeof(msg));​
 +          if(msg.toAddress == 255){ //it was a broadcast so return true so we do something with it
 +            return true;
 +          }
 +      }
 +    }
 +  }
 +  return false;
 +}
 +
 +
 +
 +</​code>​
 +
 +===== Master and Nodes Example: =====
 +
 +This example uses 3 Nodes - We will call them Node 0, 1, and 2 - Node 0 is the master and should be connected to a computer - it will use the USB keyboard library to type out any messages it receives.
 +
 +Node 1 and 2 send messages every 10 seconds of the analog Read value of their Pin 5 - they repeat messages for each other - that is all they do. They do not need to be attached to a computer once programmed - just powered on with at least one in range of the master.
 +
 +Put this code on Node 0 which will be the master:
 +
 +<code arduino>
 +#include <​SPI.h>​
 +#include "​nRF24L01.h"​
 +#include "​RF24.h"​
 +
 +//THESE MUST BE SET! CHANGE ADDRESS AS YOU UPLOAD TO EACH NODE!
 +#define TOTAL_NODES 3 //TOTAL NUMBER OF NODES IN MESH
 +#define NODE_ADDRESS 0 //the sero indexed address of this node - nodes must be numbered 0 thru TOTAL_NODES-1 //also note 255 is broadcast address
 +
 +//hold the seuqnce numbers to avoid repeating the same messages
 +uint8_t ​ sequence[TOTAL_NODES];​
 +
 +//setup radio module
 +RF24 radio(9,​12);​
 +
 +//message format - adjust data as needed ​ - the rest is madatory for this mesh to work
 +struct ​
 +{
 + char toAddress;
 + char fromAddress;​
 + char fromSequence;​
 + long data;
 +} msg;
 + 
 +//​don'​t change! read is same for all nodes, write is automatically calculated here
 +const uint64_t writePipe = 0xF0F0F0F00LL + NODE_ADDRESS;​
 +const uint64_t readPipe = 0xF0F0F0F0D2LL;​
 + 
 + 
 +void setup() {
 +  // put your setup code here, to run once:
 +  radio.begin();​
 +  radio.setDataRate(RF24_250KBPS);​ //lowest speed = most range
 +  radio.setAutoAck(false);​ //this is a mesh so we don't want ACKs!
 +  radio.setRetries(15,​ 15);
 +  radio.setPayloadSize(sizeof(msg));​
 +  radio.openWritingPipe(writePipe);​
 +  radio.openReadingPipe(1,​ readPipe);
 +  radio.startListening();​
 +}
 +
 +long now = 0;
 + 
 +void loop() {
 +  ​
 +
 +  ​
 +  if(readAndRepeat()){ //will repeat messages as needed and return false unless there is packet for this node - CALL FREQUENTLY!
 +    //if this does not return false then we have a packet for THIS node!
 +    //​msg.fromAddress is the node that sent it
 +    //msg.data is the data itself
 +    //Do something with it!
 +    //For example type out the packets coming to this node
 +    DigiKeyboard.println(msg.fromAddress);​
 +    DigiKeyboard.println(msg.data);​
 +
 +  }
 +  ​
 +  /
 +  /*
 +  We don't do any sending because this node is the master and just receives things!
 +   ​if(millis() - now > 10000){ //send a packet from this node to the mesh every 10 seconds but wait in a non-blocking ​  way so that we can still run this loop and repeat things
 +    now = millis(); //set now to millis so we wait another 10 seconds before sending again
 +    //​sendToMesh(To_Address,​Data_To_Send);​
 +    //​sendToMesh(0,​ analogRead(A5));​ //send to node 0 the analog read value of pin 5 - could also send a temp sensor value, etc ,etc 
 +    //​sendToMesh(255,​ analogRead(A5));​ //send to all nodes (255 is the broadcast address) the analog read value of pin 5
 +  }
 +  */
 +  ​
 +  DigiKeyboard.delay(100);//​give the usb some time to poll
 + 
 +}
 + 
 + 
 +void sendToMesh(uint8_t toAddress, long data){
 +  if(sequence[NODE_ADDRESS]<​255)
 +    sequence[NODE_ADDRESS]++;​ //increment sequence count for this device
 +  else
 +    sequence[NODE_ADDRESS] = 0; //set to zero if last was 255 so we don't overflow - logic for read is built to handle this too
 +    ​
 +  msg.toAddress = toAddress; //set the address of the destination node
 +  msg.fromAddress = NODE_ADDRESS;​ //set the from as this node - of course
 +  msg.fromSequence = sequence[NODE_ADDRESS];​ //set it to the sequence number we just implemented which should be greater than any the nodes have received ​
 +  radio.stopListening();​ //turn of recv so we can transmit
 +  radio.write(&​msg,​ sizeof(msg));​
 +  radio.startListening();​ //turn recv back on
 +}
 +
 +bool readAndRepeat(){
 +  if(radio.read(&​msg,​ sizeof(msg))){ //if we have incoming data
 +    if(msg.fromAddress!=NODE_ADDRESS){ //make sure this node didn't send it
 +      if(sequence[msg.fromAddress] < msg.fromSequence || (sequence[msg.fromAddress] == 255 && msg.fromSequence == 0)){ //make sure we haven'​t already repeated it or received it
 +          //increment sequence for that address so we don't repeat it twice from this node or receive it twice
 +          sequence[msg.fromAddress] = msg.fromSequence;​
 +          if(msg.toAddress==NODE_ADDRESS){ //is it for this node? if so return true so we can use it!
 +            return true;
 +          }
 +          //otherwise repeat it - send it back out
 +          radio.write(&​msg,​ sizeof(msg));​
 +          if(msg.toAddress == 255){ //it was a broadcast so return true so we do something with it
 +            return true;
 +          }
 +      }
 +    }
 +  }
 +  return false;
 +}
 +
 +
 +
 +</​code>​
 +
 +**Nodes 1 and 2:** These are the sending nodes. They send the analog value of Pin 5. Load this on to the other two Nodes changing NODE_ADDRESS to 2 for the 3rd node (which is node #2 because the addresses are zero indexed)
 +
 +<code arduino>
 +#include <​SPI.h>​
 +#include "​nRF24L01.h"​
 +#include "​RF24.h"​
 +
 +//THESE MUST BE SET! CHANGE ADDRESS AS YOU UPLOAD TO EACH NODE!
 +#define TOTAL_NODES 3 //TOTAL NUMBER OF NODES IN MESH
 +#define NODE_ADDRESS 1 //the sero indexed address of this node - nodes must be numbered 0 thru TOTAL_NODES-1 //also note 255 is broadcast address
 +
 +//hold the seuqnce numbers to avoid repeating the same messages
 +uint8_t ​ sequence[TOTAL_NODES];​
 +
 +//setup radio module
 +RF24 radio(9,​12);​
 +
 +//message format - adjust data as needed ​ - the rest is madatory for this mesh to work
 +struct ​
 +{
 + char toAddress;
 + char fromAddress;​
 + char fromSequence;​
 + long data;
 +} msg;
 + 
 +//​don'​t change! read is same for all nodes, write is automatically calculated here
 +const uint64_t writePipe = 0xF0F0F0F00LL + NODE_ADDRESS;​
 +const uint64_t readPipe = 0xF0F0F0F0D2LL;​
 + 
 + 
 +void setup() {
 +  // put your setup code here, to run once:
 +  radio.begin();​
 +  radio.setDataRate(RF24_250KBPS);​ //lowest speed = most range
 +  radio.setAutoAck(false);​ //this is a mesh so we don't want ACKs!
 +  radio.setRetries(15,​ 15);
 +  radio.setPayloadSize(sizeof(msg));​
 +  radio.openWritingPipe(writePipe);​
 +  radio.openReadingPipe(1,​ readPipe);
 +  radio.startListening();​
 +}
 +
 +long now = 0;
 + 
 +void loop() {
 +  ​
 +
 +  ​
 +  if(readAndRepeat()){ //will repeat messages as needed and return false unless there is packet for this node - CALL FREQUENTLY!
 +    //if this does not return false then we have a packet for THIS node!
 +    //​msg.fromAddress is the node that sent it
 +    //msg.data is the data itself
 +    /*
 +       These nodes just send data so we don't do anything here for this example
 +       we still need to call readAndRepeat though because we still need to repeat ​
 +       ​packets for other nodes!
 +    */
 +  }
 +  ​
 +  if(millis() - now > 10000){ //send a packet from this node to the mesh every 10 seconds but wait in a non-blocking way so that we can still run this loop and repeat things
 +    now = millis(); //set now to millis so we wait another 10 seconds before sending again
 +    //​sendToMesh(To_Address,​Data_To_Send);​
 +    sendToMesh(0,​ analogRead(A5));​ //send to node 0 the analog read value of pin 5 - could also send a temp sensor value, etc ,etc 
 +  }
 + 
 +}
 + 
 + 
 +void sendToMesh(uint8_t toAddress, long data){
 +  if(sequence[NODE_ADDRESS]<​255)
 +    sequence[NODE_ADDRESS]++;​ //increment sequence count for this device
 +  else
 +    sequence[NODE_ADDRESS] = 0; //set to zero if last was 255 so we don't overflow - logic for read is built to handle this too
 +    ​
 +  msg.toAddress = toAddress; //set the address of the destination node
 +  msg.fromAddress = NODE_ADDRESS;​ //set the from as this node - of course
 +  msg.fromSequence = sequence[NODE_ADDRESS];​ //set it to the sequence number we just implemented which should be greater than any the nodes have received ​
 +  radio.stopListening();​ //turn of recv so we can transmit
 +  radio.write(&​msg,​ sizeof(msg));​
 +  radio.startListening();​ //turn recv back on
 +}
  
-<code>+bool readAndRepeat(){ 
 +  if(radio.read(&​msg,​ sizeof(msg))){ //if we have incoming data 
 +    if(msg.fromAddress!=NODE_ADDRESS){ //make sure this node didn't send it 
 +      if(sequence[msg.fromAddress] ​msg.fromSequence || (sequence[msg.fromAddress] == 255 && msg.fromSequence == 0)){ //make sure we haven'​t already repeated it or received it 
 +          //increment sequence for that address so we don't repeat it twice from this node or receive it twice 
 +          sequence[msg.fromAddress] = msg.fromSequence;​ 
 +          if(msg.toAddress==NODE_ADDRESS){ //is it for this node? if so return true so we can use it! 
 +            return true; 
 +          } 
 +          //otherwise repeat it - send it back out 
 +          radio.write(&​msg,​ sizeof(msg));​ 
 +          if(msg.toAddress == 255){ //it was a broadcast so return true so we do something with it 
 +            return true; 
 +          } 
 +      } 
 +    } 
 +  } 
 +  return false; 
 +}
  
  
  
 </​code>​ </​code>​
digispark/tutorials/nrfmesh.1421128010.txt.gz · Last modified: 2015/01/12 21:46 by digistump