MeshNet v1.0.0
MeshNet is a sensory mesh network designed for microcontrollers with Arduino core. Nodes of the network are called MNodes. It isn't designed to transfer large amounts of data at high speed, but to collect data from sensor nodes. The code was optimized for microcontrollers with at least 20kB of RAM (tables and buffers cannot be further reduced :cry:) and at least 90kB of FLASH memory. It was tested on STM32L432KC microcontroller.
Features
- Can be used with any radio, that supports broadcasting.
- Contains at least one control node called Main Gateway Node (MGN).
- Scalability up to 251 nodes of which 1 to 3 nodes are gateway nodes.
- Data size of one packet from 1 to 512 bytes.
- Supports acknowledgment of receipt of the packet by destination node in conjunction with packet timeout.
- Supports multiple networks on same channel.
- Self-forming (with dynamic addressing) and self-healing network.
- Supports auto connect to specified network.
- Supports ping.
- Error reporting using callback functions.
- Implements user interface using SerialConsole.
- Routing protocol features:
- type: flat hybrid single-route routing protocol with hop-by-hop routing.
- route discovery method: link state -> whole network must be flooded. We want to improve this disadvantage.
- metric: hop-count -> which is not ideal for wireless mesh networks, but improvements will be made.
How to view documentation
- Download and install doxygen to your device.
- When you use:
- Windows - run makeDoc.bat file, it will create documentation to html directory and opens it. When you have already generated documentation directory (html), you can view it by opening index.html file in that directory.
- Linux or Mac - run
doxygen.exe
in console in folder to generate documentation. Open file in html/index.html
Integration with STM32 project
- Download or clone this code to folder with name
MeshNet
. - Open/create project in STM32CubeIDE.
- Right click to project > Convert to C++.
- Right click to project > New > Folder > Advanced and check radio button "Link to alternate location (Linked folder)", then click to Browse and select, where
MeshNet
folder is located. It is recommended to use variables in path. - Right click to project > Properties > C/C++ General > Paths and symbols > Source Location. Click to "Add Folder..." button and select
MeshNet
folder, then click OK. - Right click to project > Properties > C/C++ General > Paths and symbols > Includes. Click to Add button (check "Is a workspace path") and add those paths to GNU C and GNU C++ tab:
/${ProjName}/MeshNet/Mesh/Inc /${ProjName}/MeshNet/RadioInterfaces/Inc
- Right click to project > Properties > C/C++ General > Paths and symbols > Symbols. Click to Add button and add this symbol to GNU C and GNU C++ tab:
STM32
- Also check, if there is MCU type in symbols. For example, STM32L432kc type is STM32L432xx.
Interface with SerialConsole
SerialConsole is project, which allows easy implementation of console for microcontrollers using serial UART interface. With this console you can control and monitor MeshNet
class using specified commands or using GUI. Project with application for windows and linux (with mono) is located here.
Creating interface for custom radio
Almost any radio can be used with this mesh network, but those requirements has to be met:
- Broadcast transmission must be supported.
- The radio must be able to send at least 24 bytes at once. The more bytes it can send, the better.
- It must be possible to disable the auto acknowledgment and auto retransmit when ACK was not received. ACKs are handed by MeshNet.
Optional requirements for radio, which increases performance:
- Radio should contain FIFO buffer for received frames. Its size should be at least 3 rows.
- Radio may support hardware CRC calculation for each sent and received frame. If radio does not support CRC calculation, it must be done by software in radio driver and if it won't pass, frame must be discarded. At least CRC-16 must be used.
- Radio should fire interrupt when frame is received.
- Radio may be able to check if carriage wave is present. This is used for interference avoiding.
- Radio may be able to check RSSI of received frame. This is used during path discovery, to determine link quality.
- Radio should support low power modes.
To create custom radio interface first create inherited class from RFInterface
and continue with those steps:
- In its constructor call constructor of base class
RFInterface
with all arguments. Some of those arguments describes radio features and behavior. - Create destructor, where
deInit()
function is called. - In
init()
function setup radio. - In
deInit()
function disable or power off radio and unregister interrupt callback (set it toNULL
) if set. - Function
hasError()
has to return true, when radio has some error or is not connected to MCU. - In
send()
function perform frame transmission. It must return true when frame was sent without any error. It has to be checked here, if radio is connected and if not, it has return false. - In
available()
function check if any frame was received and return size of frame. If not, it must return 0. - In
recv()
function read received bytes from radio buffer and write it to buffer specified in arguments. If it failed, return false. If RSSI cannot be established, set it to -60dBm. - Create
uint8_t
array with same size asMaxMessageLength
and in functiongetInternalTemporaryBuffer()
return pointer to that array. - From this step, other steps are optional.
- Implement
sleep()
andwakeUp()
functions. As its name says, those functions should be able to temporary power off and power on radio. When the radio is in sleep mode, it may not be able to receive data. If not implemented, just return true in both functions. - Implement
checkInterference()
function, which returns true, when carried wave is present. - Implement
setChannel()
function to check specific channel. If channel cannot be changed, just return false. - Implement
setPower()
function, which can set power mode. If power modes cannot be set, return false. - Implement
setFullSpeed()
function, which has to be able to set low speed or full speed. Only two speed modes are supported. Return false if not implemented. - Implement
setReceiveInterruptCallback()
, which registers callback function, that is called when interrupt is fired because frame was received. If not implemented, return false. - Implement
interruptHandle()
function, which will be called in callback function from previous step. In this function flags in radio should be cleared. handle()
function is periodically called. It may contain a code for radio handling, which must be called often.- Function
printDetails()
may be able to print radio details in pretty form to specified stream. It is recommended to implement this function. - Implement
scanChannels()
function, which checks carrier wave on each channel and returns best channel number with the smallest chance of an interference. If stream is specified, it must print chance of an interference for each scanned channel. Format of printed data is strictly specified:Retries: <number of retries for each channel> Channel span: <count of channels scanned at one retry, in most cases 1> Base frequency: <frequency of first channel (ch. 0) in kHz> kHz Channel width: <channel width in kHz> kHz <channel number>: <interfered retries> <channel number>: <interfered retries> ...
Example for nRF24L01:
Retries: 50 Channel span: 1 Base frequency: 2400000 kHz Channel width: 1000 kHz 0: 2 1: 12 0: 0
- Implement
scanChannelsWorking()
function, which returns true if radio is busy, because scanning in progress. - Implement
getChannelsWidth()
which returns width of channels in kHz.
Configuration
Behavior of MeshNet
can be configured in meshConfig.h
file with those macros:
Size configuration
Packet payload size
MESH_PACKET_PAYLOAD_SIZE_LIMIT
- Size limit of packet payload. By default, set to 512 bytes, but can be set from 128 bytes to 1024 bytes. When changing this macro do not forget to changeMESH_PACKET_PAYLOAD_SIZE_LIMIT_txt
andMESH_PING_PAYLOAD_SIZE_LIMIT_txt
macros.
Tables sizes and life times
ROUTETABLE_SIZE
- count of rows in route table. By default, set to 64, but for gateway node it is recommended to set higher value. When changing this macro do not forget to changeROUTETABLE_SIZE_txt
andROUTETABLE_MAX_IND_txt
macros.ROUTETABLE_ROW_LIFETIME
- maximum lifetime of each row in route table in milliseconds. By default, set to 30 minutes.IDTABLE_SIZE
- count of rows in IDTable. By default, set to 64, but for gateway node it is recommended to set higher value. When changing this macro do not forget to changeIDTABLE_SIZE_txt
andIDTABLE_MAX_IND_txt
macros.IDTABLE_ROW_LIFETIME
- maximum lifetime of each row in IDTable in milliseconds. By default, set to 5 minutes.FIDTABLE_SIZE
- count of rows in FIDTable. By default, set to 8, but for gateway node it is recommended to set higher value. When changing this macro do not forget to changeFIDTABLE_SIZE_txt
andFIDTABLE_MAX_IND_txt
macros.FIDTABLE_ROW_LIFETIME
- maximum lifetime of each row in FIDTable in milliseconds. By default, set to 5 minutes.OIDTABLE_SIZE
- count of rows in OIDTable. By default, set to 64, but for gateway node it is recommended to set higher value. When changing this macro do not forget to changeOIDTABLE_SIZE_txt
andOIDTABLE_MAX_IND_txt
macros.DHCPTABLE_VALID_ROW_LIFETIME
- lifetime of each row in DHCP table in milliseconds. By default, set to one day. After this time, row stays in DHCP table for same time as life time, but is marked as reserved.
Buffers sizes
MESH_OFIFO_HIGH_SIZE
- size of high priority part (Route and Unicast packets) of output FIFO. By default, set to 3.MESH_OFIFO_LOW_SIZE
- size of low priority part (Flood and Broadcast packets) of output FIFO. By default, set to 4.MESH_WFIFO_SIZE
- size of waitingFIFO buffer. By default, set to 8.
Miscellaneous
MESH_INST_ARRAY_SIZE
- size of instance array. This array stores all created instances of current class. Number of created instances of MeshNet in this program cannot exceed this number. By default, set to 2, but can be set to higher value if multiple instances are defined on one MCU.
Timing
Frame receive timing
MESH_NEXT_FRAME_TIMEOUT
- timeout in milliseconds. If header frame was received and next frame is expected, until this timeout all other frames are ignored. By default, set to 40ms.
Frame send timing
MESH_ROUTE_RETR_TIME
- after this time in milliseconds routed frame (Type: Route or Unicast) can be retransmitted when ACK was not received. By default, set to 50ms.MESH_FRAME_SEND_DELAY
- after this time in milliseconds from last frame send, broadcasted frame (Type: Flood or Broadcast) can be sent. By default, set to 15ms.
Clear to send time
MESH_ACK_CTS_TIME
- this time in microseconds is added to CTS time, when ACK is expected. By default, set to 15ms.-
MESH_FRAME_CTS_TIME
- this time in microseconds is added to CTS time, when another frame is expected. By default, set to 20ms.Clear to send random time
MESH_RANDOM_ROUTED_CTS_TIME
- random value between 0 and this time in microseconds is added to CTS time, when routed frame (Type: Route or Unicast) was received. By default, set to 5ms.MESH_RANDOM_UNROUTED_CTS_TIME
- random value between 0 and this time in microseconds is added to CTS time, when broadcasted frame (Type: Flood or Broadcast) was received. By default, set to 10ms.
Miscellaneous
MESH_PATH_DISC_TIMEOUT
- timeout in milliseconds for path discovery. By default, set to 5 seconds.
Settings
Packet relaying limit
MESH_HOP_RELAY_LIMIT
- maximum count of hops, that can one packet do. After exceeding this limit, packet is discarded. By default, set to 25.
Retransmissions limits
MESH_ROUTE_RETR_CNT
- maximum count of routed frame (Type: Route or Unicast) retransmission when ACK was not received. When this limit exceeds, node is marked as inacessible. By default, set to 4, so frame can be sent in worst case 5 times.MESH_FLOOD_RETR_CNT
- count of guaranteed retransmittments for every broadcasted frame (Type: Flood or Broadcast). Those frames are broadcasted multiple times, because there is higher chance, that they will be received by all neighbor nodes. This is by default set to 1, so broadcasted frame will be sent 2 times. Set to 0 to disable retransmittment of broadcasted frames.
RSSI threshold
MESH_RSSI_THRESHOLD
- RSSI threshold. If RSSI of any frame (excepts ACK) exceeds this threshold it is ignored. RSSI threshold is by default set to -70dBm.
Connection settings
MESH_SYS_NET_SCAN_TIMEOUT
- system network scan timeout in milliseconds. By default, set to 1.5ms.MESH_CONNECT_TIMEOUT
- connect process timeout including system network scan before connection in milliseconds. By default, set to 7.5s.
Disconnection settings
MESH_DISCONNECT_TIMEOUT
- after this time in milliseconds node is definitely disconnected from network and all packets in FIFO for that network are cleared. By default, set to 5s.MESH_REQ_DISCONNECT_TIMEOUT
- until this time in milliseconds, node, the gateway requested disconnection, must respond with disconnect packet, which says, it is disconnecting. By default, set to 7.5s.
Miscellaneous
MESH_IMPL_CONSOLE_ITEM
- if set to 0, SerialConsole interface won't be implemented to MeshNet. This setting can significantly reduce FLASH memory usage. By default, set to 1.MESH_NO_STATISTICS
- if uncommented, statistics will not be recorded. This setting can slightly reduce RAM memory usage. By default, commented out.
File list
- Folder Mesh:
mesh.h
- Main file with the MeshNet class. This file must be included to useMeshNet
.meshNetGateway.h
- Main file with theMeshNetGateway
class, which is inherited from theMeshNet
class and implements functionality of MGN.meshBuffers.h
- File, which contains implementations of all used buffers in mesh protocol.meshTables.h
- File, which contains implementations of all used tables in mesh protocol.meshFrame.h
- Contains classMeshFrame
, which represents frames to be sent or received. Frames data part is dynamically allocated.meshPacket.h
- Contains classMeshPacket
, which represents packet, that are split to multiple frames, which are sent. Payload is dynamically allocated, same asMeshFrame
and its length can be up to 512B.meshPacketBuilder.h
- Contains classMeshPacketBuilder
, which builds packet from multiple received frames.meshPacketSplitter.h
- Contains classMeshPacketSplitter
, which splits packet to multiple frames, that are sent.meshPacketDecoders
- Contains functions, that decodes or encodes system packets fields.meshPacketFlags
- Contains structures, that represents flags inMeshPacket
orMeshFrame
headers and other flags used in code.meshConfig.h
- Configuration file.meshHelper.h
- File, which contains helper function or classes forMeshNet
. For example,MeshMAC
is located here.meshVariableArray.h
- File, which contains class, that behaves as dynamically allocated array. This array can be used to create packet payload to be send. Best performance can be achieved when usingsend()
method in conjunction withstd::move
andMeshVariableArray
with payload data.
- Folder RadioInterfaces:
RFInterface.h
- Contains abstract class, that act as interface with almost any radio.NRF24L01_interface.h
- Contains class inherited from theRFInterface
. This class acts as interface with nRF24L01 or nRF24L01+ radio.Simulation_interface.h
- Contains class inherited from theRFInterface
. This class acts as interface with virtual radio, which is used with MeshProtocolSimulator.
Examples
Simple example of basic node with nRF24L01+, which sends data over network:
Macro
MESH_IMPL_CONSOLE_ITEM
inmeshConfig.h
must be set to 0//Includes for STM32 #include "wirish.h" //On STM32 arduino core (framework) must be included #include "HardwareSPI.h" //Includes for Arduino //#include <SPI.h>
include "mesh.h"
include "NRF24L01_interface.h"
//On STM32 in STM32CubeIDE hspi1 is imported from main.c and enabled in .ioc file //On arduino comment out next two lines extern SPI_HandleTypeDef hspi1; HardwareSPI SPI(hspi1);
RF24 radio(5, 3); //Creating nRF24L01+ driver instance NRF24L01_RFI intf(radio, SPI); //Creating wrapper instance MeshNet mesh; //Creating MeshNet instance
//Called in main.c before infinity loop void setup(){ SPI.begin(); //Enabling SPI needed for nRF24L01+ mesh.setChannel(76); //Setting custom channel //Starting node with MeshMAC address 00-00-00-05 mesh.begin(MeshMAC(5), intf); //Setting auto connect to network with BSSID 00-00-00-01 //with retry every 30 seconds mesh.setAutoConnect(MeshMAC(1), 30000); }
uint32_t nextGreetingTime = 0;
//Called in main.c inside infinity loop void loop(){ //Sending data only if connected if(mesh.isConnected() && nextGreetingTime < millis()){ const uint8_t data[5] = {'H', 'e', 'l', 'l', 'o'}; mesh.send(0, data, 5); //Sending data to gateway node with address 0 nextGreetingTime = millis() + 10000; //Wait 10 seconds } mesh.handle(); //Handling mesh node code }
Simple example of main gateway node with nRF24L01+, which received data from network:
> Macro `MESH_IMPL_CONSOLE_ITEM` in `meshConfig.h` must be set to 0
```c++
#include "wirish.h" //On STM32 arduino core (framework) must be included
#include "HardwareSPI.h"
#include "HardwareSerial.h"
//Includes for Arduino
//#include <SPI.h>
//#include <Serial.h>
#include "meshNetGateway.h"
#include "NRF24L01_interface.h"
//On STM32 in STM32CubeIDE hspi1 and huart1 are imported from main.c and enabled in .ioc file
//On arduino comment out next four lines
extern SPI_HandleTypeDef hspi1;
HardwareSPI SPI(hspi1);
extern UART_HandleTypeDef huart1;
HardwareSerial Serial(huart1);
RF24 radio(5, 3); //Creating nRF24L01+ driver instance
NRF24L01_RFI intf(radio, SPI); //Creating wrapper instance
MeshNetGateway mesh; //Creating MeshNetGateway instance
void onReceive(MeshNet* mesh, MeshPacket& packet){
//Reporting, that packet was received
Serial.print("Received packet from ");
Serial.print((int)packet.PacketHeader.Source);
Serial.print(" with payload: ");
Serial.write(packet.p_ptr(), packet.Length());
Serial.println();
}
//Called in main.c before infinity loop
void setup(){
SPI.begin(); //Enabling SPI needed for nRF24L01+
Serial.begin(115200); //Enabling UART for printing
mesh.setChannel(76); //Setting custom channel
//Starting gateway node with MeshMAC address
//and BSSID 00-00-00-01
//and with SSID "My network"
mesh.begin(MeshMAC(1), intf, "My network");
//Setting callback function, that is called when packet is received
mesh.setOnReceive(onReceive);
}
//Called in main.c inside infinity loop
void loop(){
mesh.handle(); //Handling mesh node code
}
Simple example of basic node with nRF24L01+, which is controlled using SerialConsole interface through serial port:
Macro
MESH_IMPL_CONSOLE_ITEM
inmeshConfig.h
must be set to 1
//Includes for STM32
#include "wirish.h" //On STM32 arduino core (framework) must be included
#include "HardwareSPI.h"
#include "HardwareSerial.h"
//Includes for Arduino
//#include <SPI.h>
//#include <Serial.h>
#include "SerialConsole.h"
#include "mesh.h"
#include "NRF24L01_interface.h"
//On STM32 in STM32CubeIDE hspi1 and huart1 are imported from main.c and enabled in .ioc file
//On arduino comment out next four lines
extern SPI_HandleTypeDef hspi1;
HardwareSPI SPI(hspi1);
extern UART_HandleTypeDef huart1;
HardwareSerial Serial(huart1);
RF24 radio(5, 3); //Creating nRF24L01+ driver instance
NRF24L01_RFI intf(radio, SPI); //Creating wrapper instance
MeshNet mesh; //Creating MeshNet instance
SerialConsole console(Serial, "My console"); //Creating SerialConsole
//Called in main.c before infinity loop
void setup(){
SPI.begin(); //Enabling SPI needed for nRF24L01+
mesh.setChannel(76); //Setting custom channel
//Starting node with MeshMAC address 00-00-00-05
mesh.begin(MeshMAC(5), intf);
console.begin(); //Enabling SerialConsole
console.addConsoleItem(mesh); //Adding mesh as ConsoleItem to the SerialConsole
//Setting auto connect to network with BSSID 00-00-00-01
//with retry every 30 seconds
mesh.setAutoConnect(MeshMAC(1), 30000);
}
uint32_t nextGreetingTime = 0;
//Called in main.c inside infinity loop
void loop(){
console.handle(); //Handling SerialConsole code
mesh.handle(); //Handling mesh node code
}
License
Copyright 2022 Matej Fitoš
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
Author
Created by Matej Fitoš
Copyright © 2022 Matej Fitoš

