diff --git a/examples/sPSN_Client1/sPSN_Client1.ino b/examples/sPSN_Client1/sPSN_Client1.ino index e6d27ad5fb64d54c1e9aaa1c348a234b33830d4d..1ba99c0352c90ad45282cc5f3dbcb10f47b0f2a3 100644 --- a/examples/sPSN_Client1/sPSN_Client1.ino +++ b/examples/sPSN_Client1/sPSN_Client1.ino @@ -7,115 +7,80 @@ * 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 + *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 +#define THRESHOLD 10 //Schwellwert für min. Wertänderung +#define SEND_DELAY 500 //Mindestwarezeit zwischen zwei Sendevorgängen -byte ledPin = 9; -byte potiPin = A0; +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) -int lastValue = 0; +char payload[MAX_LEN_PAYLOAD] = {0}; +boolean newData = false; +SoftwareSerial sSerial(10, 11); //Erzeuge SoftwareSerial-Instanz mit Rx = Pin10 -> Empfänger | Tx = Pin11 -> Sender -//Maximale Nachrichten- und Topic-Länge: -//MAX_LEN_PAYLOAD = 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 +didacticPSNetClient psnClient; //Erzeuge PubSub-Client-Instanz -//Arrays für Empfangs- und Sende Topic -char topicSub[MAX_LEN_TOPICS] = "buttonVal"; -char topicPub[MAX_LEN_TOPICS] = "potiVal"; -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_PAYLOAD] = {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(valueChanged(currentValue, THRESHOLD)){ + itoa(currentValue, payload, 10); //wandle Wert in ASCII-Zeichen + //Alternativ: sprintf(payload, "%d", currentValue); //wandle Wert in ASCII-Zeichen + newData = true; // neue Daten zum Senden + } + if(timeElapsed(SEND_DELAY) && newData){ + psnClient.publish(topicPublish, payload); //bereite Topic und Nutzdaten zum senden vor + newData = false; //Daten wurden gesendet } } + diff --git a/examples/sPSN_Client2/sPSN_Client2.ino b/examples/sPSN_Client2/sPSN_Client2.ino index 9e8fedde38d183aa6c1629ff7fdea725b34d2488..a978799a0799151c1e712fce8c26761f9bb9798b 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_PAYLOAD = 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_PAYLOAD] = {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(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; // neue Daten zum Senden + } + if(timeElapsed(SEND_DELAY) && newData){ + psnClient.publish(topicPublish, payload); //bereite Topic und Nutzdaten zum senden vor + newData = false; //Daten wurden gesendet } - - //speichere aktuellen Wert, um zu erkennen, ob er sich ändert - lastState = currentState; } + diff --git a/keywords.txt b/keywords.txt index 7dd0b528cb972466c577299ff1dfc00552720d87..9334b7be09db19a4ae2d1fa1eaa6257eb76b7181 100644 --- a/keywords.txt +++ b/keywords.txt @@ -13,12 +13,16 @@ didacticPSNetClient KEYWORD1 ####################################### # Methods and Functions (KEYWORD2) ####################################### -setCallback KEYWORD2 -setStream KEYWORD2 +edgeDetected KEYWORD2 +valueChanged KEYWORD2 +timeElapsed KEYWORD2 + +setCallback KEYWORD2 +setStream KEYWORD2 handleNetwork KEYWORD2 isDataToSend KEYWORD2 -publish KEYWORD2 +publish KEYWORD2 subscribe KEYWORD2 unsubscribe KEYWORD2 @@ -30,11 +34,11 @@ MSG_DELIMITER LITERAL1 MSG_SEPARATOR LITERAL1 MSG_PUBLISH LITERAL1 -MSG_SUBSCRIBE LITERAL1 +MSG_SUBSCRIBE LITERAL1 MSG_UPDATE LITERAL1 MSG_TOPIC_MULTI LITERAL1 MAX_NR_TOPICS_CLIENT LITERAL1 MAX_NR_TOPICS_BROKER LITERAL1 -MAX_LEN_TOPICS LITERAL1 -MAX_LEN_PAYLOAD LITERAL1 +MAX_LEN_TOPICS LITERAL1 +MAX_LEN_PAYLOAD LITERAL1 diff --git a/src/didacticNet.cpp b/src/didacticNet.cpp index 50c78b4a6769d5dee56d631a79f36e3786e2c9a8..0052e7279e47fef1fc95d7c41f044db4b5c864ce 100644 --- a/src/didacticNet.cpp +++ b/src/didacticNet.cpp @@ -8,9 +8,47 @@ #include "Arduino.h" #include "didacticNet.h" +//************************************************************************** +//LITTLE HELPERS ;-) +//************************************************************************** +int edgeDetected(bool currentState){ + static bool lastState = false; + int edge = 0; + + if(currentState && !lastState){ + edge = RISING; + } + if(!currentState && lastState){ + edge = FALLING; + } + lastState = currentState; + return edge; +} + +bool valueChanged(int value, int threshold){ + static int lastValue = 0; + + if(abs(value-lastValue) > threshold){ + lastValue = value; + return true; + } + return false; +} + +bool timeElapsed(long delayTime){ + static long lastTime = 0L; + long currentTime = millis(); + + if(lastTime + (delayTime-1) < currentTime){ + lastTime = currentTime; + return true; + } + return false; +} + //************************************************************************** -//BASIC +//ROOT //************************************************************************** didacticPSNet::didacticPSNet(){} @@ -70,12 +108,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_PAYLOAD + 5){ - return false; - } } return true; } diff --git a/src/didacticNet.h b/src/didacticNet.h index 514339679c82fee961e6774feabede37f053b99f..05b0990df369ebe52dd7c91c65f3f44215ae8a5f 100644 --- a/src/didacticNet.h +++ b/src/didacticNet.h @@ -15,6 +15,7 @@ #define MSG_DELIMITER '>' #define MSG_SEPARATOR '|' + //@ publish → on publish check topic, then send topic-update //? subscribe → subscribe starts update, topic filter @client //# update → update to specific topic Broker to client @@ -22,8 +23,12 @@ #define MSG_SUBSCRIBE '?' #define MSG_UPDATE '#' +//<@topic|payload> +#define LEN_OVERHEAD 4 + #define MSG_TOPIC_MULTI '*' + #define CSMA_CHECK_DELAY_US 400 #define CSMA_MIN_DELAY_MS 10 #define CSMA_MID_DELAY_MS 20 @@ -34,6 +39,11 @@ #define MAX_LEN_TOPICS 10 #define MAX_LEN_PAYLOAD 20 +//little helpers +int edgeDetected(bool); +bool valueChanged(int, int); +bool timeElapsed(long); + class didacticPSNet { protected: