From 0c8e8582b93fe57f143b899ef6aef860c68b8034 Mon Sep 17 00:00:00 2001 From: Marcel Schulz Date: Mon, 20 Nov 2017 16:54:06 +0100 Subject: [PATCH] Added sources, library properties and example code. --- Readme.md | 13 +- .../GettingStartedClient.ino | 83 +++ .../GettingStartedMaster.ino | 81 +++ library.properties | 10 + src/SBDevice.h | 57 ++ src/SBNetwork.cpp | 599 ++++++++++++++++++ src/SBNetwork.h | 160 +++++ src/SBNetwork_config.h | 18 + src/SBSensors_config.h | 6 + src/SBTypes.cpp | 30 + src/SBTypes.h | 46 ++ 11 files changed, 1099 insertions(+), 4 deletions(-) create mode 100644 examples/GettingStartedClient/GettingStartedClient.ino create mode 100644 examples/GettingStartedMaster/GettingStartedMaster.ino create mode 100644 library.properties create mode 100644 src/SBDevice.h create mode 100644 src/SBNetwork.cpp create mode 100644 src/SBNetwork.h create mode 100644 src/SBNetwork_config.h create mode 100644 src/SBSensors_config.h create mode 100644 src/SBTypes.cpp create mode 100644 src/SBTypes.h diff --git a/Readme.md b/Readme.md index b44792e..039c36c 100644 --- a/Readme.md +++ b/Readme.md @@ -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 . diff --git a/examples/GettingStartedClient/GettingStartedClient.ino b/examples/GettingStartedClient/GettingStartedClient.ino new file mode 100644 index 0000000..ad42935 --- /dev/null +++ b/examples/GettingStartedClient/GettingStartedClient.ino @@ -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 +#include + +// 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 diff --git a/examples/GettingStartedMaster/GettingStartedMaster.ino b/examples/GettingStartedMaster/GettingStartedMaster.ino new file mode 100644 index 0000000..9baca04 --- /dev/null +++ b/examples/GettingStartedMaster/GettingStartedMaster.ino @@ -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 +#include + +// 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 diff --git a/library.properties b/library.properties new file mode 100644 index 0000000..b969952 --- /dev/null +++ b/library.properties @@ -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 diff --git a/src/SBDevice.h b/src/SBDevice.h new file mode 100644 index 0000000..5f0653a --- /dev/null +++ b/src/SBDevice.h @@ -0,0 +1,57 @@ +#ifndef _SB_SENSOR_NETWORK_DEVCIE_ +#define _SB_SENSOR_NETWORK_DEVCIE_ + +#include + +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 \ No newline at end of file diff --git a/src/SBNetwork.cpp b/src/SBNetwork.cpp new file mode 100644 index 0000000..ab8d460 --- /dev/null +++ b/src/SBNetwork.cpp @@ -0,0 +1,599 @@ +#include "SBNetwork.h" +#include +#include +#include + +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 \ No newline at end of file diff --git a/src/SBNetwork.h b/src/SBNetwork.h new file mode 100644 index 0000000..446daf2 --- /dev/null +++ b/src/SBNetwork.h @@ -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 + +#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 diff --git a/src/SBNetwork_config.h b/src/SBNetwork_config.h new file mode 100644 index 0000000..72ebc47 --- /dev/null +++ b/src/SBNetwork_config.h @@ -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 diff --git a/src/SBSensors_config.h b/src/SBSensors_config.h new file mode 100644 index 0000000..eff212b --- /dev/null +++ b/src/SBSensors_config.h @@ -0,0 +1,6 @@ +#ifndef __SBSENSORS_CONFIG_H__ +#define __SBSENSORS_CONFIG_H__ + + + +#endif diff --git a/src/SBTypes.cpp b/src/SBTypes.cpp new file mode 100644 index 0000000..4b655a6 --- /dev/null +++ b/src/SBTypes.cpp @@ -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; +} + diff --git a/src/SBTypes.h b/src/SBTypes.h new file mode 100644 index 0000000..c4aed74 --- /dev/null +++ b/src/SBTypes.h @@ -0,0 +1,46 @@ + +#ifndef _SB_TYPES_ +#define _SB_TYPES_ + +#include + +#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 +