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/README.md b/README.md index fc8eff8e36c5fc233b72124da973c40ded9e050b..86d7ceedad082ebd6cc0c7590d3398055939fe51 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,12 @@ # 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 benötigt, wleche sich an der Seriellen Schnittstelle betreiben lässt. +Wir empfehlen unsere IR-Link-Module, die Sie selbst herstellen können (Schaltplan und Layout bei uns anfragen - Kontakt über [letsgoING.org](httsp://letsgoING.org) +oder z. B. hier erwerben können: [Hinweise zur Bestellung bei Aisler](#bestellung-bei-aisler). -### usage -see example-files ## Download Start download with the button on the right side. Choose the right format and start the download. @@ -16,3 +15,160 @@ Start download with the button on the right side. Choose the right format and st 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 +didacticPSNetClient psnClient; + +#Starten der Client-Instanz +void psnClient.begin(Stream& sSerial, fcn callback); +// param1: Schnittstelle (Serial | SoftSerial) +// param2: Callback-Funktion + +#Netzwerkverwaltung Client (Daten senden und Empfangen, Zugriffsregelung) +bool psnClient.handleNetwork(); +// return: true wenn Daten versendet / false wenn nicht + +#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 +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_ERROR_NO_ERROR, +// 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) +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 +``` + +### Broker + +```cpp +#Anlegen der Broker-Instanz +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(); +``` + +### Hilfreiche Funktionen + +Wichtiger Hinweis: +Diese Funktionen koennen derzeit nur einmal pro Programm/Client-Instanz eingesetzt werden. +Werden diese an verschiedenen Stellen mit verschiedenen Werten verwendet, werden Werte u. U. ueberschrieben. + +```cpp +#Flankenerkennung z.B. fuer Taster +int psnClient.edgeDetected(bool currentState); +// param: aktueller binärer Zustand +// return: 0 -> keine Flanke, RISING, FALLING + +#Auf Wertaenderung groeßer Schwellwert pruefen +bool psnClient.valueChanged(int currentvalue, int threshold); +// param1: aktueller Wert +// param2: Schwellwert +// return: true -> Aenderung groeßer als Schwellwert; sonst false + +#Nicht blockierendes Warten +bool psnClient.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 +int psnClient.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 teilweise 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 + +#ERRORs +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 öffnen: [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**. diff --git a/examples/brokerClient/brokerClient.ino b/examples/brokerClient/brokerClient.ino index ccabaa076185898ac5dedfff7502f3910b830e80..8f8a63f1a3c04cb2eef5db064d17e6ff48b5e1e2 100644 --- a/examples/brokerClient/brokerClient.ino +++ b/examples/brokerClient/brokerClient.ino @@ -1,4 +1,4 @@ -#include "Arduino.h" + #include "Arduino.h" #include "SoftwareSerial.h" #include "didacticNet.h" diff --git a/examples/sPSN_Broker/sPSN_Broker.ino b/examples/sPSN_Broker/sPSN_Broker.ino index 662ecf03641f9d69a3289d9f9be861024f9514bd..1aec4de47f1a6f83355889f44f998605e7bcd017 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" @@ -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..d480f6a8c5aa0e33a3b6a876ef339ee88449363c 100644 --- a/examples/sPSN_Chat/sPSN_Chat.ino +++ b/examples/sPSN_Chat/sPSN_Chat.ino @@ -1,84 +1,67 @@ /** - *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 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" + //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 +69,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 = psnClient.readSerialData(Serial, 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..249b4c3ce1446f68cc6187196ff50aa3df39c6df 100644 --- a/examples/sPSN_Client1/sPSN_Client1.ino +++ b/examples/sPSN_Client1/sPSN_Client1.ino @@ -7,115 +7,83 @@ * 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 +#define SEND_DELAY 500 //Mindestwarezeit zwischen zwei Sendevorgängen -int lastValue = 0; +char topicPublish[MAX_LEN_TOPICS] = "potiVal"; //Topic für zu sendende (eigene) Daten +char topicSubscribe[MAX_LEN_TOPICS] = "buttonVal"; //Topic für zu empfangende Daten (von anderem TN) +char payload[MAX_LEN_PAYLOAD] = {0}; +boolean newData = false; -//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 stateLED = bool(atoi(mData)); //wandle ASCII-Zeichen in Wert + //Alternativ: sscanf(mData, "%d", &stateLED); //wandle ASCII-Zeichen in Wert + Serial.print(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.begin(SERIAL_BAUD); //Starte Serielle Schnittstelle (zum PC) + sSerial.begin(SERIAL_BAUD); //Starte SoftwareSerielle Schnittstelle (zu IR-Link-Modulen) - Serial.print("\nStarting sPSNet: \nlistening to:\t"); - Serial.println(topicSub); - Serial.print("sending to:\t"); - Serial.println(topicPub); + pinMode(LED_PIN,OUTPUT); - //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); - - //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 -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; + if(psnClient.valueChanged(currentValue, THRESHOLD)){ + itoa(currentValue, payload, 10); //wandle Wert in ASCII-Zeichen + //Alternativ: sprintf(payload, "%d", currentValue); //wandle Wert in ASCII-Zeichen + newData = true; //Flag: neue Daten zum Senden + } + if(psnClient.timeElapsed(SEND_DELAY) && newData){ //Sende neue Daten wenn Mindestwaretzeit abgelaufen + psnClient.publish(topicPublish, payload); //bereite Topic und Nutzdaten zum senden vor + newData = false; //Flag: keine neuen Daten vorhanden } } + diff --git a/examples/sPSN_Client2/sPSN_Client2.ino b/examples/sPSN_Client2/sPSN_Client2.ino index dc3be3bf1df9dec594464c653d6dfe7714bc963f..ad31487990138e20535fad7c15f1ac5950b210e7 100644 --- a/examples/sPSN_Client2/sPSN_Client2.ino +++ b/examples/sPSN_Client2/sPSN_Client2.ino @@ -1,119 +1,86 @@ /** - *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 BUTTON_PIN 2 -bool lastState = 0; +#define SEND_DELAY 500 //Mindestwarezeit zwischen zwei Sendevorgängen -//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 topicPublish[MAX_LEN_TOPICS] = "buttonVal"; //Topic für zu sendende (eigene) Daten +char topicSubscribe[MAX_LEN_TOPICS] = "potiVal"; //Topic für zu empfangende Daten (von anderem TN) -//Arrays für Empfangs- und Sende Topic -char topicSub[MAX_LEN_TOPICS] = {"potiVal"}; -char topicPub[MAX_LEN_TOPICS] = {"buttonVal"}; - -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); +char payload[MAX_LEN_PAYLOAD] = {0}; +boolean newData = false; +boolean ledState = false; - //Setze Ausgang entsprechend dem gesendeten Wert - analogWrite(ledPin, map(atoi(mData), 0, 1023, 0, 255)); -} +SoftwareSerial sSerial(10, 11); //Erzeuge SoftwareSerial-Instanz mit Rx = Pin10 -> Empfänger | Tx = Pin11 -> Sender +didacticPSNetClient psnClient; //Erzeuge PubSub-Client-Instanz -//lege Pins für SoftwareSerielle Schnittstelle fest -// Rx = 10 -> Empfänger | Tx = 11 -> Sender -SoftwareSerial sSerial(10, 11); -//Erzeuge Client-Instanz -didacticPSNetClient psnClient; +//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); + int valueLED = atoi(mData); //wandle ASCII-Zeichen in Wert + valueLED = constrain(map(valueLED, 0, 1023, 0, 255), 0, 255); //passe analogRead-Wert für analogWrite an + //Alternativ: sscanf(mData, "%d", &stateLED); //wandle ASCII-Zeichen in Wert -void setup() { - //Starte Serielle Schnittstelle (zum PC) - Serial.begin(SERIAL_BAUD); + analogWrite(LED_PIN, valueLED); //Setze Ausgang entsprechend dem empfangenen Wert +} - Serial.print("\nStarting sPSNet: \nlistening to:\t"); - Serial.println(topicSub); - Serial.print("sending to:\t"); - Serial.println(topicPub); - //Starte SoftwareSerielle Schnittstelle (zu IR-Link-Modulen) - sSerial.begin(SERIAL_BAUD); +void setup() { + + Serial.begin(SERIAL_BAUD); //Starte Serielle Schnittstelle (zum PC) + sSerial.begin(SERIAL_BAUD); //Starte SoftwareSerielle Schnittstelle (zu IR-Link-Modulen) - pinMode(ledPin, OUTPUT); - pinMode(buttonPin, INPUT); + 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); -} + boolean buttonState = digitalRead(BUTTON_PIN); //lese Poti ein und speichere Wert -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); + if(psnClient.edgeDetected(buttonState) == RISING){ + ledState = !ledState; //Invertiere zu sendenden Wert "LED-Zustand" + itoa(ledState, payload, 10); //wandle Wert in ASCII-Zeichen + //Alternativ: sprintf(payload, "%d", currentValue); //wandle Wert in ASCII-Zeichen + newData = true; //Flag: neue Daten zum Senden + } + if(psnClient.timeElapsed(SEND_DELAY) && newData){ //Sende neue Daten wenn Mindestwaretzeit abgelaufen + psnClient.publish(topicPublish, payload); //bereite Topic und Nutzdaten zum senden vor + newData = false; //Flag: keine neuen Daten vorhanden } - - //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..0e58d6bb209d2a2497290a1ece8f8f0464317507 --- /dev/null +++ b/examples/sPSN_ClientMinimal/sPSN_ClientMinimal.ino @@ -0,0 +1,65 @@ +/** + *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 + + +//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 + + char payload[5]; + itoa(currentValue, payload, 10); + + if(psnClient.timeElapsed(1000)){ //Sende neue Daten wenn Mindestwaretzeit 1 Sekunde abgelaufen + //Hier SENDE-TOPIC ANPASSEN -> default "client1" + psnClient.publish("client1", payload); //bereite Topic und Nutzdaten zum senden vor + } +} + 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..d70dbd25191371f0109951bed444664952040e3d 100644 --- a/keywords.txt +++ b/keywords.txt @@ -5,7 +5,6 @@ ####################################### # Datatypes (KEYWORD1) ####################################### - didacticPSNet KEYWORD1 didacticPSNetBroker KEYWORD1 didacticPSNetClient KEYWORD1 @@ -22,12 +21,17 @@ publish KEYWORD2 subscribe KEYWORD2 unsubscribe KEYWORD2 +edgeDetected KEYWORD2 +valueChanged 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 +41,11 @@ 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_ERROR_NO_ERROR LITERAL1 +DN_ERROR_TOPIC_LEN LITERAL1 +DN_ERROR_PAYLOAD_LEN LITERAL1 \ No newline at end of file diff --git a/src/didacticNet.cpp b/src/didacticNet.cpp index 54dacbcf39745d212d3a932c5b49f58184fa28e7..6529e29f65f275139ccda0158965048c4ec2ed56 100644 --- a/src/didacticNet.cpp +++ b/src/didacticNet.cpp @@ -8,9 +8,8 @@ #include "Arduino.h" #include "didacticNet.h" - //************************************************************************** -//BASIC +//ROOT //************************************************************************** didacticPSNet::didacticPSNet(){} @@ -70,12 +69,19 @@ bool didacticPSNet::isDataToSend(){ 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; } @@ -86,11 +92,12 @@ int didacticPSNet::extractData(int startCounter, int maxLength, char* buffer, ch 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(){ @@ -100,7 +107,7 @@ int didacticPSNet::checkData(){ 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; } @@ -109,7 +116,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) { @@ -139,64 +146,78 @@ didacticPSNetClient::didacticPSNetClient(){} didacticPSNetClient::~didacticPSNetClient(){} -bool didacticPSNetClient::publish(char* topic, char* data){ +int didacticPSNetClient::publish(char* topic, char* payload){ + int error = DN_ERROR_NO_ERROR; + int topicLength = strlen(topic); - int dataLength = strlen(data); + int payloadLength = strlen(payload); _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; +int didacticPSNetClient::publish(char* topic, int topicLength, char* payload , int payloadLength){ + int error = DN_ERROR_NO_ERROR; + + _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::subscribe(char* topic){ +int didacticPSNetClient::subscribe(char* topic){ + int error = DN_ERROR_NO_ERROR; + int topicLength = strlen(topic); + + 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; @@ -208,7 +229,6 @@ bool didacticPSNetClient::subscribe(char* topic){ 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]; @@ -216,28 +236,28 @@ bool didacticPSNetClient::subscribe(char* topic){ _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::subscribe(char* topic, int topicLength){ +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; @@ -249,7 +269,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]; @@ -257,25 +276,18 @@ 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; } @@ -302,22 +314,22 @@ 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(){ 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); } } return true; @@ -325,7 +337,7 @@ bool didacticPSNetClient::handleData(){ 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; } } @@ -341,6 +353,63 @@ int didacticPSNetClient::getFreeTopicNr() { return -1; } +//************************************************************************** +//LITTLE HELPERS FOR CLIENTS ;-) +//************************************************************************** +int didacticPSNetClient::edgeDetected(bool edCurrentState){ + static bool edLastState = false; + int edEdge = 0; + + if(edCurrentState && !edLastState){ + edEdge = RISING; + } + else if(!edCurrentState && edLastState){ + edEdge = FALLING; + } + edLastState = edCurrentState; + return edEdge; +} + +bool didacticPSNetClient::valueChanged(int vcValue, int vcThreshold){ + static int vcLastValue = 0; + + if(abs(vcValue-vcLastValue) > vcThreshold){ + vcLastValue = vcValue; + return true; + } + return false; +} + +bool didacticPSNetClient::timeElapsed(long teDelayTime){ + static long teLastTime = 0L; + long currentTime = millis(); + + if(teLastTime + (teDelayTime-1) < currentTime){ + teLastTime = currentTime; + return true; + } + return false; +} + +int didacticPSNetClient::readSerialData(Stream& rsStream, char* rsDataArray, char rsEndSign) { + static int rsCounter = 0; + + if (rsStream.available()) { + char charBuffer = rsStream.read(); + rsDataArray[rsCounter] = charBuffer; + + if (charBuffer == rsEndSign) { + rsDataArray[rsCounter] = '\0'; + int nrOfChars = rsCounter; + rsCounter = 0; + return nrOfChars; + } else { + rsCounter++; + } + } + return 0; +} + //************************************************************************** //Broker @@ -354,7 +423,7 @@ 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; } @@ -374,9 +443,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); } } @@ -408,7 +477,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]; } diff --git a/src/didacticNet.h b/src/didacticNet.h index 6d4642ea8e3ee1769dbd47da74a560bf04bb0492..fda76125d25b7ba99a791feecf4b73beec7f4979 100644 --- a/src/didacticNet.h +++ b/src/didacticNet.h @@ -1,9 +1,7 @@ -/**************************************************************************/ -/*! +/************************************************************************** @file didacticNet.h @author anian buehler @ letsgoING -*/ -/**************************************************************************/ +**************************************************************************/ #ifndef _DIDACTICNET_ @@ -23,18 +21,28 @@ #define MSG_PUBLISH '@' #define MSG_SUBSCRIBE '?' #define MSG_UPDATE '#' - #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 MAX_NR_TOPICS_CLIENT 5 +#define MAX_NR_TOPICS_CLIENT 5 #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_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 didacticPSNet { @@ -43,15 +51,15 @@ class didacticPSNet 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; int _currentTopicLength = 0; - int _currentDataLength = 0; + int _currentPayloadLength = 0; didacticPSNet& setCallback(PSNET_CALLBACK_SIGNATURE); void setStream(Stream& _port); @@ -64,7 +72,7 @@ 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: @@ -81,11 +89,10 @@ class didacticPSNet class didacticPSNetClient : public didacticPSNet { private: - 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*); @@ -95,24 +102,28 @@ class didacticPSNetClient : public didacticPSNet didacticPSNetClient(); ~didacticPSNetClient(); - bool publish(char*, char*); - bool publish(char*, int, char*, int); - bool subscribe(char*); - bool subscribe(char*, int); + int publish(char*, char*); + int publish(char*, int, char*, int); + int subscribe(char*); + int subscribe(char*, int); bool unsubscribe(char*); bool unsubscribe(char*, int); + //little helpers + int edgeDetected(bool); + bool valueChanged(int, int); + bool timeElapsed(long); + int readSerialData(Stream&, char*, char); }; 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();