diff --git a/.gitignore b/.gitignore index 12a5de458818d9652e3110e932d4de0c3b6df64d..231fb3a0f8f145f92db50e3a9376090f92ec5dfa 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ didacticNet.h.gch +.vscode +.log diff --git a/DownloadButton.png b/DownloadButton.png new file mode 100644 index 0000000000000000000000000000000000000000..2f8a5b006a4935a43ebc0d8d075e6d66ac21d1ed Binary files /dev/null and b/DownloadButton.png differ diff --git a/README.md b/README.md index 797b14dec96d3bf21dca892c72956899947f0343..50f583eee6fb565f9e2041b9ab73c6ba7d2e8397 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,243 @@ # didacticNetwork -Library for implementing networks on Arduino boards. -The functionalities correspond to state of the art technologies, which are didactically reduced. +Bibliothek zur Implementierung von vereinfachten Netzwerken auf Arduino-Boards. +Die Funktionalitaeten entsprechen dem Stand der Technik, die Umsetzung ist didaktisch reduziert. -## Supported so far: -publish/subscribe network +Für eine Nutzung der Bibliothek wird eine drahtlose Schnittstelle benoetigt, wleche sich an der Seriellen Schnittstelle betreiben lässt. +Wir empfehlen unsere IR-Link-Module, die Sie selbst herstellen koennen (Schaltplan und Layout bei uns anfragen - Kontakt über [letsgoING.org](httsp://letsgoING.org) +oder z. B. hier erwerben koennen: [Hinweise zur Bestellung bei Aisler](#bestellung-bei-aisler). -### usage + +## Download +Start download with the button on the right side. Choose the right format and start the download. + + + +After download install library: +https://www.arduino.cc/en/guide/libraries section "Importing a .zip Library" + +# publish/subscribe network + +## Beispiele + +**Broker:** +- sPSN_Broker.ino **->** Broker Applikation (muss nicht angepasst werden) + +**Clients:** +- sPSN_Client1(2).ino **->** Client Applikationen die sich gegenseitig einen analogen bzw. digtalen Wert senden +- sPSN-ClientMinimal.ino **->** Minimal-Code für den einfachen Einstieg +- sPSN-Chat.ino **->** Chat-Applikation die Nachrichten unter eigenem Namen austauschen lässt + +## Funktionen und Parameter + +### Client + +```cpp +#Anlegen der Client-Instanz +// Anlegen des didacticPSNetClient-Objekts +// psnClient -> moeglicher Name für Objekt +didacticPSNetClient psnClient; + +#Starten der Client-Instanz +void psnClient.begin(Stream& sSerial, fcn clientCallback); +// param1: Schnittstelle (Serial | SoftSerial) +// param2: Callback-Funktion (Bezeichnung frei waehlbar) + +#Netzwerkverwaltung Client (Daten senden und Empfangen, Zugriffsregelung) +bool psnClient.handleNetwork(); +// return: true wenn Daten versendet / false wenn nicht + +void psnClient.setInterval(long intervalTime); +// param: Mindestwartezeit in ms zwischen zwei Sendevorgängen (default 500 ms) + +#Topic eines anderen Clients abbonieren +int psnClient.subscribe(char* topic); +// param: Topic String/char-Array ("example" / char topic[n]) +int psnClient.subscribe(char* topic, int length); +// param1: Topic String/char-Array +// param2: Anzahl Zeichen des Topics +// return ERROR-Wert: DN_ERROR_NO_ERROR, +// DN_ERROR_TOPIC_LEN (Topic zu lang - wird abgeschnitten) + +#Topic eines anderen Clients entfernen +bool psnClient.unsubscribe(char* topic); +// param: Topic String/char-Array + +bool psnClient.unsubscribe(char* topic, int length); +// param1: Topic String/char-Array +// param2: Anzahl Zeichen des Topics +// return true wenn Daten versendet / false wenn nicht + +#Daten unter Topic veroeffentlichen +int psnClient.publish(char* topic, char* payload); +// param1: Topic String/char-Array; +// param2: payload-char-Array +// return: ERROR-Wert: DN_PUBLISH_SUCCESSULL, +// DN_ERROR_TOPIC_LEN (Topic zu lang - wird abgeschnitten), +// DN_ERROR_PAYLOAD_LEN (Payload zu lange - wird abgeschnitten) + +int psnClient.publish(char* topic, bool payload); +// param1: Topic String/char-Array; +// param2: payload vom Datentyp bool (wird in char-Array gewandelt) +// return: ERROR-Wert: DN_PUBLISH_SUCCESSULL, +// DN_ERROR_TOPIC_LEN (Topic zu lang - wird abgeschnitten), +// DN_ERROR_PAYLOAD_LEN (Payload zu lange - wird abgeschnitten) + +int psnClient.publish(char* topic, int payload); +// param1: Topic String/char-Array; +// param2: payload vom Datentyp int (wird in char-Array gewandelt) +// return: ERROR-Wert: DN_PUBLISH_SUCCESSULL, +// DN_ERROR_TOPIC_LEN (Topic zu lang - wird abgeschnitten), +// DN_ERROR_PAYLOAD_LEN (Payload zu lange - wird abgeschnitten) + +int psnClient.publish(char* topic, int topicLength, char* payload, int payloadLength); +// param1: Topic String/char-Array +// param2: Anzahl Zeichen des Topics +// param3: payload-char-Array +// param4: Anzahl Zeichen der Payload +// return: ERROR-Wert: DN_PUBLISH_SUCCESSULL, +// DN_ERROR_TOPIC_LEN (Topic zu lang - wird abgeschnitten), +// DN_ERROR_PAYLOAD_LEN (Payload zu lange - wird abgeschnitten) + +//Daten werden beim Zustandswechsel von payload (0->1 / 1->0) veroeffentlicht +int psnClient.publishOnChange(char* topic, bool payload); +// param1: Topic String/char-Array; +// param2: payload vom Datentyp bool (wird in char-Array gewandelt) +// return: ERROR-Wert: DN_PUBLISH_SUCCESSULL, +// DN_ERROR_NO_ERROR (Keine Daten veroeffentlicht und keine Fehler), +// DN_ERROR_TOPIC_LEN (Topic zu lang - wird abgeschnitten), +// DN_ERROR_PAYLOAD_LEN (Payload zu lange - wird abgeschnitten) + +// Daten werden veroeffentlicht, wenn Veränderung von payload groeßer als threshold ist +int psnClient.publishOnChange(char* topic, int payload, int threshold); +// param1: Topic String/char-Array; +// param2: payload vom Datentyp bool (wird in char-Array gewandelt) +// param1: Topic String/char-Array; +// return: ERROR-Wert: DN_PUBLISH_SUCCESSULL, +// DN_ERROR_NO_ERROR (Keine Daten veroeffentlicht und keine Fehler), +// DN_ERROR_TOPIC_LEN (Topic zu lang - wird abgeschnitten), +// DN_ERROR_PAYLOAD_LEN (Payload zu lange - wird abgeschnitten) + +#Callback-Funktion (wird bei neuen Daten aufgerufen) +//Bezeichnung muss mit der in psnClient.begin(...) übereinstimmen +void clientCallback(char* topic, int topicLength, char* payload, int payloadLength){...} +// param1: Topic der Nachricht +// param2: Lange des Topics +// param3: Nutdaten der Nachricht +// param4: Laenge der Nachricht +``` + +### Broker + +```cpp +#Anlegen der Broker-Instanz +// Anlegen des Broker-Objekts +// psnBroker -> moeglicher Name für Objekt +didacticPSNetBroker psnBroker; + +#Starten der Broker-Instanz +void psnBroker.begin(Stream& sSerial); +// param: Schnittstelle (Serial | SoftwareSerial) + +#Netzwerkverwaltung Broker (Daten senden und Empfangen, Zugriffsregelung) +bool psnBroker.handleNetwork(); +// return: true wenn Daten versendet / false wenn nicht + +void psnBroker.setInterval(long intervalTime); +// param: Mindestwartezeit in ms zwischen zwei Sendevorgängen (default 0 ms) +``` + +### Hilfreiche Funktionen +Die Hiflsfunktionen sind als eigenständige Klassen implementiert und koennen so mehrfach (unter verschiedenen Namen) angelegt werden. +So koennen die Funktionen mehrfach, mit unterschiedlichen Parametern, verwendet werden. + +```cpp +#Flankenerkennung z.B. fuer Taster +// Anlegen des EdgeDetector-Objekts +// eDetector -> moeglicher Name für Objekt +EdgeDetector eDetector; + +int eDetector.edgeDetected(bool currentState); +// param: aktueller binärer Zustand +// return: 0 -> keine Flanke, RISING, FALLING + +#Auf Wertaenderung groeßer Schwellwert pruefen +// Anlegen des ChangeDetector-Objekts +// cDetector -> moeglicher Name für Objekt +ChangeDetector cDetector + +bool cDetector.valueChanged(int currentvalue, int threshold); +// param1: aktueller Wert +// param2: Schwellwert +// return: true -> Aenderung groeßer als Schwellwert; sonst false + +#Nicht blockierendes Warten +// Anlegen des UnblockingTimer-Objekts +// uTimer -> moeglicher Name für Objekt +UnblockingTimer uTimer; + +bool uTimer.timeElapsed(long); +// param: zu wartende Zeit in Millisekunden +// return: true, wenn Zeit Wartezeit abgelaufen; sonst false + +#Einlesen von Text ueber Serialle Schnittstelle bis Endezeichen empfangen wird +// Anlegen des SerialReader-Objekts +// sReader -> moeglicher Name für Objekt +SerialReader sReader; + +int sReader.readSerialData(Stream&, char*, char); +// param1: Schnittstelle (Serial | SoftSerial) +// param2: Array in das die Zeichen eingelesen werden +// param3: Endezeichen (char) +// return: Anzahl der eingelesenen Zeichen (0 wenn noch kein Endezeichen) +``` + +### Konstanten + +Konstanten aus der Library die fuer die Programmierung genutzt werden und angepasst werden koennen. +```cpp +#ASCII Endezeichen +DN_ASCII_CR "carriage return CR" (int) 13 +DN_ASCII_NL "new line NL"(int) 10 + +#Laenge Topics und Payload (Client und Broker) +MAX_LEN_TOPICS default: 10 +MAX_LEN_PAYLOAD default: 20 + +#Anzahl Topics +MAX_NR_TOPICS_CLIENT default: 5 +MAX_NR_TOPICS_BROKER default: 20 + +#Mindestwartezeiten zwischen Sendevorgängen (anpassen über psnClient.setInterval()) +INTERVAL_CLIENT default: 500 ms +INTERVAL_BROKER default: 0 ms + +#ERRORs (nicht verändern) +DN_PUBLISH_SUCCESSULL 1 +DN_ERROR_NO_ERROR 0 +DN_ERROR_TOPIC_LEN -1 +DN_ERROR_PAYLOAD_LEN -2 + +#Frame +MSG_PRELIMITER '<' +MSG_DELIMITER '>' +MSG_SEPARATOR '|' + +#Dienste +MSG_PUBLISH '@' +MSG_SUBSCRIBE '?' +MSG_UPDATE '#' +MSG_TOPIC_MULTI '*' +``` + +# Bestellung bei Aisler + +- Account anlegen +- Link oeffnen: [Nachkaufprojekt bei Aisler.net](https://aisler.net/p/NBAQNHFV) +- Projekt importieren (Fehlermeldung / Hinweis ignorieren) +- Auf eigenen Account zurück gehen +- unter "Projekte -> Sandbox" befindet sich dann die Platine und die Bauteilliste +- vor der Bestellung Bauteilliste (Preise) aktualisieren +- Bestellung der Platinen (Peautiful Boards) und der Bauteile (Precious Parts) + +**Hinweis:** Das Projekt letsgoING ist in **keiner Weise** an dem **Verkauf beteiligt**. \ No newline at end of file diff --git a/examples/brokerClient/brokerClient.ino b/examples/brokerClient/brokerClient.ino deleted file mode 100644 index ccabaa076185898ac5dedfff7502f3910b830e80..0000000000000000000000000000000000000000 --- a/examples/brokerClient/brokerClient.ino +++ /dev/null @@ -1,215 +0,0 @@ -#include "Arduino.h" - #include "SoftwareSerial.h" - #include "didacticNet.h" - - - //#define BROKER - #define CLIENT - #define C5 // C1 C2 C3 C4 C5 - - #define SERIAL_BAUD 2400 - - SoftwareSerial sSerial(10, 11); - - #ifdef BROKER - didacticPSNetBroker psnB; - - void setup() { - Serial.begin(SERIAL_BAUD); - sSerial.begin(SERIAL_BAUD); - psnB.setStream(sSerial); - } - - void loop() { - psnB.handleNetwork(); - } - #endif - - #ifdef CLIENT - unsigned long lastTime = 0L; - byte ledPin3 = 3; - byte ledPin5 = 5; - byte ledPin6 = 6; - byte buttonPin1= 2; - - bool currentState = false; - bool lastState = false; - bool togglestate = true; - int lastValue = 0; - - bool dataReadDone = false; - char readData[MAX_LEN_DATA*5]={'\0'}; - char arrayBuffer[MAX_LEN_DATA+1] = {'\0'}; - int counter = 0; - - void myCallback(char* mTopic, int mToLength, char* mData, int mDaLength) { - - - #if defined(C1) || defined(C2) || defined(C3) || defined(C4) - Serial.println("Callback: "); - Serial.print("Topic: "); - Serial.print(mTopic); - Serial.print("\tData: "); - Serial.println(mData); - #elif defined(C5) - Serial.print(mTopic); - Serial.print(":\t"); - Serial.println(mData); - #endif - - #if defined(C1) - digitalWrite(ledPin3,(bool)atoi(mData)); - #elif defined(C3) - analogWrite(ledPin3, map(atoi(mData),0,1023,0,255)); - analogWrite(ledPin5, map(atoi(mData),0,1023,255,0)); - #elif defined(C4) - digitalWrite(ledPin3,atoi(mData)<400); - digitalWrite(ledPin5,atoi(mData)<400); - digitalWrite(ledPin6,atoi(mData)<400); - #endif - } - - #if defined(C1) - char topicSub[MAX_LEN_TOPICS]={"button1"}; - char topicPub[MAX_LEN_TOPICS]={"poti1"}; - #elif defined(C2) - char topicSub[MAX_LEN_TOPICS]={""}; - char topicPub[MAX_LEN_TOPICS]={"ldr1"}; - #elif defined(C3) - char topicSub[MAX_LEN_TOPICS]={"poti1"}; - char topicPub[MAX_LEN_TOPICS]={""}; - #elif defined(C4) - char topicSub[MAX_LEN_TOPICS]={"ldr1"}; - char topicPub[MAX_LEN_TOPICS]={"button1"}; - #elif defined(C5) - char topicSub[MAX_LEN_TOPICS]={""}; - char topicPub[MAX_LEN_TOPICS]={""}; - #endif - - didacticPSNetClient psnC; - void setup() { - Serial.begin(2400); - sSerial.begin(2400); - - pinMode(ledPin3, OUTPUT); - pinMode(ledPin5, OUTPUT); - pinMode(ledPin6, OUTPUT); - - pinMode(buttonPin1, INPUT); - - psnC.setStream(sSerial); - psnC.setCallback(myCallback); - - #if defined(C1) || defined(C3) || defined(C4) - psnC.subscribe(topicSub, strlen(topicSub)); - #endif - - #if defined(C5) - Serial.print("Bitte den eigenen Namen mit einem # eingeben\nund mit Enter bestaetigen. -> "); - Serial.println("#DeinName"); - Serial.print("Gebe dann die Namen deiner Chatpartner mit einem ? ein. -> "); - Serial.println("@ChatPartner"); - Serial.println("Anschließend kannst du mit ihnen schreiben"); - Serial.println(); - Serial.println("HINWEIS:"); - Serial.println("Stelle das Zeilenende im SerialMonitor auf \"Zeilenumbruch (CR)\""); - Serial.println("*********************************************************************\n"); - #endif - } - - void loop() { - psnC.handleNetwork(); - - #if defined(C1) || defined(C2) - int currentValue = analogRead(0); - if(abs(currentValue-lastValue) > 20){ - delay(500); - currentValue = analogRead(0); - char data[MAX_LEN_DATA]={0}; - itoa(currentValue, data, 10); - psnC.publish(topicPub, strlen(topicPub), data, strlen(data)); - lastValue = currentValue; - } - #endif - - #if defined(C3) - if(digitalRead(buttonPin1)){ - togglestate = !togglestate; - if(!togglestate){ - psnC.unsubscribe(topicSub, strlen(topicSub)); - digitalWrite(ledPin3,LOW); - digitalWrite(ledPin5,LOW); - digitalWrite(ledPin6,LOW); - } - else{ - psnC.subscribe(topicSub, strlen(topicSub)); - } - delay(500); - } - - #endif - - #if defined(C4) - currentState = digitalRead(buttonPin1); - if(lastState != currentState){ - char data[MAX_LEN_DATA]={0}; - itoa(currentState, data, 10); - psnC.publish(topicPub, strlen(topicPub), data, strlen(data)); - delay(500); - } - lastState = currentState; - #endif - - #if defined(C5) - if(Serial.available()){ - char buffer = Serial.read(); - readData[counter] = buffer; - if(buffer == 13){ - readData[counter] = '\0'; - counter = 0; - dataReadDone = true; - }else{ - counter++; - } - } - if(dataReadDone && strlen(readData)<= MAX_LEN_DATA*5 ){ - if(readData[0] == '@'){ - strcpy(readData, readData+1); - psnC.subscribe(readData, strlen(readData)); - Serial.print("Kontakt zu "); - Serial.print(readData); - Serial.println(" hergestellt"); - } - else if(readData[0] == '#'){ - strcpy(topicPub, readData+1); - Serial.print("Dein Name:\t"); - Serial.println(topicPub); - } - else { - for(int i = 0; i*MAX_LEN_DATA < strlen(readData); i++){ - memset(arrayBuffer, '\0', MAX_LEN_DATA+1); - strncpy(arrayBuffer, &readData[MAX_LEN_DATA*i],MAX_LEN_DATA); - //Serial.print("ReadData: ");Serial.print(readData);Serial.print("\t");Serial.print(arrayBuffer); - psnC.publish(topicPub, strlen(topicPub), arrayBuffer, strlen(arrayBuffer)); - while(psnC.isDataToSend()){ - psnC.handleNetwork(); - } - Serial.print("Ich:\t"); - Serial.println(arrayBuffer); - /*Serial.print(topicPub); - Serial.print(" | "); - Serial.print(strlen(topicPub)); - Serial.print(" | "); - Serial.print(arrayBuffer); - Serial.print(" | "); - Serial.print(strlen(arrayBuffer)); - Serial.print(" | "); - Serial.println(i); */ - //delay(1000); - } - } - dataReadDone = false; - } - #endif - } - #endif diff --git a/examples/sPSN_Broker/sPSN_Broker.ino b/examples/sPSN_Broker/sPSN_Broker.ino index 662ecf03641f9d69a3289d9f9be861024f9514bd..e2d142d2644ba0defd9a1c018852432d4ff17448 100644 --- a/examples/sPSN_Broker/sPSN_Broker.ino +++ b/examples/sPSN_Broker/sPSN_Broker.ino @@ -26,7 +26,7 @@ *date: 14.12.2020 *version: 1.0 */ - +#include <Arduino.h> #include "SoftwareSerial.h" #include "didacticNet.h" @@ -38,7 +38,7 @@ SoftwareSerial sSerial(10, 11); //Erzeuge Broker-Instanz -didacticPSNetBroker psnBroker; +DidacticPSNetBroker psnBroker; void setup() { //Starte Serielle Schnittstelle (zum PC) @@ -56,3 +56,5 @@ void loop() { //hier verarbeitet der Broker alle Daten psnBroker.handleNetwork(); } + + diff --git a/examples/sPSN_Chat/sPSN_Chat.ino b/examples/sPSN_Chat/sPSN_Chat.ino index df7dbe6bdd0abbdab2821ce70bb16757a8f9e34f..e7b228bf1438dc7015d2dcc90f5f7b8953117193 100644 --- a/examples/sPSN_Chat/sPSN_Chat.ino +++ b/examples/sPSN_Chat/sPSN_Chat.ino @@ -1,84 +1,69 @@ /** - *file: sPSN_Chat.ino - *author: letsgoING -> info@letsgoing.de + * file: sPSN_Chat.ino + * author: letsgoING -> info@letsgoing.de + * + * description: + * Dieses Programm ist ein Teil eines Beispiels für ein einfaches Pub-Sub-Chat-Netzwerk. * - *description: - * Dieses Programm ist ein Teil eines Beispiels für ein einfaches Pub-Sub-Chat-Netzwerk. - - * Für dieses Chat-Netzwerk werden mindestens 3 Arduinos benötigt: + * Für dieses Chat-Netzwerk werden mindestens 3 Arduinos benötigt: * - * Arduino1: sPSN_Broker.ino - * - IR-Sender an Pin 11 IR-Empfänger an Pin10 + * Arduino1: sPSN_Broker.ino + * - IR-Sender an Pin 11 IR-Empfänger an Pin10 * - * Arduino2-n: sPSN_Chat.ino (dieses Programm) - * - IR-Sender an Pin 11 IR-Empfänger an Pin10 + * Arduino2-n: sPSN_Chat.ino (dieses Programm) + * - IR-Sender an Pin 11 IR-Empfänger an Pin10 * Funktion: - * Am Arduino2-n kann ein eigner Name (Sende-Topic) angeben werden. - * Danach kann angeben werden wem zugehört werden soll (Empfangs-Topics). - * Arduino1 (Server) übernimmt das Verteilen der Nachrichten. + * Am Arduino2-n kann ein eigner Name (Sende-Topic) angeben werden. + * Danach kann angeben werden wem zugehört werden soll (Empfangs-Topics). + * Arduino1 (Server) übernimmt das Verteilen der Nachrichten. * - *date: 14.12.2020 - *version: 1.0 - */ - + * parameter: + * MAX_LEN_PAYLOAD = 20 Zeichen (didacticNet.h) + * MAX_LEN_TOPICS = 10 Zeichen (didacticNet.h) + * max Laenge der Eingegebenen Daten: 100 Zeichen (5*MAX_LEN_PAYLOAD) + * + * date: 13.07.2021 +*/ +#include <Arduino.h> #include "SoftwareSerial.h" #include "didacticNet.h" -//lege Geschwindigkeit für serielle Schnittstellen fest -#define SERIAL_BAUD 2400 - - -unsigned long lastTime = 0L; +#define SERIAL_BAUD 2400 //lege Geschwindigkeit für serielle Schnittstellen fest -bool dataReadDone = false; +char readData[MAX_LEN_PAYLOAD * 5] = {'\0'}; //Array für eingelesene Daten (mit Puffer für zu lange Eingaben) -//Maximale Nachrichten- und Topic-Länge: -//MAX_LEN_DATA = 20 Zeichen -//MAX_LEN_TOPICS = 10 Zeichen -//kann erhöht werden (z. B. #define MAX_LEN_TOPICS 20) , dann aber Arduino-Mega als Server sinnvoll +char topicSub[MAX_LEN_TOPICS] = {""}; //Array für neues Empfangs-Topic +char topicPub[MAX_LEN_TOPICS] = {""}; //Array für eigenes Sende-Topic -char readData[MAX_LEN_DATA * 5] = {'\0'}; -char arrayBuffer[MAX_LEN_DATA + 1] = {'\0'}; -int counter = 0; - -//Arrays für Empfangs- und Sende Topic -char topicSub[MAX_LEN_TOPICS] = {""}; -char topicPub[MAX_LEN_TOPICS] = {""}; - -void myCallback(char* mTopic, int mToLength, char* mData, int mDaLength) { +//Callback-Funktion wird bei neuen Daten automatisch aufgerufen +void clientCallback(char* mTopic, int mToLength, char* mData, int mDaLength) { Serial.print(mTopic); Serial.print(":\t"); Serial.println(mData); } -//lege Pins für SoftwareSerielle Schnittstelle fest -// Rx = 10 -> Empfänger | Tx = 11 -> Sender -SoftwareSerial sSerial(10, 11); +SoftwareSerial sSerial(10, 11); // SoftwareSerial an Rx = Pin10 -> Empfänger | Tx = Pin11 -> Sender -//Erzeuge Client-Instanz -didacticPSNetClient psnClient; +DidacticPSNetClient psnClient; //Erzeuge PubSub-Client-Instanz +SerialReader sReader; void setup() { - //Starte Serielle Schnittstelle (zum PC) - Serial.begin(SERIAL_BAUD); - //Starte SoftwareSerielle Schnittstelle (zu IR-Link-Modulen) - sSerial.begin(SERIAL_BAUD); + Serial.begin(SERIAL_BAUD); //Starte Serielle Schnittstelle (zum PC) - //Lege fest welche Serielle Schnittstelle für sPSN verwendet werden soll - //psnClient.setStream(sSerial); + sSerial.begin(SERIAL_BAUD); //Starte SoftwareSerielle Schnittstelle (zu IR-Link-Modulen) - //Lege Callback-Funktion fest (wird ausgeführt wenn neue Daten ankommen) - //psnClient.setCallback(myCallback); - - psnClient.begin(sSerial, myCallback); + psnClient.begin(sSerial, clientCallback); //Starte PubSubClient mit SoftwareSerial und Callbackfunktion "clientCallback" + sReader.begin(Serial); + //AUSGABE INFOTEXT Serial.print("Bitte den eigenen Namen mit einem # eingeben\nund mit Enter bestaetigen. -> "); Serial.println("#DeinName"); Serial.print("Gebe dann die Namen deiner Chatpartner mit einem @ ein. -> "); Serial.println("@ChatPartner"); - Serial.println("Anschließend kannst du mit ihnen schreiben"); + Serial.println("Anschließend kannst du mit ihnen schreiben."); + Serial.println("Pro Nachricht duerfen maximal 20 Zeichen eingegeben werden!"); Serial.println(); Serial.println("HINWEIS:"); Serial.println("Stelle das Zeilenende im SerialMonitor auf \"Zeilenumbruch (CR)\""); @@ -86,60 +71,44 @@ void setup() { } void loop() { - //Verarbeiten der Daten, prüfen ob Netzwerk frei und versenden der Daten - psnClient.handleNetwork(); - - //Lese Daten ein bis zum Zeichen Zeilenumbruch "CR" - if (Serial.available()) { - char buffer = Serial.read(); - readData[counter] = buffer; - //Wenn Zeilenumbruch "CR", dann merke, dass Einlesen fertig - //und setze String Abschluss "\0" ans Ende - if (buffer == 13) { - readData[counter] = '\0'; - counter = 0; - dataReadDone = true; - } else { - counter++; + + psnClient.handleNetwork(); //Verarbeiten der Daten, prüfen ob Netzwerk frei und versenden der Daten + + int nrOfAscii = sReader.readSerialData(readData, DN_ASCII_CR); //Einlesen der Nutzereingabe am SerialMonitor (Rueckgabewert = Anzahl der gelesenen Zeichen) + + if (nrOfAscii > 0) { //Wenn Daten fertig eingelesen wurden + + if (readData[0] == '@') { //Wenn '@' vorne steht, dann neuer Chatpartner anlegen (neues Topic abonnieren) + strcpy(readData, &readData[1]); //verschiebe um ein Zeichen (entferne '@') + psnClient.subscribe(readData); //Lege fest zu welchem Topic Daten empfangen werden sollen (den Namen des Chatpartners) + + Serial.print("Nachrichten von "); //Ausgabe welches Topic abonniert wurde + Serial.print(readData); + Serial.println(" abonniert."); } - } + else if (readData[0] == '!') { //Wenn '@' vorne steht, dann neuer Chatpartner anlegen (neues Topic abonnieren) + strcpy(readData, &readData[1]); //verschiebe um ein Zeichen (entferne '!') + psnClient.unsubscribe(readData); //Lege fest zu welchem Topic Daten empfangen werden sollen (den Namen des Chatpartners) - //Wenn Daten fertig eingelesen wurden - if (dataReadDone && strlen(readData) <= MAX_LEN_DATA * 5 ) { - //Wenn "@" vorne steht, dann neuer Chatpartner anlegen (neues Topic dem zugehört wird) - if (readData[0] == '@') { - //kopiere das neue Topic (den Namen des Chatpartners) in das passende Array - strcpy(readData, readData + 1); - //Lege fest zu welchem Topic Daten empfangen werden sollen (den Namen des Chatpartners) - psnClient.subscribe(readData); - Serial.print("Kontakt zu "); + Serial.print("Nachrichten von "); //Ausgabe welches Topic abonniert wurde Serial.print(readData); - Serial.println(" hergestellt"); + Serial.println(" nicht mehr abonniert."); } - //Wenn "#" vorne steht, dann neuer eigener Name (neues Topic unter dem gesendet wird) - else if (readData[0] == '#') { - //kopiere das neue Topic (deinen neuen Namen) in das passende Array - strcpy(topicPub, readData + 1); - Serial.print("Dein Name:\t"); + + else if (readData[0] == '#') { //Wenn '#' vorne steht, dann neuer eigener Name (neues Topic unter dem gesendet wird) + //strcpy(topicPub, readData + 1); //kopiere das neue Topic (deinen neuen Namen) in das passende Array (+ 1 = entferne '#') + strcpy(topicPub, &readData[1]); //kopiere das neue Topic (deinen neuen Namen) in das passende Array (+ 1 = entferne '#') + + Serial.print("Dein Name:\t"); //Ausgabe unter welchem Topic veröffentlicht wird Serial.println(topicPub); } - //Wenn normale Nachrichten eingegeben wurden, dann Daten unter eigenem Topic versenden - else { - for (int i = 0; i * MAX_LEN_DATA < strlen(readData); i++) { - memset(arrayBuffer, '\0', MAX_LEN_DATA + 1); - strncpy(arrayBuffer, &readData[MAX_LEN_DATA * i], MAX_LEN_DATA); - //Serial.print("ReadData: ");Serial.print(readData);Serial.print("\t");Serial.print(arrayBuffer); - psnClient.publish(topicPub, arrayBuffer); - //Solange Daten zu versenden sind (wenn Text länger als max Länge einer Nachricht) - while (psnClient.isDataToSend()) { - //Verarbeiten der Daten, prüfen ob Netzwerk frei und versenden der Daten - psnClient.handleNetwork(); - } - Serial.print("Ich:\t"); - Serial.println(arrayBuffer); - } + + else { //Wenn "normale" Nachrichten eingegeben wurden, dann Daten unter eigenem Topic versenden + psnClient.publish(topicPub, readData); //Bereite eingegebene Daten zum Senden vor + + Serial.print("Ich:\t"); //Ausgabe was unter deinem Topic veröffentlicht wird + Serial.println(readData); } - dataReadDone = false; } -} +} \ No newline at end of file diff --git a/examples/sPSN_Client1/sPSN_Client1.ino b/examples/sPSN_Client1/sPSN_Client1.ino index 61997358952e0f61d7bcbb755b57b0f1135b7650..e17005086f286493089dcf3ca436d9ab92b2147f 100644 --- a/examples/sPSN_Client1/sPSN_Client1.ino +++ b/examples/sPSN_Client1/sPSN_Client1.ino @@ -7,115 +7,76 @@ * Für das Netwerk werden 3 Arduinos mit IR-Link-Modulen benötigt: * * Arduino1: sPSN_Broker.ino - * - IR-Sender an Pin 11 IR-Empfänger an Pin10 + * + * Arduino2: sPSN_Client1.ino (dieses Programm) -> Sendet Wert des Potis | Empfängt Zustand des Tasters + * (Poti an PinA0 | LED an Pin5) + * + * Arduino3: sPSN_Client2.ino -> Sendet Zustand des Tasters | Empfängt Wert des Potis + * (Taster an Pin2 | LED an Pin5) * - * Arduino2: sPSN_Client1.ino (dieses Programm) - * - IR-Sender an Pin 11 IR-Empfänger an Pin10 - * - Taster an Pin2 - * - LED an Pin9 - * - * Arduino3: sPSN_Client2.ino - * - IR-Sender an Pin 11 IR-Empfänger an Pin10 - * - Poti an PinA0 - * - LED an Pin9 - * - * Funktion: - * Mit dem Poti an Arduino3 kann die LED am Arduino2 gedimmt werden. - * Mit dem Taster an Arduino2 kann die LED an Arduino2 ein-/ausgeschaltet werden. - * Arduino1 übernimmt das Verteilen der Nachrichten. - * - *date: 14.12.2020 - *version: 1.0 + * parameter: + * MAX_LEN_PAYLOAD = 20 Zeichen (didacticNet.h) + * MAX_LEN_TOPICS = 10 Zeichen (didacticNet.h) + * + *date: 06.07.2021 */ - +#include <Arduino.h> #include "SoftwareSerial.h" #include "didacticNet.h" -//lege Geschwindigkeit für serielle Schnittstellen fest -#define SERIAL_BAUD 2400 +#define SERIAL_BAUD 2400 //lege Geschwindigkeit für serielle Schnittstellen fest +#define LED_PIN 5 +#define POTI_PIN A0 -byte ledPin = 9; -byte potiPin = A0; +#define THRESHOLD 10 //Schwellwert für min. Wertänderung -int lastValue = 0; +char topicPublish[MAX_LEN_TOPICS] = "potiVal"; //Topic unter dem (eigene) Daten veröffentlicht werden +char topicSubscribe[MAX_LEN_TOPICS] = "btnState"; //Topic (von anderem TN) das abboniert werden soll +char payload[MAX_LEN_PAYLOAD] = {0}; -//Maximale Nachrichten- und Topic-Länge: -//MAX_LEN_DATA = 20 Zeichen -//MAX_LEN_TOPICS = 10 Zeichen -//kann erhöht werden (z. B. #define MAX_LEN_TOPICS 20) , dann aber Arduino-Mega als Server sinnvoll +SoftwareSerial sSerial(10, 11); //Erzeuge SoftwareSerial-Instanz mit Rx = Pin10 -> Empfänger | Tx = Pin11 -> Sender -//Arrays für Empfangs- und Sende Topic -char topicSub[MAX_LEN_TOPICS] = "buttonVal"; -char topicPub[MAX_LEN_TOPICS] = "potiVal"; +DidacticPSNetClient psnClient; //Erzeuge PubSub-Client-Instanz -void myCallback(char* mTopic, int mToLength, char* mData, int mDaLength) { - Serial.println("Callback: "); - Serial.print("Topic: "); + +//Callback-Funktion - wird beim Empfang neuer Daten aufgerufen +void clientCallback(char* mTopic, int mToLength, char* mData, int mDaLength) { + Serial.print("CB: "); Serial.print(mTopic); - Serial.print("\tData: "); + Serial.print(" | "); Serial.println(mData); - //Setze Ausgang entsprechend dem gesendeten Wert - digitalWrite(ledPin, (bool)atoi(mData)); + boolean static stateLED = false; + + //Wechsle Zustand der Variable "stateLED" wenn Taster beim Sender gedrueckt wurde + if(bool(atoi(mData)) == true){ + stateLED = !stateLED; + digitalWrite(LED_PIN, stateLED); //Setze Ausgang entsprechend dem empfangenen Wert + } } -//lege Pins für SoftwareSerielle Schnittstelle fest -// Rx = 10 -> Empfänger | Tx = 11 -> Sender -SoftwareSerial sSerial(10, 11); - -//Erzeuge Client-Instanz -didacticPSNetClient psnClient; - - void setup() { - //Starte Serielle Schnittstelle (zum PC) - Serial.begin(SERIAL_BAUD); - - Serial.print("\nStarting sPSNet: \nlistening to:\t"); - Serial.println(topicSub); - Serial.print("sending to:\t"); - Serial.println(topicPub); + + Serial.begin(SERIAL_BAUD); //Starte Serielle Schnittstelle (zum PC) + sSerial.begin(SERIAL_BAUD); //Starte SoftwareSerielle Schnittstelle (zu IR-Link-Modulen) - //Starte SoftwareSerielle Schnittstelle (zu IR-Link-Modulen) - sSerial.begin(SERIAL_BAUD); + pinMode(LED_PIN,OUTPUT); - pinMode(ledPin, OUTPUT); + psnClient.begin(sSerial, clientCallback); //Starte PubSub Client an SoftwareSerial Schnittstelle + //psnClient.begin(Serial, clientCallback); //Starte PubSub Client an Serial Schnittstelle - //Lege fest welche Serielle Schnittstelle für sPSN verwendet werden soll - //psnClient.setStream(sSerial); + psnClient.subscribe(topicSubscribe); //Lege fest zu welchem Topic Daten empfangen werden sollen +} - //Lege Callback-Funktion fest (wird ausgeführt wenn neue Daten ankommen) - //psnClient.setCallback(myCallback); +void loop() { - psnClient.begin(sSerial, myCallback); + psnClient.handleNetwork(); //Verarbeiten der Daten, prüfen ob Netzwerk frei und versenden der Daten - //Lege fest zu welchem Topic Daten empfangen werden sollen - psnClient.subscribe(topicSub); + int currentValue = analogRead(POTI_PIN); //lese Poti ein und speichere Wert + psnClient.publishOnChange(topicPublish, currentValue, THRESHOLD); + } -void loop() { - //Verarbeiten der Daten, prüfen ob Netzwerk frei und versenden der Daten - psnClient.handleNetwork(); - - //lese Poti ein und speichere Wert - int currentValue = analogRead(potiPin); - - //Übertrage Werte nur, wenn sie sich geändert haben - if (abs(currentValue - lastValue) > 20) { - //warte kurz, um nicht zu oft zu senden - delay(500); - //lese den Poti erneut ein - currentValue = analogRead(potiPin); - //setze den Inhalt des Puffer-Arrays auf 0 - char data[MAX_LEN_DATA] = {0}; - //wandle int-Wert in ASCII-Zeichen - itoa(currentValue, data, 10); - //sende analog-Wert - psnClient.publish(topicPub, data); - //speichere aktuellen Wert, um zu erkennen, ob er sich ändert - lastValue = currentValue; - } -} diff --git a/examples/sPSN_Client2/sPSN_Client2.ino b/examples/sPSN_Client2/sPSN_Client2.ino index dc3be3bf1df9dec594464c653d6dfe7714bc963f..5e49f515a0ae66a38edcbd61446f3009e42a0ba3 100644 --- a/examples/sPSN_Client2/sPSN_Client2.ino +++ b/examples/sPSN_Client2/sPSN_Client2.ino @@ -1,119 +1,78 @@ /** - *file: sPSN_Client2 + *file: sPSN_Client1 *author: letsgoING -> info@letsgoing.de * *description: * Dieses Programm ist ein einfaches Beispiel für ein einfaches Pub-Sub-Netzwerk. * Für das Netwerk werden 3 Arduinos mit IR-Link-Modulen benötigt: * - * Arduino1: sPSN_Broker..ino - * - IR-Sender an Pin 11 IR-Empfänger an Pin10 + * Arduino1: sPSN_Broker.ino + * + * Arduino2: sPSN_Client1.ino -> Sendet Wert des Potis | Empfängt Zustand des Tasters + * (Poti an PinA0 | LED an Pin5) + * + * Arduino3: sPSN_Client2.ino (dieses Programm) -> Sendet Zustand des Tasters | Empfängt Wert des Potis + * (Taster an Pin2 | LED an Pin5) * - * Arduino2: sPSN_Client1.ino - * - IR-Sender an Pin 11 IR-Empfänger an Pin10 - * - Taster an Pin2 - * - LED an Pin9 - * - * Arduino3: sPSN_Client2.ino (dieses Programm) - * - IR-Sender an Pin 11 IR-Empfänger an Pin10 - * - Poti an PinA0 - * - LED an Pin9 - * - * Funktion: - * Mit dem Poti an Arduino3 kann die LED am Arduino2 gedimmt werden. - * Mit dem Taster an Arduino2 kann die LED an Arduino2 ein-/ausgeschaltet werden. - * Arduino1 übernimmt das Verteilen der Nachrichten. - * - *date: 14.12.2020 - *version: 1.0 + *date: 06.07.2021 */ +#include <Arduino.h> #include "SoftwareSerial.h" #include "didacticNet.h" -//lege Geschwindigkeit für serielle Schnittstellen fest -#define SERIAL_BAUD 2400 +#define SERIAL_BAUD 2400 //lege Geschwindigkeit für serielle Schnittstellen fest -byte ledPin = 9; -byte buttonPin = 2; +#define LED_PIN 5 +#define LED2_PIN 6 +#define BUTTON_PIN 2 -bool lastState = 0; +char topicPublish[MAX_LEN_TOPICS] = "btnState"; //Topic unter dem (eigene) Daten veröffentlicht werden +char topicSubscribe[MAX_LEN_TOPICS] = "potiVal"; //Topic (von anderem TN) das abboniert werden soll -//Maximale Nachrichten- und Topic-Länge: -//MAX_LEN_DATA = 20 Zeichen -//MAX_LEN_TOPICS = 10 Zeichen -//kann erhöht werden (z. B. #define MAX_LEN_TOPICS 20) , dann aber Arduino-Mega als Server sinnvoll +char payload[MAX_LEN_PAYLOAD] = {0}; +boolean newData = false; +boolean ledState = false; -//Arrays für Empfangs- und Sende Topic -char topicSub[MAX_LEN_TOPICS] = {"potiVal"}; -char topicPub[MAX_LEN_TOPICS] = {"buttonVal"}; +SoftwareSerial sSerial(10, 11); //Erzeuge SoftwareSerial-Instanz mit Rx = Pin10 -> Empfänger | Tx = Pin11 -> Sender -void myCallback(char* mTopic, int mToLength, char* mData, int mDaLength) { - Serial.println("Callback: "); - Serial.print("Topic: "); - Serial.print(mTopic); - Serial.print("\tData: "); - Serial.println(mData); +DidacticPSNetClient psnClient; //Erzeuge PubSub-Client-Instanz +UnblockingTimer uTimer; - //Setze Ausgang entsprechend dem gesendeten Wert - analogWrite(ledPin, map(atoi(mData), 0, 1023, 0, 255)); -} +//Callback-Funktion - wird beim Empfang neuer Daten aufgerufen +void clientCallback(char* mTopic, int mToLength, char* mData, int mDaLength) { + Serial.print("CB: "); + Serial.print(mTopic); + Serial.print(" | "); + Serial.println(mData); -//lege Pins für SoftwareSerielle Schnittstelle fest -// Rx = 10 -> Empfänger | Tx = 11 -> Sender -SoftwareSerial sSerial(10, 11); + int valuePoti = atoi(mData); //wandle ASCII-Zeichen in Wert + //Alternativ: sscanf(mData, "%d", &stateLED); //wandle ASCII-Zeichen in Wert -//Erzeuge Client-Instanz -didacticPSNetClient psnClient; + int valueLED = constrain(map(valuePoti, 0, 1023, 0, 255), 0, 255); //passe analogRead-Wert für analogWrite an + + analogWrite(LED_PIN, valueLED); //Setze Ausgang entsprechend dem empfangenen Wert + analogWrite(LED2_PIN, 255-valueLED); //Setze Ausgang invertiert zum empfangenen Wert +} void setup() { - //Starte Serielle Schnittstelle (zum PC) - Serial.begin(SERIAL_BAUD); - - Serial.print("\nStarting sPSNet: \nlistening to:\t"); - Serial.println(topicSub); - Serial.print("sending to:\t"); - Serial.println(topicPub); + + Serial.begin(SERIAL_BAUD); //Starte Serielle Schnittstelle (zum PC) + sSerial.begin(SERIAL_BAUD); //Starte SoftwareSerielle Schnittstelle (zu IR-Link-Modulen) - //Starte SoftwareSerielle Schnittstelle (zu IR-Link-Modulen) - sSerial.begin(SERIAL_BAUD); + psnClient.begin(sSerial, clientCallback); //Starte PubSub Client an SoftwareSerial Schnittstelle + //psnClient.begin(Serial, clientCallback); //Starte PubSub Client an Serial Schnittstelle - pinMode(ledPin, OUTPUT); - pinMode(buttonPin, INPUT); - - //Lege fest welche Serielle Schnittstelle für sPSN verwendet werden soll - //psnClient.setStream(sSerial); + psnClient.subscribe(topicSubscribe); //Lege fest zu welchem Topic Daten empfangen werden sollen +} - //Lege Callback-Funktion fest (wird ausgeführt wenn neue Daten ankommen) - //psnClient.setCallback(myCallback); +void loop() { - psnClient.begin(sSerial, myCallback); + psnClient.handleNetwork(); //Verarbeiten der Daten, prüfen ob Netzwerk frei und versenden der Daten - //Lege fest zu welchem Topic Daten empfangen werden sollen - psnClient.subscribe(topicSub); + boolean buttonState = digitalRead(BUTTON_PIN); //lese Taster ein und speichere Wert + psnClient.publishOnChange(topicPublish, buttonState); //bereite Topic und Nutzdaten zum senden vor, wenn sich buttonState ändert } -void loop() { - //Verarbeiten der Daten, prüfen ob Netzwerk frei und versenden der Daten - psnClient.handleNetwork(); - - //lese aktuellen Zustand des Tasters ein - bool currentState = digitalRead(buttonPin); - - //Wenn Flanke erkannt (fallend/steigend) dann übertrage den aktuellen Wert - if (lastState != currentState) { - //setze den Inhalt des Puffer-Arrays auf 0 - char data[MAX_LEN_DATA] = {0}; - //wandle bool-Wert in ASCII-Zeichen - itoa(currentState, data, 10); - //sende digital-Wert - psnClient.publish(topicPub, data); - //warte kurz, um nicht zu oft zu senden - delay(500); - } - - //speichere aktuellen Wert, um zu erkennen, ob er sich ändert - lastState = currentState; -} diff --git a/examples/sPSN_ClientMinimal/sPSN_ClientMinimal.ino b/examples/sPSN_ClientMinimal/sPSN_ClientMinimal.ino new file mode 100644 index 0000000000000000000000000000000000000000..1805cb58108e1a730c26dcee0223ef58ced973e2 --- /dev/null +++ b/examples/sPSN_ClientMinimal/sPSN_ClientMinimal.ino @@ -0,0 +1,60 @@ +/** + *file: sPSN_Client1 + *author: letsgoING -> info@letsgoing.de + * + *description: + * Dieses Programm ist das Minimal-Beispiel für ein einfaches Pub-Sub-Netzwerk. + * Es wird jede Sekunde ein analoger Messwert (AO) übertragen + * und die empfangenen Daten auf dem SerialMonitor ausgegeben + * + * Für das Netwerk werden 3 oder mehr Arduinos mit IR-Link-Modulen benötigt: + * + * Arduino1: sPSN_Broker.ino + * + * Arduino2-n: sPSN_ClientMinimal.ino (dieses Programm) + * + * parameter: + * MAX_LEN_PAYLOAD = 20 Zeichen (didacticNet.h) + * MAX_LEN_TOPICS = 10 Zeichen (didacticNet.h) + * + *date: 06.07.2021 + */ +#include <Arduino.h> +#include "SoftwareSerial.h" +#include "didacticNet.h" + +SoftwareSerial sSerial(10, 11); //Erzeuge SoftwareSerial-Instanz mit Rx = Pin10 -> Empfänger | Tx = Pin11 -> Sender + +DidacticPSNetClient psnClient; //Erzeuge PubSub-Client-Instanz +UnblockingTimer uTimer; + +//Callback-Funktion - wird beim Empfang neuer Daten aufgerufen +void clientCallback(char* mTopic, int mToLength, char* mPayload, int mPayloadLength) { + Serial.print("Nachricht von: "); + Serial.print(mTopic); + Serial.print(" - "); + Serial.println(mPayload); +} + + +void setup() { + + Serial.begin(2400); //Starte Serielle Schnittstelle (zum PC) + sSerial.begin(2400); //Starte SoftwareSerielle Schnittstelle (zu IR-Link-Modulen) + + psnClient.begin(sSerial, clientCallback); //Starte PubSub Client an SoftwareSerial Schnittstelle + + //Hier EMPFANGS-TOPIC ANPASSEN -> default "client2" + psnClient.subscribe("client2"); //Lege fest zu welchem Topic Daten empfangen werden sollen +} + +void loop() { + + psnClient.handleNetwork(); //Verarbeiten der Daten, prüfen ob Netzwerk frei und versenden der Daten + + int currentValue = analogRead(A0); //lese Poti ein und speichere Wert + + //Hier SENDE-TOPIC ANPASSEN -> default "client1" + psnClient.publish("client1", currentValue); +} + diff --git a/examples/sPSN_PingTest/sPSN_PingTest.ino b/examples/sPSN_PingTest/sPSN_PingTest.ino new file mode 100644 index 0000000000000000000000000000000000000000..03f7594556151735e92def76407c3ca8c2ca223f --- /dev/null +++ b/examples/sPSN_PingTest/sPSN_PingTest.ino @@ -0,0 +1,114 @@ +/** + file: sPSN_HWTest + author: letsgoING -> info@letsgoing.de + + description: + Test-Software um Clients im Netzwerk "anzumelden" + + date: 01.12.2021 +*/ + +/** + *file: sPSN_PingTest + *author: letsgoING -> info@letsgoing.de + * + *description: + * Test-Software um Clients im Netzwerk "anzumelden" + * Für das Netwerk werden min. 3 Arduinos mit IR-Link-Modulen benötigt: + * + * Arduino1: sPSN_Broker.ino + * + * Arduino2: sPSN_PongTester.ino -> fragt Clients ab + * + * Arduino3: sPSN_PingTest.ino -> reagiert auf PongTester + * + *date: 01.12.2021 + */ + +#include "Arduino.h" + +#include "SoftwareSerial.h" +#include "didacticNet.h" + +//lege Geschwindigkeit für serielle Schnittstellen fest +#define SERIAL_BAUD 2400 + +//HIER CLIENT-NUMMER ANPASSEN +//******************************* +#define MY_NUMBER 1 +//******************************* + +#define LED_PIN 5 +#define LED2_PIN 6 +#define BUTTON_PIN 2 + + +//lege Pins für SoftwareSerielle Schnittstelle fest +// Rx = 10 -> Empfänger | Tx = 11 -> Sender +SoftwareSerial sSerial(10, 11); + +//Erzeuge Client-Instanz +DidacticPSNetClient psnClient; + +EdgeDetector eDetector; + +//Arrays für Empfangs- und Sende Topic +char topicSub[MAX_LEN_TOPICS] = ""; +char topicPub[MAX_LEN_TOPICS] = ""; + +char payloadSend[] = "Ping"; + +bool ledState = true; + +void myCallback(char* mTopic, int mToLength, char* mData, int mDaLength) { + Serial.println(mData); + digitalWrite(LED_BUILTIN, HIGH); +} + + +void setup() { + //Starte Serielle Schnittstelle (zum PC) + Serial.begin(SERIAL_BAUD); + delay(2000); + + sprintf(topicPub, "%02d", MY_NUMBER); + sprintf(topicSub, "%02d", MY_NUMBER); + + Serial.print("\nStarting sPSNet as Client "); + Serial.println(topicPub); + + + //Starte SoftwareSerielle Schnittstelle (zu IR-Link-Modulen) + sSerial.begin(SERIAL_BAUD); + + pinMode(LED_BUILTIN, OUTPUT); + pinMode(LED_PIN, OUTPUT); + pinMode(LED2_PIN, OUTPUT); + + digitalWrite(LED_BUILTIN, LOW); + digitalWrite(LED_PIN, ledState); + digitalWrite(LED2_PIN, ledState); + + psnClient.begin(sSerial, myCallback); + + psnClient.publish(topicPub, payloadSend); + while (!psnClient.handleNetwork()); + + psnClient.subscribe(topicSub); + while (!psnClient.handleNetwork()); + + psnClient.setInterval(10000L); +} + +void loop() { + //Verarbeiten der Daten, prüfen ob Netzwerk frei und versenden der Daten + psnClient.handleNetwork(); + + psnClient.publish(topicPub, payloadSend); + + if (eDetector.edgeDetected(digitalRead(BUTTON_PIN)) == RISING) { + ledState = !ledState; + digitalWrite(LED_PIN, !ledState); + digitalWrite(LED2_PIN, ledState); + } +} diff --git a/examples/sPSN_PongTester/sPSN_PongTester.ino b/examples/sPSN_PongTester/sPSN_PongTester.ino new file mode 100644 index 0000000000000000000000000000000000000000..bddbb0f9f95248e967c2bb387d899600e7140c44 --- /dev/null +++ b/examples/sPSN_PongTester/sPSN_PongTester.ino @@ -0,0 +1,93 @@ + +/** + *file: sPSN_PongTester + *author: letsgoING -> info@letsgoing.de + * + *description: + * Test-Software um Clients im Netzwerk "anzumelden" + * Für das Netwerk werden min. 3 Arduinos mit IR-Link-Modulen benötigt: + * + * Arduino1: sPSN_Broker.ino + * + * Arduino2: sPSN_PongTester.ino -> fragt Clients ab + * + * Arduino3: sPSN_PingTest.ino -> reagiert auf PongTester + * + *date: 01.12.2021 + */ + +#include "Arduino.h" + +#include "SoftwareSerial.h" +#include "didacticNet.h" + +//lege Geschwindigkeit für serielle Schnittstellen fest +#define SERIAL_BAUD 2400 + +//HIER CLIENT_ZAHL ANPASSEN +//******************************* +#define MAX_NR_CLIENTS 15 +//******************************* + +//lege Pins für SoftwareSerielle Schnittstelle fest +// Rx = 10 -> Empfänger | Tx = 11 -> Sender +SoftwareSerial sSerial(10, 11); + +//Erzeuge Client-Instanz +DidacticPSNetClient psnClient; + +//Arrays für Empfangs- und Sende Topic +char topicSub[MAX_LEN_TOPICS] = ""; +char topicPub[MAX_LEN_TOPICS] = ""; + +char payloadSend[] = "Pong"; + +bool ledState = true; + +void myCallback(char* mTopic, int mToLength, char* mData, int mDaLength) { + Serial.println(mTopic); + //reply pong + strcpy(topicPub, mTopic); + if (!strcmp(mData, "Ping")) { + Serial.print(" "); + Serial.println(mTopic); + psnClient.publish(topicPub, payloadSend); + } +} + + +void setup() { + //Starte Serielle Schnittstelle (zum PC) + Serial.begin(SERIAL_BAUD); + delay(2000); + + Serial.print("\nSearching for "); + Serial.print(MAX_NR_CLIENTS); + Serial.println(" sPSNet-Clients"); + + //Starte SoftwareSerielle Schnittstelle (zu IR-Link-Modulen) + sSerial.begin(SERIAL_BAUD); + + pinMode(LED_BUILTIN, OUTPUT); + digitalWrite(LED_BUILTIN, LOW); + + psnClient.begin(sSerial, myCallback); + + //psnClient.subscribe("*"); + Serial.println("Topics: "); + for (int clientNr = 0; clientNr < MAX_NR_CLIENTS; ++clientNr) { + sprintf(topicSub, "%02d", clientNr); + psnClient.subscribe(topicSub); + while (!psnClient.handleNetwork()); + Serial.print(topicSub); + Serial.print(" "); + } + + Serial.println("\n\nReady for Clients..."); + +} + +void loop() { + //Verarbeiten der Daten, prüfen ob Netzwerk frei und versenden der Daten + psnClient.handleNetwork(); +} diff --git a/examples/sPSN_Server/sPSN_Server.ino b/examples/sPSN_Server/sPSN_Server.ino deleted file mode 100644 index 849fcb72e38200df68acac04fe11957d251b610e..0000000000000000000000000000000000000000 --- a/examples/sPSN_Server/sPSN_Server.ino +++ /dev/null @@ -1,58 +0,0 @@ -/** - *file: sPSN_Server.ino - *author: letsgoING -> info@letsgoing.de - * - *description: - * Dieses Programm ist ein Teil eines Beispiels für ein einfaches Pub-Sub-Netzwerk. - * - * Für ein Sensor-Netwerk werden 3 Arduinos mit IR-Link-Modulen benötigt: - * Arduino1: sPSN_Broker.ino (dieses Programm) - * - IR-Sender an Pin 11 IR-Empfänger an Pin10 - * Arduino2: sPSN_Client1.ino - * - IR-Sender an Pin 11 IR-Empfänger an Pin10 - * - Taster an Pin2 - * - LED an Pin9 - * Arduino3: sPSN_Client2.ino - * - IR-Sender an Pin 11 IR-Empfänger an Pin10 - * - Poti an PinA0 - * - LED an Pin9 - * - * Für ein Chat-Netzwerk werden mindestens 3 Arduinos benötigt: - * Arduino1: sPSN_Server.ino (dieses Programm) - * - IR-Sender an Pin 11 IR-Empfänger an Pin10 - * Arduino2-n: sPSN_Chat.ino - * - IR-Sender an Pin 11 IR-Empfänger an Pin10 - * - *date: 14.12.2020 - *version: 1.0 - */ - -#include "SoftwareSerial.h" -#include "didacticNet.h" - -//lege Geschwindigkeit für serielle Schnittstellen fest -#define SERIAL_BAUD 2400 - -//lege Pins für SoftwareSerielle Schnittstelle fest -// Rx = 10 -> Empfänger | Tx = 11 -> Sender -SoftwareSerial sSerial(10, 11); - -//Erzeuge Server-Instanz -didacticPSNetServer psnBroker; - -void setup() { - //Starte Serielle Schnittstelle (zum PC) - Serial.begin(SERIAL_BAUD); - - //Starte SoftwareSerielle Schnittstelle (zu IR-Link-Modulen) - sSerial.begin(SERIAL_BAUD); - - //Lege fest welche Serielle Schnittstelle für sPSN verwendet werden soll - //psnBroker.setStream(sSerial); - psnClient.begin(sSerial); -} - -void loop() { - //hier verarbeitet der Server alle Daten - psnBroker.handleNetwork(); -} diff --git a/keywords.txt b/keywords.txt index 65ad9e5f2a21b798d29803b614b1849510abd967..542b41df93aae8db0b8510a1d1aca8d2970ec1eb 100644 --- a/keywords.txt +++ b/keywords.txt @@ -5,10 +5,13 @@ ####################################### # Datatypes (KEYWORD1) ####################################### - -didacticPSNet KEYWORD1 -didacticPSNetBroker KEYWORD1 -didacticPSNetClient KEYWORD1 +DidacticPSNet KEYWORD1 +DidacticPSNetBroker KEYWORD1 +DidacticPSNetClient KEYWORD1 +EdgeDetector KEYWORD1 +ChangeDetector KEYWORD1 +UnblockingTimer KEYWORD1 +SerialReader KEYWORD1 ####################################### # Methods and Functions (KEYWORD2) @@ -17,17 +20,22 @@ setCallback KEYWORD2 setStream KEYWORD2 handleNetwork KEYWORD2 isDataToSend KEYWORD2 +setInterval KEYWORD2 publish KEYWORD2 +publishOnChange KEYWORD2 subscribe KEYWORD2 unsubscribe KEYWORD2 +timeElapsed KEYWORD2 +readSerialData KEYWORD2 + ####################################### # Constants (LITERAL1) ####################################### -MSG_PRELIMITER LITERAL1 -MSG_DELIMITER LITERAL1 -MSG_SEPARATOR LITERAL1 +MSG_PRELIMITER LITERAL1 +MSG_DELIMITER LITERAL1 +MSG_SEPARATOR LITERAL1 MSG_PUBLISH LITERAL1 MSG_SUBSCRIBE LITERAL1 @@ -37,4 +45,12 @@ MSG_TOPIC_MULTI LITERAL1 MAX_NR_TOPICS_CLIENT LITERAL1 MAX_NR_TOPICS_BROKER LITERAL1 MAX_LEN_TOPICS LITERAL1 -MAX_LEN_DATA LITERAL1 +MAX_LEN_PAYLOAD LITERAL1 + +DN_ASCII_CR LITERAL1 +DN_ASCII_NL LITERAL1 + +DN_PUBLISH_SUCCESSULL LITERAL1 +DN_ERROR_NO_ERROR LITERAL1 +DN_ERROR_TOPIC_LEN LITERAL1 +DN_ERROR_PAYLOAD_LEN LITERAL1 \ No newline at end of file diff --git a/library.properties b/library.properties index fb194340583c56434140063784b0e64c7c172c3e..1333af140fa1c79bf7ca58beab746abef135e8c1 100644 --- a/library.properties +++ b/library.properties @@ -1,9 +1,9 @@ name=didacticNetwork -version=0.2 +version=0.9 author=letsgoING -maintainer=letsgoING <info@letsgoing.org> +maintainer=letsgoING <https://letsgoing.org> sentence=Library for implementing didactic reduced networks on Arduino boards. category=Communication paragraph=Library for implementing networks on Arduino boards. The functionalities correspond to state of the art technologies, which are didactically reduced. url=https://gitlab.reutlingen-university.de/letsgoing/libraries/didacticnetwork -includes=didacticNetwork.h +includes=didacticNet.h diff --git a/src/didacticNet.cpp b/src/didacticNet.cpp index 67fc6c552a253b1d8e3b8626c6a339b2ea53459f..80a9f5d3c63849e5d552e052b0ffd9f9af3ac228 100644 --- a/src/didacticNet.cpp +++ b/src/didacticNet.cpp @@ -8,34 +8,33 @@ #include "Arduino.h" #include "didacticNet.h" - //************************************************************************** -//BASIC +//ROOT //************************************************************************** -didacticPSNet::didacticPSNet(){} +DidacticPSNet::DidacticPSNet(){} -didacticPSNet::~didacticPSNet(){} +DidacticPSNet::~DidacticPSNet(){} -void didacticPSNet::begin(Stream& _port){ +void DidacticPSNet::begin(Stream& _port){ setStream(_port); } -void didacticPSNet::begin(Stream& _port, PSNET_CALLBACK_SIGNATURE){ +void DidacticPSNet::begin(Stream& _port, PSNET_CALLBACK_SIGNATURE){ setStream(_port); setCallback(callback); } -didacticPSNet& didacticPSNet::setCallback(PSNET_CALLBACK_SIGNATURE){ +DidacticPSNet& DidacticPSNet::setCallback(PSNET_CALLBACK_SIGNATURE){ this->callback = callback; return *this; } -void didacticPSNet::setStream(Stream& stream){ +void DidacticPSNet::setStream(Stream& stream){ _port = &stream; } -bool didacticPSNet::handleNetwork(){ - if(_waitingTime <= millis()){ +bool DidacticPSNet::handleNetwork(){ + //if(_waitingTime <= millis()){ if(checkData()){ if(recieveData()){ //Serial.print("Message filter: ");Serial.println(_readBufferMessage[1]); @@ -44,65 +43,75 @@ bool didacticPSNet::handleNetwork(){ handleData(); } } - _waitingTime = millis()+ random(CSMA_MIN_DELAY_MS, CSMA_MAX_DELAY_MS); + _waitingTimeCSMA = millis()+ random(CSMA_MIN_DELAY_MS, CSMA_MAX_DELAY_MS); } - else if(_dataToSend){ - //send data to network + //else if(_dataToSend){ + if(_dataToSend && _waitingTimeSend <= millis() && _waitingTimeCSMA <= millis()){ + //send data to network //TODO: test added CSMA_CHECKDELAY + 2nd checkData() unsigned long delayStartTime = micros(); while(micros() < delayStartTime + CSMA_CHECK_DELAY_US); //delayMicroseconds(CSMA_CHECK_DELAY_US); //removed: blocking SoftSerial-interrupts if(!checkData()){ - if(!sendData()){ - return false; - } + if(!sendData()){ + return false; + } else{ _dataToSend = false; - _waitingTime = millis()+ random(CSMA_MID_DELAY_MS, CSMA_MAX_DELAY_MS); + //_waitingTime = millis()+ random(CSMA_MID_DELAY_MS, CSMA_MAX_DELAY_MS); + _waitingTimeSend = millis() + _intervalTime;//random(CSMA_MID_DELAY_MS, CSMA_MAX_DELAY_MS); } } - } - } + } + //} return true; } -bool didacticPSNet::isDataToSend(){ +bool DidacticPSNet::isDataToSend(){ return _dataToSend; } -bool didacticPSNet::sendData(){ +bool DidacticPSNet::sendData(){ int counter = 0; - while(_sendBufferMessage[counter]!= '\0'){ + bool messageSent = false; + while(!messageSent){ + if(counter >= MAX_LEN_TOPICS + MAX_LEN_PAYLOAD + LEN_OVERHEAD - 1){ + //TODO: check!!! + _sendBufferMessage[counter] = MSG_DELIMITER; //cut message and stop sending + messageSent = true; + } + else if(_sendBufferMessage[counter] == MSG_DELIMITER){ + messageSent = true; + } + _port->write(_sendBufferMessage[counter]); counter++; - if(counter > MAX_LEN_TOPICS + MAX_LEN_DATA + 5){ - return false; - } } return true; } -int didacticPSNet::extractData(int startCounter, int maxLength, char* buffer, char limiter){ +int DidacticPSNet::extractData(int startCounter, int maxLength, char* buffer, char limiter){ int counter = startCounter; while(_readBufferMessage[counter]!= limiter){ buffer[counter-startCounter] = _readBufferMessage[counter]; counter++; if((counter-startCounter) > maxLength){ - return -1; + counter--; + break; //if > maxLenght -> leave while and return } } buffer[counter-startCounter] = '\0'; - return counter-startCounter; //length of Topic + return counter-startCounter; //length } -int didacticPSNet::checkData(){ +int DidacticPSNet::checkData(){ return (int)_port->available(); } -bool didacticPSNet::recieveData() { +bool DidacticPSNet::recieveData() { static int msgCounter = 0; static int topicCounter = 0; - static int dataCounter = 0; + static int payloadCounter = 0; //if(msgCounter == NULL){ msgCounter = 0; } //if(topicCounter == NULL){ topicCounter = 0; } //if(dataCounter == NULL){ dataCounter = 0; } @@ -111,7 +120,7 @@ bool didacticPSNet::recieveData() { if (localBuffer == MSG_PRELIMITER) { msgCounter = 0; topicCounter = 0; - dataCounter = 0; + payloadCounter = 0; _readBufferMessage[msgCounter] = localBuffer; } else if (localBuffer == MSG_DELIMITER && _readBufferMessage[0] == MSG_PRELIMITER) { @@ -134,112 +143,99 @@ bool didacticPSNet::recieveData() { return false; } + void DidacticPSNet::setInterval(long intervalTime){ + _intervalTime = intervalTime; + } + + //************************************************************************** // CLIENT //************************************************************************** -didacticPSNetClient::didacticPSNetClient(){} +DidacticPSNetClient::DidacticPSNetClient(){ + _intervalTime = INTERVAL_CLIENT; +} -didacticPSNetClient::~didacticPSNetClient(){} +DidacticPSNetClient::~DidacticPSNetClient(){} -bool didacticPSNetClient::publish(char* topic, char* data){ - int topicLength = strlen(topic); - int dataLength = strlen(data); - _sendBufferMessage[0] = MSG_PRELIMITER; +int DidacticPSNetClient::publish(char* topic, char* payload){ + return publish(topic, strlen(topic), payload, strlen(payload)); +} + +int DidacticPSNetClient::publish(char* topic, int topicLength, char* payload , int payloadLength){ + int error = DN_PUBLISH_SUCCESSULL; + + _sendBufferMessage[0] = MSG_PRELIMITER; _sendBufferMessage[1] = MSG_PUBLISH; _sendBufferMessage[2+topicLength] = MSG_SEPARATOR; - _sendBufferMessage[2+topicLength+1+dataLength] = MSG_DELIMITER; - _sendBufferMessage[2+topicLength+1+dataLength+1] = '\0'; + _sendBufferMessage[2+topicLength+1+payloadLength] = MSG_DELIMITER; + _sendBufferMessage[2+topicLength+1+payloadLength+1] = '\0'; - if(topicLength <= MAX_LEN_TOPICS){ - for(int i = 0; i < topicLength; i++){ + //TODO: check + if(topicLength > MAX_LEN_TOPICS){ + topicLength = MAX_LEN_TOPICS; + error += DN_ERROR_TOPIC_LEN; + } + if(payloadLength > MAX_LEN_PAYLOAD){ + payloadLength = MAX_LEN_PAYLOAD; + error += DN_ERROR_PAYLOAD_LEN; + } + + for(int i = 0; i < topicLength; i++){ _sendBufferMessage[2+i] = topic[i]; } - }else { - _dataToSend = false; - return false; - } - if(dataLength <= MAX_LEN_DATA){ - for(int i = 0; i < dataLength; i++){ - _sendBufferMessage[2+topicLength+1+i] = data[i]; + for(int i = 0; i < payloadLength; i++){ + _sendBufferMessage[2+topicLength+1+i] = payload[i]; } - }else { - _dataToSend = false; - return false; - } + _dataToSend = true; - return true; + return error; } -bool didacticPSNetClient::publish(char* topic, int topicLength, char* data , int dataLength){ - _sendBufferMessage[0] = MSG_PRELIMITER; - _sendBufferMessage[1] = MSG_PUBLISH; - _sendBufferMessage[2+topicLength] = MSG_SEPARATOR; - _sendBufferMessage[2+topicLength+1+dataLength] = MSG_DELIMITER; - _sendBufferMessage[2+topicLength+1+dataLength+1] = '\0'; +//TODO: TEST TEST TEST +int DidacticPSNetClient::publish(char* topic, int data){ + char sendPayload[MAX_LEN_PAYLOAD]; + itoa(data, sendPayload, 10); - if(topicLength <= MAX_LEN_TOPICS){ - for(int i = 0; i < topicLength; i++){ - _sendBufferMessage[2+i] = topic[i]; - } - }else { - _dataToSend = false; - return false; - } - if(dataLength <= MAX_LEN_DATA){ - for(int i = 0; i < dataLength; i++){ - _sendBufferMessage[2+topicLength+1+i] = data[i]; - } - }else { - _dataToSend = false; - return false; - } - _dataToSend = true; - return true; + return publish(topic, sendPayload); } -bool didacticPSNetClient::subscribe(char* topic){ - int topicLength = strlen(topic); - _sendBufferMessage[0] = MSG_PRELIMITER; - _sendBufferMessage[1] = MSG_SUBSCRIBE; - _sendBufferMessage[2+topicLength] = MSG_DELIMITER; +int DidacticPSNetClient::publish(char* topic, bool data){ + char sendPayload[2]; + itoa(data, sendPayload, 10); - int topicNumber = getTopicNr(topic); + return publish(topic, sendPayload); +} - if(topicNumber < 0){ - topicNumber = getFreeTopicNr(); - if(topicNumber < 0){ - topicNumber = 0; +int DidacticPSNetClient::publishOnChange(char* topic, bool input){ + if(!_dataToSend){ + if(eDetector.edgeDetected(input)){ + return publish(topic, input); } - if( topicLength <= MAX_LEN_TOPICS){ - for(int i = 0; i < topicLength; i++){ - _topic[topicNumber][i] = topic[i]; - _sendBufferMessage[2+i]= topic[i]; - } - _topic[topicNumber][topicLength] = '\0'; - _sendBufferMessage[2+topicLength+1]= '\0'; - _dataToSend = true; - } - else { - _dataToSend = false; - return false; - } - } - else{ - if( topicLength <= MAX_LEN_TOPICS){ - for(int i = 0; i < topicLength; i++){ - _sendBufferMessage[2+i]= topic[i]; - } - _sendBufferMessage[2+topicLength+1]= '\0'; - _dataToSend = true; - } } - while(_dataToSend){ - handleNetwork(); + return DN_ERROR_NO_ERROR; +} + +int DidacticPSNetClient::publishOnChange(char* topic, int input, int threshold){ + if(!_dataToSend){ + if(cDetector.valueChanged(input, threshold)){ + return publish(topic, input); + } } - return true; + return DN_ERROR_NO_ERROR; } -bool didacticPSNetClient::subscribe(char* topic, int topicLength){ +int DidacticPSNetClient::subscribe(char* topic){ + return subscribe(topic, strlen(topic)); +} + +int DidacticPSNetClient::subscribe(char* topic, int topicLength){ + int error = DN_ERROR_NO_ERROR; + + if( topicLength > MAX_LEN_TOPICS){ + topicLength = MAX_LEN_TOPICS; + error = DN_ERROR_TOPIC_LEN; + } + _sendBufferMessage[0] = MSG_PRELIMITER; _sendBufferMessage[1] = MSG_SUBSCRIBE; _sendBufferMessage[2+topicLength] = MSG_DELIMITER; @@ -251,7 +247,6 @@ bool didacticPSNetClient::subscribe(char* topic, int topicLength){ if(topicNumber < 0){ topicNumber = 0; } - if( topicLength <= MAX_LEN_TOPICS){ for(int i = 0; i < topicLength; i++){ _topic[topicNumber][i] = topic[i]; _sendBufferMessage[2+i]= topic[i]; @@ -259,39 +254,26 @@ bool didacticPSNetClient::subscribe(char* topic, int topicLength){ _topic[topicNumber][topicLength] = '\0'; _sendBufferMessage[2+topicLength+1]= '\0'; _dataToSend = true; - } - else { - _dataToSend = false; - return false; - } } else{ - if( topicLength <= MAX_LEN_TOPICS){ for(int i = 0; i < topicLength; i++){ _sendBufferMessage[2+i]= topic[i]; } _sendBufferMessage[2+topicLength+1]= '\0'; _dataToSend = true; - } } while(_dataToSend){ handleNetwork(); } - return true; + return error; } -bool didacticPSNetClient::unsubscribe(char* topic){ - int topicNumber = getTopicNr(topic); - int topicLength = strlen(topic); - if(topicNumber >= 0){ - _topic[topicNumber][0]='\0'; - return true; - } - return false; +bool DidacticPSNetClient::unsubscribe(char* topic){ + return unsubscribe(topic, strlen(topic)); } -bool didacticPSNetClient::unsubscribe(char* topic, int topicLength){ +bool DidacticPSNetClient::unsubscribe(char* topic, int topicLength){ int topicNumber = getTopicNr(topic); if(topicNumber >= 0){ _topic[topicNumber][0]='\0'; @@ -300,41 +282,42 @@ bool didacticPSNetClient::unsubscribe(char* topic, int topicLength){ return false; } -bool didacticPSNetClient::getMessageFilter(char messageType){ +bool DidacticPSNetClient::getMessageFilter(char messageType){ return messageType == MSG_UPDATE; } -bool didacticPSNetClient::saveData(char* buffer, int position){ - strcpy(_data[position], buffer); +bool DidacticPSNetClient::savePayload(char* buffer, int position){ + strcpy(_payload[position], buffer); return true; } -bool didacticPSNetClient::handleData(){ +bool DidacticPSNetClient::handleData(){ int currentTopicNr = 0; int topicLength = 0; - int dataLength = 0; + int payloadLength = 0; topicLength = extractData(2, MAX_LEN_TOPICS, _bufferTopic, MSG_SEPARATOR); if(topicLength > 0){ currentTopicNr = getTopicNr(_bufferTopic); - dataLength = extractData(topicLength+3, MAX_LEN_DATA, _bufferData, MSG_DELIMITER); + payloadLength = extractData(topicLength+3, MAX_LEN_PAYLOAD, _bufferPayload, MSG_DELIMITER); if( currentTopicNr >= 0){ - saveData( _bufferData, currentTopicNr); - callback(_topic[currentTopicNr], topicLength, _data[currentTopicNr], dataLength); + savePayload( _bufferPayload, currentTopicNr); + //callback(_topic[currentTopicNr], topicLength, _payload[currentTopicNr], payloadLength); + callback(_bufferTopic, topicLength, _bufferPayload, payloadLength); } } return true; } -int didacticPSNetClient::getTopicNr(char* topic){ +int DidacticPSNetClient::getTopicNr(char* topic){ for (int i = 0; i < MAX_NR_TOPICS_CLIENT; i++) { - if (strcmp(_topic[i], topic) == 0) { + if (strcmp(_topic[i], topic) == 0 || _topic[i][0] == MSG_TOPIC_MULTI) { //TODO: check ... or equal MSG_TOPIC_MULTI return i; } } return -1; } -int didacticPSNetClient::getFreeTopicNr() { +int DidacticPSNetClient::getFreeTopicNr() { for (int i = 0; i < MAX_NR_TOPICS_CLIENT; i++) { if (strcmp(_topic[i], "") == 0) { return i; @@ -347,28 +330,30 @@ int didacticPSNetClient::getFreeTopicNr() { //************************************************************************** //Broker //************************************************************************** -didacticPSNetBroker::didacticPSNetBroker(){} +DidacticPSNetBroker::DidacticPSNetBroker(){ + _intervalTime = INTERVAL_BROKER; +} -didacticPSNetBroker::~didacticPSNetBroker(){} +DidacticPSNetBroker::~DidacticPSNetBroker(){} -bool didacticPSNetBroker::getMessageFilter(char messageType){ +bool DidacticPSNetBroker::getMessageFilter(char messageType){ return (messageType == MSG_PUBLISH || messageType == MSG_SUBSCRIBE); } -bool didacticPSNetBroker::saveData(char* buffer, int position){ +bool DidacticPSNetBroker::savePayload(char* buffer, int position){ strcpy(_data[position], buffer); return true; } -void didacticPSNetBroker::writeDataToTopic(int topicNumber, char* usedTopic, char* newData) { +void DidacticPSNetBroker::writeDataToTopic(int topicNumber, char* usedTopic, char* newData) { if(strcmp(_topic[topicNumber], "") == 0){ strcpy(_topic[topicNumber], usedTopic); } strcpy(_data[topicNumber], newData); } -bool didacticPSNetBroker::handleData(){ +bool DidacticPSNetBroker::handleData(){ int currentTopicNr = 0; int topicLength = 0; int dataLength = 0; @@ -376,9 +361,9 @@ bool didacticPSNetBroker::handleData(){ topicLength = extractData(2, MAX_LEN_TOPICS, _bufferTopic, MSG_SEPARATOR); if(topicLength > 0){ currentTopicNr = getTopicNr(_bufferTopic); - dataLength = extractData(topicLength+3, MAX_LEN_DATA, _bufferData, MSG_DELIMITER); + dataLength = extractData(topicLength+3, MAX_LEN_PAYLOAD, _bufferPayload, MSG_DELIMITER); if( currentTopicNr >= 0){ - writeDataToTopic(currentTopicNr, _bufferTopic, _bufferData); + writeDataToTopic(currentTopicNr, _bufferTopic, _bufferPayload); update(_topic[currentTopicNr], topicLength, _data[currentTopicNr], dataLength); } } @@ -395,7 +380,7 @@ bool didacticPSNetBroker::handleData(){ return true; } -bool didacticPSNetBroker::update(char* topic, int topicLength, char* data , int dataLength){ +bool DidacticPSNetBroker::update(char* topic, int topicLength, char* data , int dataLength){ _sendBufferMessage[0] = MSG_PRELIMITER; _sendBufferMessage[1] = MSG_UPDATE; _sendBufferMessage[2+topicLength] = MSG_SEPARATOR; @@ -410,7 +395,7 @@ bool didacticPSNetBroker::update(char* topic, int topicLength, char* data , int _dataToSend = false; return false; } - if(dataLength <= MAX_LEN_DATA){ + if(dataLength <= MAX_LEN_PAYLOAD){ for(int i = 0; i < dataLength; i++){ _sendBufferMessage[2+topicLength+1+i] = data[i]; } @@ -422,7 +407,7 @@ bool didacticPSNetBroker::update(char* topic, int topicLength, char* data , int return true; } -int didacticPSNetBroker::getTopicNr(char* topic){ +int DidacticPSNetBroker::getTopicNr(char* topic){ for (int i = 0; i < MAX_NR_TOPICS_BROKER; i++) { if (strcmp(_topic[i], topic) == 0) { return i; @@ -431,7 +416,7 @@ int didacticPSNetBroker::getTopicNr(char* topic){ return getFreeTopicNr(); } -int didacticPSNetBroker::getFreeTopicNr() { +int DidacticPSNetBroker::getFreeTopicNr() { for (int i = 0; i < MAX_NR_TOPICS_BROKER; i++) { if (strcmp(_topic[i], "") == 0) { return i; @@ -439,3 +424,77 @@ int didacticPSNetBroker::getFreeTopicNr() { } return -1; } + + +//************************************************************************** +//LITTLE HELPERS FOR CLIENTS ;-) +//************************************************************************* + +EdgeDetector::EdgeDetector(){}; +EdgeDetector::~EdgeDetector(){}; + +int EdgeDetector::edgeDetected(bool currentState){ + static bool lastState = false; + int edEdge = 0; + + if(currentState && !lastState){ + edEdge = RISING; + } + else if(!currentState && lastState){ + edEdge = FALLING; + } + lastState = currentState; + return edEdge; +} + +ChangeDetector::ChangeDetector(){} +ChangeDetector::~ChangeDetector(){} + +bool ChangeDetector::valueChanged(int value, int threshold){ + static int lastValue = 0; + + if(abs(value-lastValue) > threshold){ + lastValue = value; + return true; + } + return false; +} + + +UnblockingTimer::UnblockingTimer(){} +UnblockingTimer::~UnblockingTimer(){} + +bool UnblockingTimer::timeElapsed(long delayTime){ + long currentTime = millis(); + + if(lastTime + (delayTime-1) < currentTime){ + lastTime = currentTime; + return true; + } + return false; +} + +SerialReader::SerialReader(){} +SerialReader::~SerialReader(){} + +void SerialReader::begin(Stream& rsStream){ + _port = &rsStream; +} + +int SerialReader::readSerialData(char* rsDataArray, char rsEndSign) { + + if (_port->available()) { + char charBuffer = _port->read(); + rsDataArray[charCounter] = charBuffer; + + if (charBuffer == rsEndSign) { + rsDataArray[charCounter] = '\0'; + int nrOfChars = charCounter; + charCounter = 0; + return nrOfChars; + } else { + charCounter++; + } + } + return 0; +} \ No newline at end of file diff --git a/src/didacticNet.h b/src/didacticNet.h index d7057777660f18aaad894d4e512a8d9044bc88aa..41aac4c9419efd6e05adb4e4020877690ff3e36c 100644 --- a/src/didacticNet.h +++ b/src/didacticNet.h @@ -1,9 +1,7 @@ -/**************************************************************************/ -/*! +/************************************************************************** @file didacticNet.h @author anian buehler @ letsgoING -*/ -/**************************************************************************/ +**************************************************************************/ #ifndef _DIDACTICNET_ @@ -20,40 +18,105 @@ //@ publish → on publish check topic, then send topic-update //? subscribe → subscribe starts update, topic filter @client //# update → update to specific topic Broker to client -#define MSG_PUBLISH '@' -#define MSG_SUBSCRIBE '?' -#define MSG_UPDATE '#' +#define MSG_PUBLISH '@' +#define MSG_SUBSCRIBE '?' +#define MSG_UPDATE '#' +#define MSG_TOPIC_MULTI '*' -#define MSG_TOPIC_MULTI '*' +//<@topic|payload> +#define LEN_OVERHEAD 4 #define CSMA_CHECK_DELAY_US 400 -#define CSMA_MIN_DELAY_MS 10 -#define CSMA_MID_DELAY_MS 20 -#define CSMA_MAX_DELAY_MS 30 +#define CSMA_MIN_DELAY_MS 10 +#define CSMA_MID_DELAY_MS 20 +#define CSMA_MAX_DELAY_MS 30 + +#define INTERVAL_CLIENT 500L +#define INTERVAL_BROKER 0L #define MAX_NR_TOPICS_CLIENT 10 #define MAX_NR_TOPICS_BROKER 20 -#define MAX_LEN_TOPICS 10 -#define MAX_LEN_DATA 20 +#define MAX_LEN_TOPICS 10 +#define MAX_LEN_PAYLOAD 20 + +#define DN_PUBLISH_SUCCESSULL 1 +#define DN_ERROR_NO_ERROR 0 +#define DN_ERROR_TOPIC_LEN -1 +#define DN_ERROR_PAYLOAD_LEN -2 + + +//little helpers +#define DN_ASCII_CR 13 +#define DN_ASCII_NL 10 + + +class EdgeDetector +{ + private: + + public: + EdgeDetector(); + ~EdgeDetector(); + int edgeDetected(bool); + +}; + +class ChangeDetector +{ + private: + + public: + ChangeDetector(); + ~ChangeDetector(); + bool valueChanged(int, int); + +}; + +class UnblockingTimer +{ + private: + long lastTime = 0L; + + public: + UnblockingTimer(); + ~UnblockingTimer(); + bool timeElapsed(long); + +}; +class SerialReader +{ + private: + Stream* _port; + int charCounter = 0; + + public: + SerialReader(); + ~SerialReader(); + void begin(Stream&); + int readSerialData(char*, char); + +}; -class didacticPSNet +class DidacticPSNet { protected: Stream* _port; PSNET_CALLBACK_SIGNATURE; - char _bufferTopic[MAX_LEN_TOPICS+1] = {0}; - char _bufferData[MAX_LEN_DATA+1] = {0}; - char _readBufferMessage[MAX_LEN_TOPICS + MAX_LEN_DATA +4+1]; - char _sendBufferMessage[MAX_LEN_TOPICS + MAX_LEN_DATA +4+1]; + char _bufferTopic[MAX_LEN_TOPICS +1] = {0}; + char _bufferPayload[MAX_LEN_PAYLOAD +1] = {0}; + char _readBufferMessage[MAX_LEN_TOPICS + MAX_LEN_PAYLOAD + LEN_OVERHEAD +1]; + char _sendBufferMessage[MAX_LEN_TOPICS + MAX_LEN_PAYLOAD + LEN_OVERHEAD +1]; bool _dataToSend = false; // int Data to send for queue? - unsigned long _waitingTime = 0L; + unsigned long _waitingTimeSend = 0L; + unsigned long _waitingTimeCSMA = 0L; + unsigned long _intervalTime = 0L; int _currentTopicLength = 0; - int _currentDataLength = 0; + int _currentPayloadLength = 0; - didacticPSNet& setCallback(PSNET_CALLBACK_SIGNATURE); + DidacticPSNet& setCallback(PSNET_CALLBACK_SIGNATURE); void setStream(Stream& _port); int checkData(); @@ -64,55 +127,64 @@ class didacticPSNet virtual int getTopicNr(char*)=0; virtual int getFreeTopicNr()=0; virtual bool getMessageFilter(char)=0; - virtual bool saveData(char*, int)=0; + virtual bool savePayload(char*, int)=0; virtual bool handleData()=0; public: - didacticPSNet(); - ~didacticPSNet(); + DidacticPSNet(); + ~DidacticPSNet(); void begin(Stream& _port); void begin(Stream& _port, PSNET_CALLBACK_SIGNATURE); bool handleNetwork(); bool isDataToSend(); + void setInterval(long); + }; -class didacticPSNetClient : public didacticPSNet +class DidacticPSNetClient : public DidacticPSNet { private: + EdgeDetector eDetector; + ChangeDetector cDetector; char _topic[MAX_NR_TOPICS_CLIENT][MAX_LEN_TOPICS+1] = { { 0 } }; - char _data[MAX_NR_TOPICS_CLIENT][MAX_LEN_DATA+1] = { { 0 } }; + char _payload[MAX_NR_TOPICS_CLIENT][MAX_LEN_PAYLOAD+1] = { { 0 } }; - bool saveData(char*, int); + bool savePayload(char*, int); bool getMessageFilter(char); bool handleData(); int getTopicNr(char*); int getFreeTopicNr(); - public: - didacticPSNetClient(); - ~didacticPSNetClient(); + int edgeDetected(bool); + bool valueChanged(int, int); - bool publish(char*, char*); - bool publish(char*, int, char*, int); - bool subscribe(char*); - bool subscribe(char*, int); + public: + DidacticPSNetClient(); + ~DidacticPSNetClient(); + + int publish(char*, char*); + int publish(char*, int, char*, int); + int publish(char*, int); + int publish(char*, bool); + int publishOnChange(char*, bool); + int publishOnChange(char*, int, int); + int subscribe(char*); + int subscribe(char*, int); bool unsubscribe(char*); bool unsubscribe(char*, int); - }; -class didacticPSNetBroker: public didacticPSNet +class DidacticPSNetBroker: public DidacticPSNet { private: - char _topic[MAX_NR_TOPICS_BROKER][MAX_LEN_TOPICS+1] = { { 0 } }; - char _data[MAX_NR_TOPICS_BROKER][MAX_LEN_DATA+1] = { { 0 } }; + char _data[MAX_NR_TOPICS_BROKER][MAX_LEN_PAYLOAD+1] = { { 0 } }; - bool saveData(char*, int); + bool savePayload(char*, int); bool getMessageFilter(char); void writeDataToTopic(int, char*, char*); bool handleData(); @@ -120,11 +192,13 @@ class didacticPSNetBroker: public didacticPSNet int getFreeTopicNr(); public: - didacticPSNetBroker(); - ~didacticPSNetBroker(); + DidacticPSNetBroker(); + ~DidacticPSNetBroker(); bool update(char*, int, char*, int); }; + + #endif