From b5fc93dbd057d84b5cc2653d28de2b13c2c8e9c4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Anian=20B=C3=BChler?=
 <anian.buehler@reutlingen-university.de>
Date: Fri, 9 Jul 2021 14:59:15 +0200
Subject: [PATCH] added "little helpers"

---
 examples/sPSN_Client1/sPSN_Client1.ino | 131 +++++++++--------------
 examples/sPSN_Client2/sPSN_Client2.ino | 139 ++++++++++---------------
 keywords.txt                           |  16 +--
 src/didacticNet.cpp                    |  55 +++++++++-
 src/didacticNet.h                      |  10 ++
 5 files changed, 171 insertions(+), 180 deletions(-)

diff --git a/examples/sPSN_Client1/sPSN_Client1.ino b/examples/sPSN_Client1/sPSN_Client1.ino
index e6d27ad..1ba99c0 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 9e8fedd..a978799 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 7dd0b52..9334b7b 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 50c78b4..0052e72 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 5143396..05b0990 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:
-- 
GitLab