Wireless Client Counter

Aus HRW FabLab MediaWiki
Wechseln zu: Navigation, Suche
Wireless Client Counter

WCC Logo.png

Entwickler

Gruppe 12
Münire Erdogan, Yusuf Kara, Jörn Mark Möllenkamp, Mike Schmitt

Projektzeit

November 2020 - April 2021

Verwendete Programmiersprache

C++, php, JavaScript, SQL

Eingesetzte Software

Arduino IDE, FileZilla, Gimp, MQTT Explorer, PhotoShop, TinkerCad, Visual Studio, Visual Studio Code

Eingesetzte Hardware

TTGO ESP 32 OLED, Ultraschallsensor, Servomotor, Taster

Externe Anwendungen

Datenbank, Webseite

Lehrende

Tandem-Lehrteam: Prof. Dr. Michael Schäfer, Florian Paproth
Tutur: Leon Heindl
externe wissenschaftliche Begleitung: Bonny Brandenburger (Uni Potsdam)


Wireless Client Counter, auf Deutsch Kabelloser Kundenzähler, informiert Kunden in der Pandemiezeit über die aktuelle Besucheranzahl in einem Laden. Kunden wissen mithilfe des Wireless Client Counter, wann sie in den Laden betreten dürfen und wann nicht.


Inhaltsverzeichnis

Projektidee[Bearbeiten]

Plakat Wireless Client Counter

Seitdem Ausbruch des Covid-19 gibt es für die Menschen Einschränkungen im Alltag. Dazu gehört auch das Einkaufen. Coronaschutzverordnung hat für die Pandemiezeit Regeln zur Begrenzung von Besucheranzahl der Einkaufsläden aufgestellt. Abhängig von der Fläche des Ladens sowie den aktuellen Inzidenzwerten werden bestimmte Anzahl von Kunden in den Laden eingelassen. Beispielsweise können sich in einem Laden 90 Kunden aufhalten. Der nächste Kunde bekommt die Gelegenheit zum Eintritt in den Laden sobald ein anderer Kunde den Laden verlassen hat. Nur wenige Einkaufsläden haben an ihrem Eingang ihre Mitarbeiter zum kontrollierten Einlass der Kunden aufgestellt. Andere Läden haben dazu nicht mal Mitarbeiter beauftragt oder versuchen diese Zählung mithilfe der Verringerung der Einkaufswägen und eine Pflicht des mitführen des Einkaufswagen zu bewältigen. Allerdings nutzen nicht alle Kunden einen Einkaufswagen, wodurch oftmals die maximale Kundenanzahl deutlich überschritten wird. Aus diesem Grund wissen Kunden oft nicht, ob sie in den Laden zugelassen sind oder nicht. Wireless Client Counter, der von Studierenden im Modul Eingebettete Systeme, im Wintersemester 2020/2021 entwickelt wurde, kommt an dieser Stelle zum Einsatz. Er zeigt an, wie viele Besucher max. den Laden betreten dürfen, wie viele sich aktuell in dem Laden befinden und wie viele noch in den Laden hineindürfen. Dieses System erleichtert somit den Alltag der Kunden und erspart gleichzeitig auch Kosten für die Unternehmer der Einkaufsläden.

Funktion des Wireless Client Counter[Bearbeiten]

Für die Kundenzählung gibt es zwei Wireless Client Counter. Der Eine befindet sich am Eingang des Ladens und der Zweite am Ausgang. Der Wireless Client Counter am Eingang zählt die Besucherzahl beim Vorbeigehen des Kunden hoch. Diese Zahl wird wieder runtergezählt, wenn ein anderer Kunde den Ausgang des Ladens durchpassiert.

Hardware[Bearbeiten]

In dieser Tabelle sind Komponenten zu finden, die für die Entwicklung von Wireless Client Counter verwendet worden sind. Kabeln werden hier nicht mitberücksichtigt. Die Preise der einzelnen Komponenten können im Markt unterschiedlich sein. Hier sind die ungefähren Preise aufgelistet, für die man die Produkte im Internet bekommen kann.

Komponente Menge Wo? Preis/Stück Zzgl. Versandkosten
TTGO ESP32 3x Amazon 8,97 EUR Kostenlos
Ultraschallsensor 2x reichelt Elektronik 3,80 EUR 5,95 EUR
Arduino-Board 1x ebay 2,84 EUR Kostenlos
Gesamtkosten 43,30 EUR

Konzept[Bearbeiten]

Bilder vom Aufbau des Wireless Client Counter[Bearbeiten]

Aufbau-Erklärung[Bearbeiten]

Der Wireless Client Counter setzt sich aus einer Säule, die die Sensoren beinhaltet und einem Gegenstück, der nur eine Entfernungsgrenze darstellt, zusammen (Abbildung 1, Bilder Aufbau). Der Eingang verfügt über eine Schranke (Abbildung 2, Bilder Aufbau). Diese Schranke kann auch durch eine weiterleitung zu einer festen Tür umgeleitet werden. Abbildung 3 und 4 zeigt die Verkabelung, wobei in diesem Prototyp auch der Taster für den Webserver eingebaut ist (Dieser ist für den ESP-Webserver)

Implementierung[Bearbeiten]

Die Implementierung des Wireless Client Counter erfolgt in mehreren Schritten. Zunächst einmal ist zu betrachten, dass es den Wireless Client Counter zweimal gibt, nämlich für den Eingang und den Ausgang des Ladens. Aus diesem Grund werden für den Zähler zwei Codes geschrieben, Zähler für die Eingangskontrolle und die Ausgangskontrolle. Diese beiden Codes sind jedoch bis auf eine Ausnahme ein und derselbe Code. Nähere Erläuterungen folgen unten in den jeweiligen Teilabschnitten.

Eingangskontrolle[Bearbeiten]

Schaltplan espEntry

Die Codierung der Eingangskontrolle wurde in der Entwicklungsumgebung Arduino mit der Programmiersprache Java geschrieben. Wie oben erwähnt, befindet sich ein Wireless Client Counter am Eingang des Ladens. Die Zählfunktion geschieht hierbei über den Ultraschallsensor, sodass er einen Kunden beim Vorbeigehen erkennt und dies dann im System bzw. im ESP32-Webserver aufwertet. Dabei spielt der Abstand des Kunden zum Ultraschallsensor bzw. zu dem Wireless Client Counter eine wichtige Rolle. Der optimale Abstand für die Messung beträgt hier zwischen 10 und 60 cm. Nur unter dieser Bedingung kann der Ultraschallsensor Messwerte erfassen und anschließend an den Webserver senden. Was dann im Webserver passiert, wird im Abschnitt Webserver erläutert.

Arduino Bibliotheken[Bearbeiten]

Bevor es zu der Erklärung der Codes weitergeht, werden hier die in der Eingangskontrolle verwendeten Bibliotheken aufgezeigt. Es wurden nur zwei Bibliotheken gebraucht:

Übersicht der Bibliotheken-Eingangskontrolle
Wifi
PubSubClient

Code-Erläuterungen[Bearbeiten]

Code für die WiFi/MQTT-Verbindung[Bearbeiten]

In diesem Abschnitt werden die ID, das Passwort vom WLAN; der Name, die Port-Nr., der Benutzername und das Passwort von MQTT-Server initialisiert.

 //Setup Wifi
 const char* ssid="***";
 const char* password="***";
 const char* mqttServer = "hrw-fablab.de";
 const int mqttPort = 1883;
 const char* mqttUser = "gruppe12";
 const char* mqttPassword = "***********";
 ... 


Code für den WiFi-Verbindungsaufbau[Bearbeiten]

Kurz darauf versucht der ESP32 sich über die Informationen in Euren WLan Router einzuloggen. Sollte das nicht funktionieren, versucht er es weiter.

 // Connect to Wi-Fi   
 WiFi.begin(ssid, password); 
 
 while (WiFi.status() != WL_CONNECTED) { 
   delay(1000); 
   Serial.println("Connecting to WiFi.."); 
 } 
 Serial.println("Connected to the WiFi network");


Code für den MQTT-Verbindungsaufbau[Bearbeiten]

Nach der erfolgreichen Verbindung mit dem WLan verbindet er sich mit dem MQTT-Server. Wie auch mit dem WLan, versucht er es so oft, bis er sich verbinden konnte.

 void connectMQTT() {
   client.setServer(mqttServer, mqttPort);
    
   while (!client.connected()) {
     Serial.println("Connecting to MQTT...");  
     if (client.connect("ESP32ClientEntry", mqttUser, mqttPassword )) {   
       Serial.println("connected");   
     } else {  
       Serial.print("failed with state ");
       Serial.print(client.state());
       delay(2000);
     }
    }
   }


Code für den MQTT-Subscibe[Bearbeiten]

Der Teil gehört noch zum MQTT-Verbindungsaufbau, nur dass nun gesagt wird, worauf dieser ESP hören soll, damit dieser nicht auf alles vom MQTT reagiert.

   client.subscribe("ES/WS20/gruppe12/counterReset");
   client.subscribe("ES/WS20/gruppe12/statusRequestEntry");
   client.subscribe("ES/WS20/gruppe12/moveBarrier");
 }


Code für die Besucher-Messung[Bearbeiten]

In diesem Abschnitt wird definiert, dass der Ultraschallsensor alle 10 Mikrosekunden seine Messung mit den Besuchern neu beginnt und erfasst somit die aktuelle Besucherzahl. Diese werden dann nach der Verbindung zum WiFi und MQTT-Server auf der Website angezeigt.

 int measureVisitor() {
// Clears the trigPin digitalWrite(trigPin, LOW); delay(250);
// Sets the trigPin on HIGH state for 10 micro seconds digitalWrite(trigPin, HIGH); delay(10); digitalWrite(trigPin, LOW);
// Reads the echoPin, returns the sound wave travel time in microseconds duration = pulseIn(echoPin, HIGH);
// Calculating the distance distance = (duration/2) * 0.03432;
...
if(distance > 10 && distance < 60) { if(jam == 0) { visitor++; sprintf(visitorChar, "%d", visitor); client.publish("ES/WS20/gruppe12/visitorExit", visitorChar); jam = 1; } } else { jam = 0; Serial.println("Auf Besucher warten..."); } return visitor; }


Code für die Sensorerkennung[Bearbeiten]

Über dem WebServer werden in zeitlichen Abschnitten Sensorabfragen über den MQTT an den Ein- und Ausgangsesp geschickt. Beim Empfang dieser Abfrage prüft der ESP den eingebauten Sensor auf die Funktionsfähigkeit und schickt, wenn dieser funktionsfähig ist eine Rückgabe an den Webserver.

 void responseStatus() {
   Serial.println("");
   Serial.println("---------- SENSORREQUEST ----------");
   //Testing Sensor
   //Ein Teil von der Besuchermessung, indem der Sensor getestet wird

//publish if(distance > 0) { sensorStatus = 1; sprintf(sensorStatusChar, "%d", sensorStatus); client.publish("ES/WS20/gruppe12/statusResponseEntry", sensorStatusChar); Serial.println("Sensor correct"); } else { Serial.println("Sensor not correct. Please check the sensor!"); } Serial.println("---------- SENSORREQUEST END ----------"); Serial.println(""); sensorStatus = 0; sprintf(sensorStatusChar, "%d", sensorStatus); }


Code für den Besucher reset[Bearbeiten]

Dieser Code wird vom Webserver angesprochen, damit am Ende des Tages die Besucheranzahl der Sensoren gelöscht wird.

 void resetVisitor() {
   Serial.println("");
   Serial.println("---------- RESETCOUNTER ----------");
   visitor = 0;
   sprintf(visitorChar, "%d", visitor);
   client.publish("ES/WS20/gruppe12/visitorEntry", visitorChar);
   Serial.println("---------- RESETCOUNTER END----------");
   Serial.println("");
 }


Code für die Schranke[Bearbeiten]

Wenn der Webserver berechnet hat, dass das Limit der maximalen Besucher erreicht wurde, wird dieser Code am Eingang aktiviert. Hier die Schranke geschlossen bzw. nachdem der Webserver die Öffung pusht, wieder geöffnet. Wenn der Wert location 1 ist, dann wird der Eingang geschlossen, bei 0 geöffnet.

 void moveBarrier(int location) {
   if(location == 1) {
     for(int i=0; i<1000; i++) {
       if(servoHIGH > 1000) {
         servoHIGH--;
       }
       servoLOW = 2000 - servoHIGH;
       digitalWrite(barrierPin,HIGH);
       delayMicroseconds(servoHIGH);
       digitalWrite(barrierPin,LOW);
       delayMicroseconds(servoLOW);
     }
   } else if(location == 0) {
     for(int i=0; i<1000; i++){
       if(servoHIGH < 2000) {
         servoHIGH++; 
       }
       servoLOW = 2000 - servoHIGH;
       digitalWrite(barrierPin,HIGH);
       delayMicroseconds(servoHIGH);
       digitalWrite(barrierPin,LOW);
       delayMicroseconds(servoLOW);
     }
   } else {
     Serial.print("wrong incomming for barrier move: ");
     Serial.println(location);
   }
 }


Code für den Callback[Bearbeiten]

Der Callback reagiert auf Signale, die über den MQTT versendet werden. Diese Signale werden im Code verglichen (topic). Wenn die Datei für diesen ESP war, dann wird der Inhalt gelesen (payload) und in einer dazugehörigen Variable mit der richtigen Entität abgespeichert und die jeweilige Funktion aufgerufen (z.B. resetVisitor()).

 void callback(char* topic, byte* payload, unsigned int length) {
   Serial.print("Message arrived in topic: ");
   Serial.println(topic);
   Serial.print("Message:");
   for (int i = 0; i < length; i++) {
     contentCharArray[i] = (char)payload[i];
   }
   Serial.println(contentCharArray);
   
   Serial.println("-----------------------");
 
   topicString = String((char*)topic);
   if(topicString == "ES/WS20/gruppe12/counterReset") { 
     sscanf(contentCharArray, "%d", &counterReset);
     if(counterReset == 1) {
       resetVisitor();
     }
   } else if(topicString == "ES/WS20/gruppe12/statusRequestEntry") {
     sscanf(contentCharArray, "%d", &statusRequest);
     if(statusRequest == 1) {
       responseStatus();
     }
   } else if(topicString == "ES/WS20/gruppe12/moveBarrier") {
     sscanf(contentCharArray, "%d", &barrierLocation);
     moveBarrier(barrierLocation);
   } else {
     Serial.println("false topic");
     Serial.println("-----------------------");
     Serial.println("");
   }
 
   //nullen des Arrays fuer naechsten Eintrag
   for (int i = 0; i < length; i++) {
     contentCharArray[i] = 0;
   }
 }

Ausgangskontrolle[Bearbeiten]

Schaltplan espExit

Die Ausgangskontrolle ist wie die Eingangskontrolle codiert worden. Es wurden auch wieder dieselben Bibliotheken wie in der Eingangskontrolle verwendet. Einziger Unterschied zeigt sich im Subscribe des MQTTs, pushen und noch weiteren Verbindungen mit dem MQTT, die nun unten beschreiben werden. Zusätzlich gibt es keine Schranke wie beim Eingang.

Codeänderung-Erläuterung[Bearbeiten]

Da der Ausgang einen anderen Namen für den MQTT braucht und auf andere Topics, die von anderen ESPs gesendet werden hören soll, müssen viele Änderungen vorgenommen werden.

Codeänderung für den MQTT-Verbindungsaufbau Ausgang[Bearbeiten]

Hier ändert sich nur eine Zeile, damit der Ausgang einen anderen Namen beim MQTT bekommt als der Name vom Eingang.

 ...
 if (client.connect("ESP32ClientExit", mqttUser, mqttPassword )) {
   Serial.println("connected");   
 }
 ...


Codeänderung für den MQTT-Subscibe Ausgang[Bearbeiten]

Hier muss der Ausgang auf andere Topics hören, da dieser andere Topics als der Eingang empfängt (Topics des Eingangs).

 ...
 client.subscribe("ES/WS20/gruppe12/counterReset");
 client.subscribe("ES/WS20/gruppe12/statusRequestExit");
 ...


Codeänderung für die Besucher-Messung Ausgang[Bearbeiten]

Auch hier wird nicht die Zählung vom Eingang, sondern der vom Ausgang zum MQTT gesendet.

 ...
 client.publish("ES/WS20/gruppe12/visitorExit", visitorChar);
 ...


Codeänderung für die Sensorerkennung Ausgang[Bearbeiten]

Hier wird das Topic der Sensorrückgabe geändert, damit sich diese von der Sensorrückgabe des Eingangs unterscheidet.

 ...
 client.publish("ES/WS20/gruppe12/statusResponseExit", sensorStatusChar);
 ...


Codeänderung für den Besucher reset Ausgang[Bearbeiten]

Hier wird der reset der Besucherzählung bestätigt. Da sich dieser auch unterscheidet, muss hier das eingefügt werden (hier der Eingangsreset)

 ...
 client.publish("ES/WS20/gruppe12/visitorExit", visitorChar);
 ...


Codeänderung für den Callback Ausgang[Bearbeiten]

Hier sollte sich einiges ändern, da der Teil auf das hört, was vom Callback kommt. Da der Ausgang aber nicht auf viel hören muss, ändert sich nur ein Teil des statusRequests. (Hier zum Callback vom Eingang)

 ...
 } else if(topicString == "ES/WS20/gruppe12/statusRequestExit") {
 ...

Webserver[Bearbeiten]

Schaltplan espWebServer

Wireless Client Counter Webseite


Code-Erläuterung[Bearbeiten]

Da die Darstellung auf einem Fernseher sein soll, haben wir eine Webseite gebaut. Der Frontend wurde mit HTML, CSS(Bulma - ist ein CSS-Framework) & Javascript realisiert und das Backend wurde mit PHP, C++, SQL programmiert. Wir kommen zur Beschreibung unserer Webseite: In der obersten Reihe sehen Sie eine Art füllenden Wasserstand. Dies zeigt den aktuellen Stand der Belastung des Ladens an. Im Wasserstand stehen die aktuellen Besucherzahlen und die maximalen Besucheranzahl.

In der unteren Hälfte haben wir die Öffnungszeit. Die Öffnungszeiten können individuell angepasst werden. Danach haben wir in der Mitte unseren Säulendiagramm. Es zeigt die letzte Statistik der letzten 7 Tage. Die Werte empfangen wir von einer externen Datenbank, die wir eingerichtet haben. Auf der rechten Seite ist die Statusanzeige für die beiden Sensoren. Bei "Grün" ist der Sensor online und bei "Rot" ist der Sensor offline.

Arduino Bibliotheken[Bearbeiten]

Folgende Bibliotheken wurden für die Implementierung von Webserver für ESP32 Arduino verwendet:

Übersicht der Bibliotheken für den Webserver
AsyncTCP
ESPAsyncWebServer
HttpClient
PubSubClient
NTPClient

Code der Webseite[Bearbeiten]

WebServer-Part1.PNG WebServer-Part2.PNG

Codeänderung für den espWebserver[Bearbeiten]

Wie schon oben in Arduino Bibliotheken beschrieben, wurden hier ein paar mehr Bibliotheken verwendet. Diese werden für den untenstehenden Code gebraucht. Die Verindung mit dem WLAN bleibt bestehen und ist im EingangsESP beschrieben. Auch hier sind manche Codeabschnitte gleich, aber einiges ist auch neu.


Code für die WiFi/MQTT-Verbindung WebServer[Bearbeiten]

Zu den zusätzlichen Settings, die auch im Eingang und Ausgang vorhanden sind, kommen nun noch weitere für den WebServer hinzu (Code Settings Eingang).

 ...
 const char* serverName = "*****";
 String apiKeyValue = "*****";
 ...


Code für den WiFi-Verbindungsaufbau WebServer[Bearbeiten]

Da es sich im einen WebServer handelt, müssen wir hier noch hinzufügen, dass die verbindung über http funktioniert (Code vom Eingang).

 ...
 // Print ESP Local IP Address  
 Serial.println(WiFi.localIP()); // Route for root / web page 
 server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) { 
   request->send_P(200, "text/html", index_html, processor); 
 }); // Start server 
 server.begin();
 ...

und für die Zeit brauchen wir auch noch etwas, damit die periodischen Suchen nach den Sensoren, sowie der Reset funktioniert.

 ...
 WiFiUDP ntpUDP;
 NTPClient timeClient(ntpUDP, "pool.ntp.org", utcOffsetInSeconds);
 ...


Code für den MQTT-Verbindungsaufbau WebServer[Bearbeiten]

Auch hier wird der Name vom Webserver ein anderer als der Name vom Eingang.

 ...
 if (client.connect("ESP32ClientWebServer", mqttUser, mqttPassword )) {
     Serial.println("connected"); 
 }
 ...


Codeänderung für den MQTT-Subscibe WebServer[Bearbeiten]

Auch hier hört der WebServer nun auf andere pushes, die vom MQTT kommen (Topics des Eingangs).

 ...
 client.subscribe("ES/WS20/gruppe12/visitorEntry");
 client.subscribe("ES/WS20/gruppe12/visitorExit");
 client.subscribe("ES/WS20/gruppe12/statusResponseEntry");
 client.subscribe("ES/WS20/gruppe12/statusResponseExit");
 ...


Codeänderung für den Callback WebServer[Bearbeiten]

Der Callback des WebServers soll natürlich auch nur auf die Sachen hören, die von den Sensoren zurückgegeben werden und nicht auf die eigenen. Somit sind auch hier Änderungen. den oberen Teil davon stimmt mit Code für den Callback vom Eingang überein.

 ...
 if(topicString == "ES/WS20/gruppe12/visitorEntry") {
   sscanf(contentCharArray, "%d", &contentEntry);
   calculateVisitor(contentEntry, contentExit);
 } else if(topicString == "ES/WS20/gruppe12/visitorExit") {
   sscanf(contentCharArray, "%d", &contentExit);
   calculateVisitor(contentEntry, contentExit);
 } else if(topicString == "ES/WS20/gruppe12/statusResponseEntry") {
   sscanf(contentCharArray, "%d", &statusEntry);
   if(statusEntry == 1) {
     responseSensorStatus(topicString);
   }
 } else if(topicString == "ES/WS20/gruppe12/statusResponseExit") {
   sscanf(contentCharArray, "%d", &statusExit);
   if(statusExit == 1) {
     responseSensorStatus(topicString);
   }
 } else {
 ...

Neuer Code für den espWebserver[Bearbeiten]

Da sich der espWebserver nun um etwas handelt, was keine Sensorik mehr hat, wird dieser hauptsächlich Rechenoperationen und die WebSeite erstellen. Dafür brauchen wir neuen Code


Taster für manuellen Reset[Bearbeiten]

Hierbei handelt es sich um einen Resetbutton der, wenn dieser gedrückt wurde, die Sensoren resettet. Diesen haben wir gebraucht, wenn wir außerhalb des Tagesendes die Sensoren resetten wollten. In der Regel braucht man diesen aber in einem Unternehmen nicht mehr

 void buttonChecker() {
   if((digitalRead(resetButton) == HIGH) && (resetJam == 0)){
     Serial.print("--- Button push counterReset in Time: ");
     Serial.print(timeClient.getFormattedTime());
     Serial.println(" ---");
     resetJam = 1;
     toggleReset();
   } else if((digitalRead(resetButton) == LOW) && (resetJam == 1)) {
     Serial.println("--- Button counterReset pushed 0 ---");
     client.publish("ES/WS20/gruppe12/counterReset", "0");
     resetJam = 0;
   }
 }


Zeit überprüfen für Sensorerkennung und Reset[Bearbeiten]

Dieser Code ist dafür gedacht, dass regelmäßig die Zeit abgefragt wird und geprüft wird, ob 30 Sekunden, 10 Minuten oder gar der Tag vorbei ist. Wenn der Tag vorbei ist, werden die Sensoren resettet und etwas in die Datenbank gepushed, aber dazu später mehr. Am Anfang sind die Sensoren noch nicht registriert. Hier werden alle 30 Sekunden, wenn mindestens ein Sensor nicht gefunden wurde, nach den Sensoren gesucht. Wenn letzte Mal alle Sensoren gefunden wurden, dann wird der Code das nächste Mal erst nach 10 Minuten ausgegeben, damit der MQTT nicht überlastet wird. Zum Sensorenfinden kommt man hier.

 void timeChecker() {
   //updateTimeJam because it goes through several times in a second! without it will crash
   //timeClient.getFormattedTime() -> 19:12:05
   formattedTime = timeClient.getFormattedTime();
   formattedTimeLenght = formattedTime.length();
   if(updateTimeJam == 0) {  //has not yet been executed in this second = 0 (otherwise it would be executed several times per second)
     if(formattedTime == "00:00:00") {  //when the day ends -> reset Sensors/push DB
       updateTimeJam = 1;
       Serial.print(formattedTime);
       Serial.println("  -  Day ends - DBPush and reset Sensor");
       toggleReset();
     } else {  //day not end -> update/search Sensors
       if((entryOnline == 1) && (exitOnline == 1)) {  //entrance and exit was found last time
         if(formattedTime.substring(4, 5) == "0") {  //update/search Sensors every ten minutes
           updateTimeJam = 1;
           Serial.print(timeClient.getFormattedTime());
           Serial.println("  -  ten minutes - updateSensor");
           requestSensorStatus();
         }
       } else {  //entrance or/and exit was not found last time (onlineEntry == 0 || onlineExit == 0)
         if((formattedTime.substring(6, formattedTimeLenght) == "00") || (formattedTime.substring(6, formattedTimeLenght) == "30")) {  //update/search Sensors every thirty seconds
           updateTimeJam = 1;
           Serial.print(timeClient.getFormattedTime());
           Serial.println("  -  thirty seconds - updateSensor");
           requestSensorStatus();
         }
       }
     }
   } else if(formattedTime.substring(7, formattedTimeLenght) == "5"){ //Update/reset was (five seconds ago) (updateTimeJam == 1)
     updateTimeJam = 0;
     if(resetJam == 1) {
       Serial.println("--- Auto counterReset pushed 0 ---");
       client.publish("ES/WS20/gruppe12/counterReset", "0");
       resetJam = 0;
     }
   }
 }


Besucher Anzeige berechnen[Bearbeiten]

Hier werden die Besucher berechnet indem ganz einfach die eingegangenen Informationen (meistenes Zahlen) die vom Ein- und Augang nur hochgerechnet wurden, voneinander abgezogen werden. Im unteren Teil wird bei einer Überfüllung des Ladens die Schranke am Eingang gepushed, dass diese zu gehen soll. Bei einer gewissen Anzahl von der Personen, die den Laden wieder müssen, geht die Schranke wieder auf.

 void calculateVisitor(int contentEnrty, int contentExit) {
   Serial.print("contentEntry: ");
   Serial.println(contentEnrty);
   Serial.print("contentExit: ");
   Serial.println(contentExit);
   visitor = contentEnrty - contentExit;
   availableVisitor = maxVisitor - visitor;
   Serial.print("Visitor: ");
   Serial.println(visitor);
   Serial.print("Available Visitor: ");
   Serial.println(availableVisitor);
   Serial.println("-----------------------");
   Serial.println("");
 
   if((visitor >= maxVisitor) && (barrierClosed == 0)) {  //close entry barrier, if maxVisitor reached and barrier hasnt closed yet 
     client.publish("ES/WS20/gruppe12/moveBarrier", "1");
     barrierClosed = 1;
   } else if((visitor <= (maxVisitor-5)) && (barrierClosed == 1)) {  //open entry barrier, if visitor 5 less than the maximum visitor and barrier is closed
     client.publish("ES/WS20/gruppe12/moveBarrier", "0");
     barrierClosed = 0;
   }
 }


Reset für die Sensoren[Bearbeiten]

Dieser Teil wird beim Reset der Sensoren aktiviert. Damit werden die Variablen, die sich in diesem ESP befinden, in die Datenbank gepushed und nachträglich gelöscht. Zusätzlich wird der Reset an die Sensoren geleitet.

 void toggleReset(){
   average = ((float)contentEntry)/oeffnungszeit;
   Serial.print("3:");
   Serial.println(average);
   pushDB(average);
   Serial.print("DB Pushed and toggle reset launched in Time: ");
   Serial.print(timeClient.getFormattedTime());
   Serial.println("!");
   Serial.print("4:");
   Serial.println(average);
   //average = 0.0;
   Serial.print("5:");
   Serial.println(average);
   contentEntry = 0;
   contentExit = 0;
   entryOnline = 0;
   exitOnline = 0;
   
   resetJam = 1;
   tryResetSensor = 1;
   //are all sensors aviable (reset is inside the statusRequest and goes to resetVisitor())
   client.publish("ES/WS20/gruppe12/statusRequestEntry", "1");
   client.publish("ES/WS20/gruppe12/statusRequestExit", "1");
 }


Sensoren prüfen[Bearbeiten]

Die Sensoren werden in regelmäßigen abständen geprüft und dafür ist hier der Codeabschnitt. Wenn es sich sogar um ein Reset gehandelt oder dass sich kein Sensor beim letzten durchgang gemeldet hatte, dann werden noch ein paar Variablen resettet (für die Anzeige im Webserver).

 void requestSensorStatus() {
 
   if((tryRequest == 0) || (noResponse != 0)) {
       statusEntry = 0;
       statusExit = 0;
       entryOnline = 0;
       exitOnline = 0;
       client.publish("ES/WS20/gruppe12/statusResponseEntry", "0");
       client.publish("ES/WS20/gruppe12/statusResponseExit", "0");
   }
   noResponse = 1;  //check whether all sensors are not there
   client.publish("ES/WS20/gruppe12/statusRequestEntry", "1");
   client.publish("ES/WS20/gruppe12/statusRequestExit", "1");
   Serial.println("----- pushed sensor status -----");
 }


Sensoren melden sich[Bearbeiten]

Sobald ein Sensor auf den sensorRequest gemeldet haben, dann wird dieser Teil ausgelöst. Es wird nachgeguckt welcher Sensor es ist, um die anderen nochmals zu pushen, da der esp meist nur den ersten push zuhört und zwischen der Berechnung keine weiteren empfangen kann. Das ganze geht so lange durch, bis alle Sensoren gefunden wurden. Nun wird das entweder nur abgespeichert oder, wenn der Tag zu ende war, resettet .

 void responseSensorStatus(String topicString) {
   noResponse = 0;  //one or more sensors found
   if(tryRequest < 4) {  //four tries to find the sensors
     if(topicString == "ES/WS20/gruppe12/statusResponseEntry") {
       entryOnline = statusEntry;
     } else if(topicString == "ES/WS20/gruppe12/statusResponseExit") {
       exitOnline = statusExit;
     }
 
     if((statusEntry == 1) && (statusExit == 1)) {  //reset because sensor online for next request
       statusEntry = 0;
       statusExit = 0;
       tryRequest = 0;
       client.publish("ES/WS20/gruppe12/statusRequestEntry", "0");
       client.publish("ES/WS20/gruppe12/statusRequestExit", "0");
       client.publish("ES/WS20/gruppe12/statusResponseEntry", "0");
       client.publish("ES/WS20/gruppe12/statusResponseExit", "0");
       Serial.println("----- all sensors found -----");
       //from sensor reset?
       if(tryResetSensor == 1) {
         tryResetSensor = 0;
         resetVisitor();
       }
     } else {
       if(statusEntry == 0) {  //Entry not found
         client.publish("ES/WS20/gruppe12/statusRequestEntry", "1");
         Serial.print(tryRequest);
         Serial.println(" try to find Entry again");
         tryRequest++;
       }
       if(statusExit == 0) {   //Exit not found
         client.publish("ES/WS20/gruppe12/statusRequestExit", "1");
         Serial.print(tryRequest);
         Serial.println(" try to find Exit again");
         Serial.println(exitOnline);
         tryRequest++;
       }
     }
   } else {  //cant find sensors
     tryRequest = 0;
     Serial.println();
     Serial.println("------------------------");
     Serial.print("Can't find one or more Sensors. Sensor Entry: ");
     Serial.print(statusEntry);
     Serial.print(", sensor Exit: ");
     Serial.println(statusExit);
     delay(1000);
   
     Serial.println("Request again!");
     Serial.println("------------------------");
     Serial.println();
     requestSensorStatus();
   }
 }


SQL Datenbank[Bearbeiten]

Wir nutzen im Hintergrund eine SQL Datenbank, die nach einem Reset gefüllt werden muss und dauerhaft vom WebServer abgerufen wird. In diesem Teil ensteht das füllen der Datenbank

 void pushDB(float average) {
   HTTPClient http;
   Serial.print("1:");
   Serial.println(average);
   // Your Domain name with URL path or IP address with path
   http.begin(serverName);
   
   // Specify content-type header
   http.addHeader("Content-Type", "application/x-www-form-urlencoded");
   
   // Prepare your HTTP POST request data
   String httpRequestData = "api_key=" + apiKeyValue + "&average=" + average;
   Serial.print("2:");
   Serial.println(average);
   Serial.print("httpRequestData: ");
   Serial.println(httpRequestData);
   
   // Send HTTP POST request
   int httpResponseCode = http.POST(httpRequestData);
     
   if (httpResponseCode>0) {
     Serial.print("HTTP Response code: ");
     Serial.println(httpResponseCode);
   }
   else {
     Serial.print("Error code: ");
     Serial.println(httpResponseCode);
   }
   // Free resources
   http.end();
 }


Besucheranzahl resetten[Bearbeiten]

Nun wird am Ende noch die Besucheranzahl gelöscht und das ganze auch an die Sensoren weiter gegeben. Das passiert entweder nach dem Tagesende oder durch den Taster.

 void resetVisitor() {
   contentEntry = 0;
   contentExit = 0;
   client.publish("ES/WS20/gruppe12/counterReset", "1");  //publish to reset all sensors
   if(barrierClosed == 1) {
     client.publish("ES/WS20/gruppe12/moveBarrier", "0");  //publish to reset/open the gate/barrier for e.g. the next day
     barrierClosed = 0;
   }
 }


Datenbank[Bearbeiten]

Die Datenbank benötigen wir für die durchschnittliche Besucheranzahl nach Ladenschluss. Der Wert wird in die Datenbank gesendet und von dort aus werden die Werte in einen Bar-Chart gesendet.

MQTT[Bearbeiten]

MQTT.png

MQTT, Message Queuing Telemetry Transport ist ein Netzwerkprotokoll für die Kommunikation von Maschinen. Hierbei handelt es sich bei den Geräten hauptsächlich um Eingebettete Systeme, die untereinander mit Hilfe von MQTT-Netzwerkprotokoll Telemetriedaten in Form von Nachrichten austauschen. In unserem Projekt ist der MQTT-Server ein wichtiger Bestandteil. Es empfängt die wichtigsten Werte der Sensoren aus dem Eingang und Ausgang. Als Topics haben wir noch die Kontrolle der Verfügbarkeit der Sensoren, ob die mit dem Webserver kommunizieren können oder online sind.

Aufgetretene Probleme und Lösungen[Bearbeiten]

Ein kleines Problem war ein Fehler im Code, wo wir die gleiche Client-ID in den MQTT-Server gepusht haben und dadurch haben wir beim Senden der Daten Probleme gehabt. Ansonsten hatten wir viele Probleme mit der Arduino IDE gehabt, wo wir immer alles abgespeichert haben und er uns eine alte Version angezeigt hat. Dieses Problem war nicht so schlimm, da wir ein Backup für alle Versionen hatten.

Ein schlimmeres Problem war die Abfragen der Sensoren. Der ESP32 hört nur auf einen push. Sollte sich zu der Zeit bzw. in der Zeit, indem er etwas anderes erledigt, ein weiterer Push kommen, reagiert er nicht darauf. Deshalb wurden teilweise Sensoren nicht gefunden. Um dieses Probem zu lösen, werden Variablen mit 1 belegt, wenn ein Sensor gefunden wurde und die ohne der 1 wieder gepusht bis alle gefunden wurden. Dadurch hatten wir aber innderhalb von 10 Sekunden mehr als 30.000 Übertragungen in dem MQTT, sodass wir das mit der Internetzeit abstoppen mussten (delay würde sonst alles stoppen, was fatal wäre). Zudem, damit der nicht mehrmals in dieser einen Sekunde, wo der WebServer z.B. 00:00:00 empfängt versucht die Sensoren zu resetten, haben wir viele "jams" eingesetzt, damit es nur beim ersten Mal ausgeführt wird.

Vieles, was schnell fertiggewesen wäre, wurden zu mehreren Stunden Arbeit. Das lag entweder an den oberen Problemen, an Unkenntnissen wie man es am besten macht und daran, dass wir immer mehr nutzen mussten, damit etwas funktioniert. z.B. sollten mehr Animationen auf dem WebServer passieren wie z.B. Werbung die sich immer ändert und mehr. Das funktionierte aber nicht so wie wir es wollten auf dem ESP32. Somit mussten wir für PHP und JavaScript einen öffentlichen WebServer einrichten, der dann auf die öffentliche Datenbank zugriff hat, damit die Kundenanzeige funktioniert. Es ist somit viel Arbeit, welches z.Z. aber weniger kostet, als selber einen eigenen Server zu kaufen.

Informationen zum Nachbauen[Bearbeiten]

Boardverwalter[Bearbeiten]

Man braucht, damit man den ESP32 benutzen kann erstmal die Bordinformationen, die in die Arduiuno IDE eingelesen werden. Um diese zu ändern, müssen Sie in der Arduiuno IDE auf Datei -> Voreinstellungen -> Zusätzliche Boardverwalter-URLs und dort die Links: "https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json,,https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_dev_index.json,,https://dl.espressif.com/dl/package_esp32_index.json" einfügen. Das sind die Boardverwalter für den TTGO ESP32, den wir für unser Projekt verwendet haben

Libraries[Bearbeiten]

Hier ist nochmal eine Ansicht aller libraries, die wir in unserem Projekt benutzt haben. Diese können Sie auch über Werkzeuge -> Bibliotheken verwalten suchen

Übersicht der Bibliotheken Published by Herkunft
AsyncTCP GitHub
ESPAsyncWebServer GitHub
HttpClient Adrian McEwen Bibliotheksverwalter
PubSubClient Nick O'Leary Bibliotheksverwalter
NTPClient Fabrice Weinberg Bibliotheksverwalter

Beim Herunterladen von GitHub kann es passieren, dass die Datein master_* heißen. Wenn es so sein sollte, die Datein in einen Ordner entpacken und bei dem Ordner, sowie der datei das "master" löschen und speichern.

Werkzeugeinstellung[Bearbeiten]

Board ESP32 Dev Module
Upload Speed 921600
CPU Frequency 240 MHz (WiFi/BT)
Flash Frequency 80 MHz
Flash Mode QIO
Flash Size 4MB (32Mb)
Partition Scheme Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS)
Core Debug Level Keine
PSRAM Disabled