Added sources, library properties and example code.

This commit is contained in:
Marcel Schulz 2017-11-20 16:54:06 +01:00
parent b11766a5cf
commit 0c8e8582b9
11 changed files with 1099 additions and 4 deletions

View File

@ -1,11 +1,16 @@
# SBNetwork
Schullebernd Network library is a simple and easy to use library to connect devices like arduinos or ESP8266 via a nRF24L01 Transmitter.
It uses the basic RF24 lib from https://tmrh20.github.io/RF24 and add further functions for a simple master/client network.
It uses the basic RF24 lib from https://tmrh20.github.io/RF24 and adds further functions for a simple master/client network.
## Master Client Network
With this library you can build a simple master/client network.
One device will be defined as master device. The master device is like a basic station and all clients are connected to it.
Usually the master device should have a connection to the LAN or WLAN (e.g. by using an ESP8266) and can forward communication from or to the clients (e.g. MQTT-Client or own web interface).
The clients devices are connecting (pairing) to a master device. Typical clients are sensors or actors in a smart home automation environment (e.g. temperature sensor).
One device will be defined as a master device. All other devices are clients and connecting to the master. The master device is like a basic station (e.g. weather station with many sensors).
Usually the master device should have a connection to the LAN or WLAN (e.g. by using an ESP8266) and can forward communication from or to the clients (e.g. MQTT-Client or build in web interface). The client devices are (automatically) connecting to (pairing with) the master device. Typical clients are sensors or actors in a smart home automation environment (e.g. temperature sensor).
## Target of this library
The target of this library is tobuild up a small and easy closed network of devices. I simply wanted to have a library for an easy integration of multible sensor and actor devices. But the devices shouldn't be a kind of an IoT device, which are directly connected to the LAN or internet. I know this is not a typical IoT thinking, but this definitifly reduced the overhead communication in the local LAN/WLAN. The target of this library is explicitly NOT to have TCP/IP library for nRF24L01 connected devices. Ther are many really good libraries available to achive this.
# How does it work?
It's quite simple. One device is defined as the master device. In my case it is an Wemos D1 mini / ESP826. The master devices is the device where the clients are connecting to. The master can allow or deny new client connections. If a client gets connected to the master, the master stores its mac in the internal flash. If a client is sending transmissions to a master and the master has not stored the clients mac address, the communication will be blocked. Clients are automatically searching for and connecting to a master. You don't have to configure this. The only thing that has to be done is to define a unique mac adress for the clients in the sketch. If a client is connected to a master the first time, it stores the master mac in the internal flash. Even if the client loses the power or turnes off, the master mac gets stored. After turning on the client again, the connection to the master gets restored .

View File

@ -0,0 +1,83 @@
/*
* Author - Marcel Schulz (alias Schullebernd)
* *************************************************************
* See further library details on https://github.com/Schullebernd/SBNetwork
* *************************************************************
* This Getting started is prepared for using it with a Arduino as a client device.
* If you want to use a Wemos, then change the line "SBNetwork networkDevice(6, 7);" to the correct pinout for a Wemos (D2, D8).
* To get shure, that the SBNetwork library is build for a Client-Device, open the SBNetwork_config.h in the libraries folder and get shure that line 6 is commented out.
* //#define RUN_AS_MASTER
* **************************************************************
* Step 1 - Prepare your device
* Connect a nRF24L01 transmitter to the Arduino or to a Wemos D1 mini.
* WEMOS > RF24 ARDUINO > RF24 ---------------------------------------
* ------------ -------------- | GND # # VCC TOP VIEW |
* 3V3 > VCC VCC > VCC | CE # # CSN OF nRF24L01 |
* GND > GND GND > GND | SCK # # MOSI |
* D2 > CE 6 > CE | MISO # # IRQ |
* D8 > CSN 7 > CSN | |
* D7 > MOSI 11 > MOSI ---------------------------------------
* D6 > MISO 12 > MISO
* D5 > SCK 13 > SCK
*
* Step 2 - Build the sketch for the client device
* Open the file SBNetwork_config.h in the library folder ../libraries/SBNetwork/src/SBNetwork_config.h and comment out (put two // at the line start) the line 6.
* Line 6 should now look like this //#define RUN_AS_MASTER
* Connect the Arduino via USB to the PC, select the right board and COM interface in the tools menu and run the project.
* After building and loading up to the Arduino, the serial monitor should show some log data.
* If you already run a master device you should now see the pairing process in the serial montor for both devices.
*/
#include <SBNetwork_config.h>
#include <SBNetwork.h>
// Type in here the mac address of the device.
// This must be unique within your complete network otherwise the network will not work.
//SBMacAddress deviceMac(0x01, 0x02, 0x03, 0x04, 0x05);
SBMacAddress deviceMac(0x05, 0x04, 0x03, 0x02, 0x01);
// Create a new network device with Wemos D1 mini and set the _ce and _cs pin
//SBNetwork networkDevice(D2, D8);
SBNetwork networkDevice(6, 7);
void setup() {
Serial.begin(19200);
Serial.println(F("*** PRESS 'N' to reset the device"));
// Initialize the network device
networkDevice.initialize(deviceMac);
}
// time variables
uint32_t wait = 4000;
uint32_t lastWait = wait;
void loop() {
if (Serial.available())
{
char c = toupper(Serial.read());
if (c == 'N') {
networkDevice.resetData();
}
}
// Call this in the loop() function to maintain the network device
networkDevice.update();
// Check, if there are messages available
uint8_t messageSize = networkDevice.available();
if (messageSize > 0) {
// Read the message from network device and print out the text.
Serial.print(F("Received Content: "));
Serial.println((char*)networkDevice.getMessage());
}
// Send a message to the master every 4 seconds
if (lastWait < millis()) {
char* message = "Hello Master, here is your client, can you hear me?";
networkDevice.sendToDevice(networkDevice.NetworkDevice.MasterMAC, message, strlen(message) + 1);
lastWait += wait;
}
} // Loop

View File

@ -0,0 +1,81 @@
/*
* Author - Marcel Schulz (alias Schullebernd)
* *************************************************************
* See further library details on https://github.com/Schullebernd/SBNetwork
* *************************************************************
* This Getting started is prepared for using it with a Wemos D1 mini as a master device.
* If you want to use an Arduino, then change the line "SBNetwork networkDevice(D2, D8);" to the correct pinout for an Arduino (6,7).
* To get shure, that the SBNetwork library is build for a Master-Device open the SBNetwork_config.h in the libraries folder and get shure that line 6 is not commented out.
* #define RUN_AS_MASTER
* **************************************************************
* Step 1 - Prepare your device
* Connect a nRF24L01 transmitter to a Wemos D1 mini or an Arduino Device
* WEMOS > RF24 ARDUINO > RF24 ---------------------------------------
* ------------ -------------- | GND # # VCC TOP VIEW |
* 3V3 > VCC VCC > VCC | CE # # CSN OF nRF24L01 |
* GND > GND GND > GND | SCK # # MOSI |
* D2 > CE 6 > CE | MISO # # IRQ |
* D8 > CSN 7 > CSN | |
* D7 > MOSI 11 > MOSI ---------------------------------------
* D6 > MISO 12 > MISO
* D5 > SCK 13 > SCK
*
* Step 2 - Build the sketch for the master device
* Open the file SBNetwork_config.h in the library folder ../libraries/SBNetwork/src/SBNetwork_config.h and comment out (put two // at the line start) the line 6.
* Line 6 should now look like this //#define RUN_AS_MASTER
* Connect the Wemos via USB to the PC, select the right board and COM interface in the tools menu and run the project.
* After building and loading up to the Wemos, the serial monitor should show some log data.
*
* Step 3 - Now open the example sketch GettingStartedClient to build a client device
*/
#include <SBNetwork_config.h>
#include <SBNetwork.h>
// Type in here the mac address of the device.
// This must be unique within your complete network otherwise the network will not work.
SBMacAddress deviceMac(0x01, 0x02, 0x03, 0x04, 0x05);
//SBMacAddress deviceMac(0x05, 0x04, 0x03, 0x02, 0x01);
// Create a new network device with Wemos D1 mini and set the _ce and _cs pin.
SBNetwork networkDevice(D2, D8);
//SBNetwork networkDevice(6, 7);
void setup() {
// Init serial connection
Serial.begin(19200);
// Initialize the network device
networkDevice.initialize(deviceMac);
Serial.println(F("*** PRESS 'N' to reset the device"));
}
void loop() {
// This routine is for resetting the device
// All flash data will be deleted
if (Serial.available())
{
char c = toupper(Serial.read());
if (c == 'N') {
networkDevice.resetData();
}
}
// Call this in the loop() function to maintain the network device
networkDevice.update();
// Check, if there are messages available
uint8_t messageSize = networkDevice.available();
if (messageSize > 0) {
Serial.print(F("Received Content: "));
Serial.println((char*)networkDevice.getMessage());
}
// If the master has received a message, it will sent a message to the sender
if (networkDevice.available()) {
char* message = "Hello client, yes I can hear you well!";
networkDevice.sendToDevice(networkDevice._MasterStorage.Slaves[0], message, strlen(message) + 1);
}
} // Loop

10
library.properties Normal file
View File

@ -0,0 +1,10 @@
name=SBNetwork
version=1.0.0
author=Schullebernd,schullebernd@googlemail.com
maintainer=Schullebernd,schullebernd@googlemail.com
sentence=Build simple master/client networks with nRF24L01 transmitter.
paragraph=The goal of this library is to have a simple network of arduino or ESP8266 devices. There is always one master device and n client devices. The clients automatically searching and pairing with the master. No configuration is needed. The library is explicitly not desiged to build kind of TCP/IP networks (For this there are already perfect matching libraries available).
category=Communication
url=https://github.com/Schullebernd/SBNetwork
architectures=avr,esp8266
includes=SBNetwork_config.h,SBNetwork.h

57
src/SBDevice.h Normal file
View File

@ -0,0 +1,57 @@
#ifndef _SB_SENSOR_NETWORK_DEVCIE_
#define _SB_SENSOR_NETWORK_DEVCIE_
#include <EEPROM.h>
class SBNetworkDevice {
public:
char ID[2] = { 'D', 'S' }; // DS stands for device storage
SBMacAddress MAC;
SBMacAddress MasterMAC;
byte ConnectedToMaster;
uint32_t NetworkKey;
};
#ifdef RUN_AS_MASTER
class SBMasterStorage{
public:
SBMasterStorage(){};
char ID[2] = { 'M', 'S' }; // MS stands for master storage
SBMacAddress Slaves[MAX_CLIENTS];
static SBMasterStorage initialize(){
SBMasterStorage storage;
#if defined(ESP8266)
EEPROM.begin(FLASH_SIZE);
#endif
EEPROM.get(0 + sizeof(SBNetworkDevice), storage);
if (storage.ID[0] != 'M' || storage.ID[1] != 'S'){
// We have to create a new one
storage.ID[0] = 'M';
storage.ID[1] = 'S';
Serial.println("Creating new Master Storage");
EEPROM.put(0 + sizeof(SBNetworkDevice), storage);
#if defined(ESP8266)
EEPROM.commit();
EEPROM.end();
#endif
}
return storage;
}
void save(){
#if defined(ESP8266)
EEPROM.begin(FLASH_SIZE);
#endif
EEPROM.put(0 + sizeof(SBNetworkDevice), *this);
#if defined(ESP8266)
EEPROM.commit();
EEPROM.end();
#endif
}
};
#endif
#endif

599
src/SBNetwork.cpp Normal file
View File

@ -0,0 +1,599 @@
#include "SBNetwork.h"
#include <SPI.h>
#include <RF24.h>
#include <EEPROM.h>
void printAddress(byte address[5]){
Serial.print("0x");
Serial.print(address[0], HEX);
Serial.print(" 0x");
Serial.print(address[1], HEX);
Serial.print(" 0x");
Serial.print(address[2], HEX);
Serial.print(" 0x");
Serial.print(address[3], HEX);
Serial.print(" 0x");
Serial.print(address[4], HEX);
}
void printDeviceData(SBNetworkDevice &device){
Serial.print(F("Device MAC = "));
printAddress(device.MAC.Bytes);
Serial.println();
Serial.print(F("Master MAC = "));
printAddress(device.MasterMAC.Bytes);
Serial.println("");
#ifdef RUN_AS_MASTER
Serial.print(F("NetKey = "));
Serial.print(device.NetworkKey, DEC);
Serial.println("");
#endif
}
SBNetwork::SBNetwork(uint8_t cePin, uint8_t csPin) : radio(cePin, csPin){
}
void SBNetwork::initialize(SBMacAddress mac){
Serial.print(F("SBNetwork Version "));
Serial.println(F(SB_VERSION));
Serial.println(F("===================="));
Serial.println();
_LastTime = 0;
_Uptime = 0;
_NextCheck = 0;
this->initializeNetworkDevice(NetworkDevice, mac);
#if defined(RUN_AS_MASTER)
this->_MasterStorage = SBMasterStorage::initialize();
for (uint8_t i = 0; i < MAX_CLIENTS; i++) {
Serial.print("Masterstorage Slot "); Serial.print(i); Serial.print(" ");
printAddress(_MasterStorage.Slaves[i]);
Serial.println();
}
#endif
Serial.print(F("Initializing NRF24L01 transmitter..."));
this->radio.begin();
// Set the PA Level low to prevent power supply related issues since this is a
// getting_started sketch, and the likelihood of close proximity of the devices. RF24_PA_MAX is default.
this->radio.setPALevel(RF24_PA_HIGH);
this->radio.enableDynamicPayloads();
//this->radio.enableDynamicAck();
this->radio.setAutoAck(true);
//this->radio.enableAckPayload();
this->radio.setRetries(0, 15);
// Listen at the own address
this->radio.openReadingPipe(0, NetworkDevice.MAC);
// Listen at the broadcast address
this->radio.openReadingPipe(1, _BroadcastAddress);
// Start the listening phase
this->radio.startListening();
Serial.println(F("Done"));
#ifndef RUN_AS_MASTER // In case of we defined a client device
// Connect to a master
_Connected = false;
while (!_Connected) {
_Connected = connectToNetwork();
delay(500); // This can be an endless loop in case of no connection to master is available
}
#endif
}
void SBNetwork::initializeNetworkDevice(SBNetworkDevice &device, SBMacAddress mac){
Serial.print(F("Try to read device config from internal flash..."));
#if defined(ESP8266)
EEPROM.begin(FLASH_SIZE);
#endif
EEPROM.get(0, device); // The first two bytes of a storage must always be 'D' 'S' ID to identifiy, that the device was already initiated
if (device.ID[0] == 'D' && device.ID[1] == 'S'){
Serial.println(F("Done"));
printDeviceData(device);
}
else{
Serial.println(F("Failed"));
Serial.println(F("Creating new device config and stroing it to internal flash..."));
device.ID[0] = 'D';
device.ID[1] = 'S';
device.MAC = mac;
device.ConnectedToMaster = 0;
device.NetworkKey = 0;
// Write the data to EEPROM
EEPROM.put(0, device);
#if defined(ESP8266)
EEPROM.commit();
#endif
Serial.println("Done");
printDeviceData(device);
}
#if defined(ESP8266)
EEPROM.end();
#endif
}
void SBNetwork::resetData(){
Serial.print(F("Erasing device configuration data..."));
#if defined(ESP8266)
EEPROM.begin(FLASH_SIZE);
#endif
for(uint16_t i = 0; i < FLASH_SIZE; i++){
EEPROM.write(i, 0);
}
#if defined(ESP8266)
EEPROM.commit();
EEPROM.end();
#endif
Serial.println(F("Done"));
}
bool SBNetwork::sendToDevice(SBMacAddress mac, void* message, uint8_t messageSize){
Serial.print("Sending transmission");
SBNetworkHeader header;
header.ToAddress = mac;
header.FromAddress = this->NetworkDevice.MAC;
header.CommandType = SBS_COMMAND_NO_COMMAND;
SBNetworkFrame frame = SBNetworkFrame();
frame.Header = header;
uint8_t maxPackageSize = MAX_PACKAGE_SIZE;
if (messageSize <= maxPackageSize){
Serial.print(" with no fragmentation");
// We can send directly without fragmentation
frame.Header.FragmentNr = 0;
frame.Header.FragmentCount = 1;
frame.Header.PackageId = millis();
frame.MessageSize = messageSize;
frame.Message = (uint8_t*)message;
bool bSuccess = this->sendToDevice(frame);
if (bSuccess) {
Serial.println(" Done");
}
else {
Serial.println(" Failed");
}
return bSuccess;
}
else{
Serial.print(" with fragmentation ");
// We have to send it in fragments
uint8_t fragmentCount = messageSize / maxPackageSize;
if ((fragmentCount * maxPackageSize) < messageSize){
fragmentCount++;
}
//Serial.println(F("Have to send fragments"));
//Serial.print(F("Fragment count = "));
//Serial.println(fragmentCount, DEC);
for (uint8_t i = 0; i < fragmentCount; i++){
Serial.print(".");
uint8_t buffer[32];
if (i != (fragmentCount - 1)){
memcpy(buffer, (uint8_t*)message + (i * maxPackageSize), maxPackageSize);
frame.Message = (uint8_t*)buffer;
frame.MessageSize = maxPackageSize;
}
else{
memcpy(buffer, (uint8_t*)message + (i * maxPackageSize), messageSize - (i*maxPackageSize));
frame.Message = (uint8_t*)buffer;
frame.MessageSize = messageSize - (i*maxPackageSize);
}
frame.Header.FragmentCount = fragmentCount;
frame.Header.FragmentNr = i;
bool bSuccess = this->sendToDevice(frame);
if (!bSuccess){
Serial.println(" Failed");
return false;
}
}
Serial.println(" Done");
return true;
}
}
bool SBNetwork::sendToDevice(SBNetworkFrame frame){
uint8_t bufferSize = sizeof(SBNetworkHeader) + frame.MessageSize;
uint8_t buffer[32]; // = (uint8_t*)malloc(bufferSize);
memcpy(buffer, &frame.Header, sizeof(SBNetworkHeader));
if (frame.MessageSize > 0){
memcpy(buffer + sizeof(SBNetworkHeader), frame.Message, frame.MessageSize);
}
// Send to broadcast
radio.stopListening();
radio.openWritingPipe(frame.Header.ToAddress);
bool bSuccess = radio.write(buffer, bufferSize);
//free(buffer);
radio.openReadingPipe(0, this->NetworkDevice.MAC);
radio.startListening();
return bSuccess;
}
bool SBNetwork::receive(SBNetworkFrame *frame){
uint8_t pipe = -1;
if (radio.available(&pipe)){
// Variable for the received timestamp
uint8_t size = radio.getDynamicPayloadSize();
if (size == 0){
return false;
}
else{
byte buffer[32];
radio.read(buffer, size);
// We cant use the target address of frame, because the first element in fram is the header
memcpy(frame, buffer, sizeof(SBNetworkHeader));
frame->MessageSize = size - sizeof(SBNetworkHeader);
if (frame->MessageSize > 0){
//uint8_t *payload = (uint8_t*)malloc(frame->MessageSize);
memcpy(_ReceiveBuffer, buffer + sizeof(SBNetworkHeader), frame->MessageSize);
frame->Message = _ReceiveBuffer;
}
// We must check, if the received package is a NO_COMMAND_PACKAGE otherwise we have to handle it internally
return this->handleCommandPackage(frame);
}
}
return false;
}
bool SBNetwork::receiveMessage(void **message, uint8_t *messageSize, SBMacAddress *mac){
uint8_t pipe = -1;
uint8_t maxPackageSize = MAX_PACKAGE_SIZE;
if (radio.available()){
SBNetworkFrame frame;
bool bReceive = this->receive(&frame);
if (bReceive) {
Serial.print("Incomming transmission from ");
printAddress(frame.Header.FromAddress);
Serial.println();
if (frame.Header.FragmentCount == 1) {
// We only have to receive this package
memcpy(_ReadBuffer, frame.Message, maxPackageSize);
(*message) = _ReadBuffer;
(*messageSize) = frame.MessageSize;
(*mac) = frame.Header.FromAddress;
return true;
}
else if (frame.Header.FragmentNr == 0) {
// We have to receive more packages
//uint8_t *buffer = (uint8_t*)malloc((frame.MessageSize * frame.Header.FragmentCount));
memcpy(_ReadBuffer, frame.Message, maxPackageSize);
//free(frame.Message);
delay(10);
while (radio.available()) {
bReceive = this->receive(&frame);
if (!bReceive) {
//free(buffer);
return false;
}
else {
memcpy(_ReadBuffer + (frame.Header.FragmentNr * maxPackageSize), frame.Message, frame.MessageSize);
//free(frame.Message);
if (frame.Header.FragmentNr == (frame.Header.FragmentCount - 1)) {
// Last fragment received
*message = _ReadBuffer;
*messageSize = ((frame.Header.FragmentCount - 1) * maxPackageSize) + frame.MessageSize;
(*mac) = frame.Header.FromAddress;
return true;
}
delay(10);
}
}
//free(buffer);
return false;
}
else {
if (frame.Message != NULL) {
//free(frame.Message);
}
return false;
}
}
else {
return false;
}
}
return false;
}
#ifndef RUN_AS_MASTER
bool SBNetwork::connectToNetwork(){
Serial.print(F("Try to connect to master..."));
// First we have to check, if we already have a master stored
if (!this->NetworkDevice.ConnectedToMaster){
Serial.println("Warning - Not paired to a master");
Serial.print(F("Sending broadcast transmission to find a master..."));
// If not, we have to search for a master
SBNetworkHeader header;
header.ToAddress = this->_BroadcastAddress;
header.FromAddress = this->NetworkDevice.MAC;
header.CommandType = SBS_COMMAND_SEARCH_MASTER;
header.FragmentCount = 1;
header.PackageId = millis();
SBNetworkFrame frame;
frame.Header = header;
frame.Message = NULL;
frame.MessageSize = 0;
bool bMasterAck = this->sendToDevice(frame);
unsigned long started_waiting_at = millis();
boolean timeout = false;
while (!this->receive(&frame)){
if ((millis() - started_waiting_at) > 1000){
timeout = true;
break;
}
}
if (timeout){
Serial.println(F("Timeout"));
return false;
}
else{
if (frame.Header.CommandType != SBS_COMMAND_MASTER_ACK){
if (frame.MessageSize > 0){
free(frame.Message);
}
Serial.println(F("Failed - Got answer but no master ack"));
return false;
}
else{
Serial.println(F("Done"));
Serial.print(F("Got answer from a master. Master-MAC is "));
printAddress(frame.Header.FromAddress);
Serial.println();
Serial.print(F("Try to pair with master..."));
SBNetworkFrame conFrame;
conFrame.Header.CommandType = SBS_COMMAND_REQUEST_PAIRING;
conFrame.Header.FragmentCount = 1;
conFrame.Header.FragmentNr = 0;
conFrame.Header.FromAddress = this->NetworkDevice.MAC;
conFrame.Header.PackageId = millis();
conFrame.Header.ToAddress = frame.Header.FromAddress;
conFrame.MessageSize = 0;
if (!this->sendToDevice(conFrame)){
Serial.println("Failed - Sending pairing request");
}
else{
while (!this->receive(&frame)){
if (millis() - started_waiting_at > 1000){
timeout = true;
break;
}
}
if (timeout) {
Serial.println(F("Timeout"));
return false;
}
if (frame.Header.CommandType != SBS_COMMAND_PAIRING_ACK){
Serial.println(F("Failed - Pairing rejected from the master"));
return false;
}
else{
this->NetworkDevice.MasterMAC = frame.Header.FromAddress;
this->NetworkDevice.NetworkKey = *(frame.Message);
this->NetworkDevice.ConnectedToMaster = -1;
EEPROM.put(0, NetworkDevice);
Serial.println("Suceeded");
Serial.print("Try to ping to master...");
delay(100);
}
}
}
}
}
bool bMasterAvailable = this->pingDevice(this->NetworkDevice.MasterMAC);
if (bMasterAvailable){
Serial.println(F("Done - Master available"));
}
else{
Serial.println(F("Failed - Master not responding"));
}
return bMasterAvailable;
}
#endif
bool SBNetwork::pingDevice(SBMacAddress mac){
SBNetworkHeader header;
header.ToAddress = mac;
header.FromAddress = this->NetworkDevice.MAC;
header.CommandType = SBS_COMMAND_PING;
header.FragmentCount = 1;
header.FragmentNr = 0;
header.PackageId = millis();
SBNetworkFrame frame;
frame.Header = header;
frame.Message = NULL;
frame.MessageSize = 0;
return this->sendToDevice(frame);
}
bool SBNetwork::handleCommandPackage(SBNetworkFrame *frame){
#if defined(RUN_AS_MASTER)
// First check, if the device is listed in the storage
bool bFound = false;
for (uint8_t i = 0; i < MAX_CLIENTS; i++){
if (this->_MasterStorage.Slaves[i].isEquals(frame->Header.FromAddress)){
_SlavePings[i] = _Uptime;
bFound = true;
break;
}
}
if (!bFound){
// If an unknown device was detected, then never handle the network control traffic and never handle the messages
#ifdef _DEBUG
Serial.print("Unknown device detected with MAC: ");
printAddress(frame->Header.FromAddress);
Serial.println();
#endif
//return false;
}
switch (frame->Header.CommandType){
case SBS_COMMAND_PING:{
#ifdef _DEBUG
Serial.println("Received 'PING'");
#endif
break;
}
case SBS_COMMAND_SEARCH_MASTER:{
#ifdef _DEBUG
Serial.println("Received 'SEARCH_MASTER' Package. Send MasterACK...");
#endif
delay(100);
bool bSend = sendMasterAck(frame->Header.FromAddress);
if (bSend){
return false;
}
Serial.println("Done");
break;
}
case SBS_COMMAND_REQUEST_PAIRING:{
#ifdef _DEBUG
Serial.println("Received 'PAIRING_REQUEST' Package. Send PairingACK");
#endif
delay(100);
// This is the point where we could stop orpcessing and wait for an user input on the controller to let the new device access the network
bool bSend = sendPairingAck(frame->Header.FromAddress);
if (bSend){
addMac(frame->Header.FromAddress);
}
break;
}
case SBS_COMMAND_NO_COMMAND:
default:{
//Serial.println("No Command received. Passing through transport layer.");
return bFound;
break;
}
}
return false;
#else
return true;
#endif
}
#if defined(RUN_AS_MASTER)
bool SBNetwork::sendMasterAck(SBMacAddress mac){
SBNetworkHeader header;
header.ToAddress = mac;
header.FromAddress = this->NetworkDevice.MAC;
header.CommandType = SBS_COMMAND_MASTER_ACK;
header.FragmentCount = 1;
header.PackageId = millis();
SBNetworkFrame frame;
frame.Header = header;
frame.Message = (uint8_t*)&(this->NetworkDevice.NetworkKey);
frame.MessageSize = sizeof(uint32_t);
return this->sendToDevice(frame);
}
bool SBNetwork::sendPairingAck(SBMacAddress mac){
SBNetworkHeader header;
header.ToAddress = mac;
header.FromAddress = this->NetworkDevice.MAC;
header.CommandType = SBS_COMMAND_PAIRING_ACK;
header.FragmentCount = 1;
header.PackageId = millis();
SBNetworkFrame frame;
frame.Header = header;
frame.Message = NULL;
frame.MessageSize = 0;
return this->sendToDevice(frame);
}
#endif
#ifndef RUN_AS_MASTER
bool SBNetwork::checkMaster(){
if (this->pingDevice(this->NetworkDevice.MasterMAC)){
Serial.println("Master OK");
return true;
}
else{
Serial.println("Master ERROR");
return false;
}
}
#endif
void SBNetwork::update(){
// Update the uptime counter
if (_LastTime > millis()){
long add = millis();
_Uptime += add;
}
else{
long add = millis() - _LastTime;
_Uptime += add;
}
_LastTime = millis();
#ifndef RUN_AS_MASTER
if (NetworkDevice.ConnectedToMaster && MASTER_CHECK_INTERVAL){
if (_Uptime > _NextCheck){
// Now we have to check our sensors if they are still available
_NextCheck += MASTER_CHECK_INTERVAL;
checkMaster();
}
}
#endif
_LastReceivedMessageSize = 0;
_LastReceivedMessage = NULL;
SBMacAddress fromAddress;
if (!receiveMessage((void**)&_LastReceivedMessage, &_LastReceivedMessageSize, &_LastReceivedFromAddress)) {
_LastReceivedMessageSize = 0;
_LastReceivedMessage = NULL;
}
}
#if defined(RUN_AS_MASTER)
uint8_t SBNetwork::addMac(SBMacAddress mac){
// iterate through the storage and look if the mac already exists
uint8_t iPos;
for (iPos = 0; iPos < MAX_CLIENTS; iPos++){
if (_MasterStorage.Slaves[iPos].isEquals(mac)){
return iPos;
}
}
// Search the first free place and add the mac
for (iPos = 0; iPos < MAX_CLIENTS; iPos++){
if (_MasterStorage.Slaves[iPos].isEquals(EMPTY_MAC)){
_MasterStorage.Slaves[iPos] = mac;
_MasterStorage.save();
return iPos;
}
}
return -1;
}
uint8_t SBNetwork::removeMac(SBMacAddress mac){
// iterate through the storage and look if the mac is in the list, if not, then return -1. If yes, remove it.
for (uint8_t iPos = 0; iPos < MAX_CLIENTS; iPos++){
if (_MasterStorage.Slaves[iPos].isEquals(mac)){
_MasterStorage.Slaves[iPos] = EMPTY_MAC;
_MasterStorage.save();
return iPos;
}
}
return -1;
}
#endif

160
src/SBNetwork.h Normal file
View File

@ -0,0 +1,160 @@
#ifndef _SB_NETWORK_
#define _SB_NETWORK_
#define SB_VERSION "1.0.0"
#include "SBTypes.h"
#include "SBNetwork_config.h"
#include "SBDevice.h"
#include <RF24.h>
#define BROADCAST_MAC (SBMacAddress(0x9E, 0x0E, 0x9E, 0x0E, 0x9E))
#define EMPTY_MAC (SBMacAddress(0x00, 0x00, 0x00, 0x00, 0x00))
class SBNetwork{
private:
/*
* Stores the uptime of the device
*/
unsigned long long _Uptime;
/*
* Stores the last time from millis()
*/
unsigned long _LastTime;
/*
* Stores the time when the device should ping to the master
*/
unsigned long _NextCheck;
/*
* Stores the connection state to a master device in case of it is a client device
*/
bool _Connected;
/**
* Here the payload will be stored for each frame receive
*/
uint8_t _ReceiveBuffer[MAX_PACKAGE_SIZE];
/**
* Here the received message is stored after a full message receive (fragmented or not fragmented)
*/
uint8_t _ReadBuffer[MAX_FRAME_SIZE];
#if defined(RUN_AS_MASTER)
/*
* Store the times, when the slaves sent the last signal to the master
*/
unsigned long long _SlavePings[MAX_CLIENTS];
#endif
/**
Points to the last message which was received
*/
void* _LastReceivedMessage;
/**
Stores the length of the last receives message
*/
uint8_t _LastReceivedMessageSize;
/**
Stores the mac of the sender of the last received message
*/
SBMacAddress _LastReceivedFromAddress;
void initializeNetworkDevice(SBNetworkDevice &device, SBMacAddress mac);
bool sendToDevice(SBNetworkFrame frame);
bool handleCommandPackage(SBNetworkFrame *frame);
#if defined(RUN_AS_MASTER)
bool sendMasterAck(SBMacAddress mac);
bool sendPairingAck(SBMacAddress mac);
#endif
bool receive(SBNetworkFrame *frame);
bool receiveMessage(void **message, uint8_t *messageSize, SBMacAddress *mac);
public:
/*
* Define the standard addresses for the sensor network
*/
//SBMacAddress _StandardSensorAddress = SBMacAddress(0x01, 0x01, 0x01, 0x01, 0x01);
SBMacAddress _BroadcastAddress = BROADCAST_MAC;
SBNetworkDevice NetworkDevice;
#if defined(RUN_AS_MASTER)
SBMasterStorage _MasterStorage;
#endif
RF24 radio;
//######################################################################################
/*
* Constructor with setting the used pins for commnicate with the NRF24L01(+) chip.
*/
SBNetwork(uint8_t cePin, uint8_t csPin);
/*
* Constructor with setting the used pins for commnicate with the NRF24L01(+) chip.
*/
SBNetwork();
/*
* Initializes the sensor / master
*/
void initialize(SBMacAddress mac);
void initialize(byte mac[]) { initialize(SBMacAddress(mac[0], mac[1], mac[2], mac[3], mac[4])); }
/*
* Resets the Sensors data (including the eeprom).
* The sensor then will loos the connaction to the master.
*/
void resetData();
/*
Sends a SBNetworkFrame to a device.
*/
bool sendToDevice(SBMacAddress mac, void* message, uint8_t messageSize);
/*
Check if a new incomming transmission is available
*/
uint8_t available() { return _LastReceivedMessageSize; }
/*
Returns a pointer to the buffer, where the last incomming transmission is stored
*/
void* getMessage() { return _LastReceivedMessage; }
#ifndef RUN_AS_MASTER
bool connectToNetwork();
bool checkMaster();
#else
// Adds a mac to the storage and returns the position in the storage.
uint8_t addMac(SBMacAddress mac);
// Removes the mac from the storage. If the mac was stored, it returns the position of the mac, if not, it returns -1.
uint8_t removeMac(SBMacAddress mac);
#endif
bool pingDevice(SBMacAddress mac);
// Updates the uptime counter.
// If this device is a sensor, it pings the master after a time. The time is definded in the config under SENSOR_CHECK_INTERVAL.
// If SENSOR_CHECK_INTERVAL is set to 0, it will not ping the master.
void update();
unsigned long long uptime(){
return _Uptime;
}
};
#endif

18
src/SBNetwork_config.h Normal file
View File

@ -0,0 +1,18 @@
#ifndef _SB_NETWORK_CONFIG_
#define _SB_NETWORK_CONFIG_
// Uncomment the following line, to compile the library for a master device.
#define RUN_AS_MASTER
#define _DEBUG
#define MASTER_CHECK_INTERVAL 0 // All sensors will ping the master every xxx milliseconds. if set to 0, they will not ping the master
#define MAX_CLIENTS 10
#define CLIENT_TIMEOUT 20000
#define FLASH_SIZE 512
#endif

6
src/SBSensors_config.h Normal file
View File

@ -0,0 +1,6 @@
#ifndef __SBSENSORS_CONFIG_H__
#define __SBSENSORS_CONFIG_H__
#endif

30
src/SBTypes.cpp Normal file
View File

@ -0,0 +1,30 @@
#include "SBTypes.h"
SBMacAddress::SBMacAddress(uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4, uint8_t a5){
this->Bytes[0] = a1;
this->Bytes[1] = a2;
this->Bytes[2] = a3;
this->Bytes[3] = a4;
this->Bytes[4] = a5;
}
bool SBMacAddress::isEquals(SBMacAddress otherAddress){
if (this->Bytes[4] != otherAddress.Bytes[4]){
return false;
}
if (this->Bytes[3] != otherAddress.Bytes[3]){
return false;
}
if (this->Bytes[2] != otherAddress.Bytes[2]){
return false;
}
if (this->Bytes[1] != otherAddress.Bytes[1]){
return false;
}
if (this->Bytes[0] != otherAddress.Bytes[0]){
return false;
}
return true;
}

46
src/SBTypes.h Normal file
View File

@ -0,0 +1,46 @@
#ifndef _SB_TYPES_
#define _SB_TYPES_
#include <arduino.h>
#define SBS_COMMAND_PING 0 // Will be sent to check, if a device is available
#define SBS_COMMAND_NO_COMMAND 1
#define SBS_COMMAND_SEARCH_MASTER 2 // Will be sent from a slave to find search a master
#define SBS_COMMAND_MASTER_ACK 3 // Will be sent from a master after receiving a search master request
#define SBS_COMMAND_REQUEST_PAIRING 4
#define SBS_COMMAND_PAIRING_ACK 5
class SBMacAddress{
public:
uint8_t Bytes[5];
SBMacAddress(){};
SBMacAddress(uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4, uint8_t a5);
bool isEquals(SBMacAddress otherAddres);
operator uint8_t*(){
return (uint8_t*)Bytes;
}
};
typedef struct {
SBMacAddress FromAddress;
SBMacAddress ToAddress;
uint8_t CommandType;
uint8_t PackageId; // The unique ID of the package. Will be
uint8_t FragmentCount; // How many fragments will be sent
uint8_t FragmentNr; // Which fragment is this package
} SBNetworkHeader;
#define MAX_PACKAGE_SIZE (32 - sizeof(SBNetworkHeader))
typedef struct{
SBNetworkHeader Header;
uint8_t MessageSize;
uint8_t* Message;
} SBNetworkFrame;
#define MAX_FRAME_SIZE 200
#endif