Zisterne 4.0 + Nextion Display ohne Internet / WLAN Router (UPD)

10 Kommentare
Zisterne 4.0 + Nextion Display ohne Internet / WLAN Router (UPD)

In diesem Beitrag geht es um einen Füllstandsanzeige für eine Zisterne, Brunnen oder einen Wassertank. Sicher gibt es noch viele weitere Anwendungsgebiete.

Das besondere an diesem Projekt ist, man kann es überall einsetzten wo kein WLAN / Internet zur Verfügung steht. Wie zum Beispiel abgelegene Gartenanlagen oder ähnliches.

Es werden hier 2 ESP8266 Boards benötigt, dass eine Board fungiert als Sender und das andere Board als Empfänger. Die Ausgabe wird über ein Nextion Display 3.5 Zoll realisiert. Als Übertragungprotokoll wird UPD verwendet.


Ich konnte hier Reichweiten von 60-100m erreichen. Es ist hier vom Gelände abhängig wie weite das WLAN reicht. Im Bereich von 90-100m kommt es jedoch schon zu Fehlern bei der Übertragung (Aussetzer)


Für das Projekt habe ich mir zwei Platinen anfertigen lassen.





Das Empfängerboard wird normal mit Stromversorgt. Das Senderboard kann hier mit einem Akku betrieben werden. Beim Lolin D1 mini Pro v2 ist die Ladeelektronik mit auf der Platine. Wenn hier eine Spannungquelle an den USB Port angeschlossen wird, wird der Akku geladen.

Alternativ kann auch der D1 mini Pro v1 verwendet werden. Hier muss dann das Batterie Schield mit genutzt werden.



Was wird für das Projekt benötigt.

1. Installation ESP8266 in der Arduino IDE

Um den EPS8266 in der Arduino IDE verwenden zu können, muss dieser erst installiert werden. Wie das geht habe ich in diesem Blogbeitrag schon einmal erklärt.



Welche Bauteile werden benötigt.

1x Nextion 3.5″ Discovery Touch Display 480×320 HMI – NX4832F035 - https://ap-url.de/nextion35d
1x Zisterne 4.0 Platinen Set - https://arduino-projekte.info/produkt/zisterne-4-0-platine/
1x Ultraschallentfernungsmesser AJ-SR04M / JSN-SR04T - https://ap-url.de/jsnsr04t
2x Externe Antenne für D1 mini Pro Modelle - https://ap-url.de/extantenne
je nach Wahl einen D1 für ich Empfehle
2x Wemos Lolin D1 mini Pro V2.0 - https://ap-url.de/d1miniprov2

Funktionsweise

Der Sender sendet 3 Datenpacket. 1. die Batteriespannung, 2. die Nummer der letzten Messung und 3. die Messdaten vom Ultraschallsensor.

Der Empfänger empfängt die Daten und teilt sie auf zur Weitergabe an das Nextion Display.

Wenn die Externe Antenne verwendet werden soll muss der Widerstand umgelötet werden oder einfach einen Lötklecks über die Pads ziehen.


Sender

Sketch Um die Batteriefunktion  (Messung) nutzen zu können müsst Ihr am D1 mini pro v2.0 oder dem D1 Lipo Battery Shield. Je nachdem was Ihr verwendet das Lotpad auf der Rückseite verbinden.


Für die Deep Sleep Funktion müsst Ihr auf der Rückseite des D1 mini pro v2.0 das Lötpad Sleep verbinden.


#include <esp8266wifi.h>
#include <wifiudp.h>
#include <eeprom.h>
  
#ifndef STASSID
#define STASSID "WIFI_UPD_R"
#define STAPSK  "123456789+"
#endif

IPAddress ip(192, 168, 10, 10);
IPAddress gateway(192, 168, 10, 1);
IPAddress subnet(255, 255, 255, 0);

unsigned int sendPort = 9500;

int trigger = 2;
int echo = 4;
long dauer = 0;
int counter = 0;

unsigned int raw = 0;
float batt = 0.0;
long wasser = 0;
const int sleepSeconds = 2000;

WiFiUDP Udp;

void setup() {
  pinMode(A0, INPUT);
  Serial.begin(115200);
  pinMode(trigger, OUTPUT);
  pinMode(echo, INPUT);

  WiFi.mode(WIFI_STA);
  WiFi.config(ip, gateway, subnet);
  WiFi.begin(STASSID, STAPSK);
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print('.');
    delay(500);
  }
  Serial.print("Connected! IP address: ");
  Serial.println(WiFi.localIP());
  Serial.printf("UDP server on port %d\n", sendPort);
  Udp.begin(sendPort);
}

void batt_status()
{
  raw = analogRead(A0);
  batt = raw / 1023.0;
  batt = batt * 4.2;
}

void zeit()
{
  counter++;
  EEPROM.write(1, counter);
  Serial.println(counter);
  if (counter >= 21)
  {
    counter = 0;
    EEPROM.write(1, counter);
    EEPROM.commit();
    EEPROM.end();
    ESP.reset();
  }
}

void loop() {
  EEPROM.begin(512);
  counter = EEPROM.read(1);
  batt_status();
  zeit();
  digitalWrite(trigger, LOW);
  delay(5);
  digitalWrite(trigger, HIGH);
  delay(10);
  digitalWrite(trigger, LOW);
  dauer = pulseIn(echo, HIGH);
  wasser = (dauer / 2) / 29.1;

for (int i=0; i <= 3; i++){
  Udp.beginPacket(gateway, sendPort);
  Udp.write("Batterie:");
  Udp.print(batt);
  Udp.write(";count:");
  Udp.print(counter);
  Udp.write(";wasser:");
  Udp.print(wasser);
  Udp.endPacket();
  delay(1000);
}
  EEPROM.commit();
  EEPROM.end();
  delay(3000);
  ESP.deepSleep(sleepSeconds * 1000000);
}


Funktionsweise

Als erstes werden die Librarys (Bibliotheken) inkludiert.

#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
#include <EEPROM.h>


WLAN Daten um auf den Empfänger zugriff zu bekommen. IP Adressen und festlegen auf welchen UDP Port gesendet wird.

#ifndef STASSID
#define STASSID "WIFI_UPD_R"
#define STAPSK  "123456789+"
#endif

IPAddress ip(192, 168, 10, 10);
IPAddress gateway(192, 168, 10, 1);
IPAddress subnet(255, 255, 255, 0);

unsigned int sendPort = 9500;

Definieren der Signale und Variablen.

// Ultraschallsensor
int trigger = 2;
int echo = 4;
long dauer = 0;

// Zähler für die Messreihe
int counter = 0;

// Variablen für die Batterie und dem Wasserstand
unsigned int raw = 0;
float batt = 0.0;
long wasser = 0;

// Zeit für den DeepSleep in Sekunden
const int sleepSeconds = 2000;

WiFiUDP Udp;

Im Setupteil brauch ich glaub ich nicht viel zu sagen. Hier werden die Ausgangspin, Eingangspins und das WLAN gestartet.

void setup() {
  pinMode(A0, INPUT);
  Serial.begin(115200);
  pinMode(trigger, OUTPUT);
  pinMode(echo, INPUT);

  WiFi.mode(WIFI_STA);
  WiFi.config(ip, gateway, subnet);
  WiFi.begin(STASSID, STAPSK);
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print('.');
    delay(500);
  }
  Serial.print("Connected! IP address: ");
  Serial.println(WiFi.localIP());
  Serial.printf("UDP server on port %d\n", sendPort);
  Udp.begin(sendPort);
}

Routine für das Batterie messen, der Wert wird in "batt" gespeichert.

void batt_status()
{
  raw = analogRead(A0);
  batt = raw / 1023.0;
  batt = batt * 4.2;
}

Routine für den Zähler. Dieser dient nur dazu um zu pürfen ob die Messung funktioniert hat. Hier kann man sicher auch eine andere Möglichekeit wählen. Der Zähler zählt bis 20 und fängt dann wieder von vorne an. Der Zähler wird im EEPROM gespeichert, damit er nach dem DeepSleep / Ausschalten wieder zur verfügung steht. Der ESP wird nach den 20 Messwerten 1x Resetet.

void zeit()
{
  counter++;
  EEPROM.write(1, counter);
  if (counter >= 21)
  {
    counter = 0;
    EEPROM.write(1, counter);
    EEPROM.commit();
    EEPROM.end();
    ESP.reset();
  }
}

Routine für den Zähler. Dieser dient nur dazu um zu pürfen ob die Messung funktioniert hat. Hier kann man sicher auch eine andere Möglichekeit wählen. Der Zähler zählt bis 20 und fängt dann wieder von vorne an.

void loop() {
  // EEPROM Starten
  EEPROM.begin(512);
  // Zähler aus dem EEPROM lesen
  counter = EEPROM.read(1);
  batt_status();
  zeit();
  // Ultraschallmessung
  digitalWrite(trigger, LOW);
  delay(5);
  digitalWrite(trigger, HIGH);
  delay(10);
  digitalWrite(trigger, LOW);
  dauer = pulseIn(echo, HIGH);
  wasser = (dauer / 2) / 29.1;

  // Daten werden gesendet, zur sicherheit 3x im ein Sekundenabstand
for (int i=0; i <= 3; i++){
  Udp.beginPacket(gateway, sendPort);
  Udp.write("Batterie:");
  Udp.print(batt);
  Udp.write(";count:");
  Udp.print(counter);
  Udp.write(";wasser:");
  Udp.print(wasser);
  Udp.endPacket();
  delay(1000);
}
  // EEPROM schließen
  EEPROM.commit();
  EEPROM.end();
  delay(3000);
  // deepSleep ausführen
  ESP.deepSleep(sleepSeconds * 1000000);
}

Empfänger Sketch

#include <esp8266wifi.h>
#include <wifiudp.h>
#include <softwareserial.h>

#ifndef APSSID
#define APSSID "WIFI_UPD_R"
#define APPSK  "123456789+"
#endif
SoftwareSerial nextion(12, 13); // RX, TX

IPAddress ip(192, 168, 10, 1);
IPAddress gateway(192, 168, 10, 10);
IPAddress subnet(255, 255, 255, 0);

unsigned int recivePort = 9500;

// buffers für Empfangene Daten
char packetBuffer[UDP_TX_PACKET_MAX_SIZE + 1]; //buffer to hold incoming packet,
char * values[3];
char * buf[3];
float batt;
unsigned int w_stand_txt;
unsigned int w_stand;

WiFiUDP Udp;

void setup() {
  nextion.begin(9600);
  Serial.begin(115200);
  Serial.println();
  Serial.print("Configuring access point...");
  WiFi.softAPConfig(ip, gateway, subnet);
  WiFi.softAP(APSSID, APPSK);
  delay(500);
  Serial.print("AP IP address: ");
  Serial.println(WiFi.softAPIP());
  Serial.printf("UDP server on port %d\n", recivePort);
  Udp.begin(recivePort);

  nextion.print("p0.pic=");
  nextion.print(0);
  sendTOdisplay();
}

void sendTOdisplay()
{
  nextion.write(0xFF);
  nextion.write(0xFF);
  nextion.write(0xFF);
}

void loop() {
  String cmd;
  cmd += "\"";
  // sind Daten vorhanden, lese ein packet
  int packetSize = Udp.parsePacket();
  if (packetSize) {

    // lese das packet in packetBufffer
    int n = Udp.read(packetBuffer, UDP_TX_PACKET_MAX_SIZE);
    packetBuffer[n] = 0;
    //Empfangene Daten zerlegen
    char* ptr = strtok(packetBuffer, ";");
    for (byte i = 0; i < sizeof(values) / sizeof(values[0]); i++)
    {
      values[i] = ptr;
      ptr = strtok(NULL, ";");
    }

    buf[0] = strtok(values[0], ":");
    batt = atof(strtok(NULL, ":"));     //Batterie Wert
    buf[1] = strtok(values[1], ":");
    buf[2] = strtok(NULL, ":");     // Counter
    buf[3] = strtok(values[2], ":");
    w_stand_txt = atof(strtok(NULL, ":"));     // Messwert Ultraschallsensor

    //Daten an das Display senden
    nextion.print("batterie.txt=" + cmd + batt);
    nextion.print(" V" + cmd);
    sendTOdisplay();
    nextion.print("zeit.txt=" + cmd + buf[2] + cmd);
    sendTOdisplay();
    nextion.print("w_stand.txt=" + cmd + w_stand_txt + cmd);
    sendTOdisplay();
    w_stand = w_stand_txt * 0.1666667; // Berechnung für die Seule
    w_stand = 100 - w_stand;           // Berechnung für die Seule
    nextion.print("wstand.val=");
    nextion.print(w_stand);
    sendTOdisplay();
    if (batt >= 3.8 && batt <= 4.1)
    {
      nextion.print("p0.pic=");
      nextion.print(4);
      sendTOdisplay();
    }
    if (batt >= 3.5 && batt <= 3.8)
    {
      nextion.print("p0.pic=");
      nextion.print(3);
      sendTOdisplay();
    }
    if (batt >= 3.3 && batt <= 3.5)
    {
      nextion.print("p0.pic=");
      nextion.print(2);
      sendTOdisplay();
    }
    if (batt >= 0 && batt <= 3.3)
    {
      nextion.print("p0.pic=");
      nextion.print(1);
      sendTOdisplay();
    }
    delay(100);
  }
}

Funktionsweise

Als erstes werden die Librarys (Bibliotheken) inkludiert.

#include <esp8266wifi.h>
#include <wifiudp.h>
#include <softwareserial.h>

WLAN Daten für den eigenen Access Point. IP Adressen und festlegen auf welchen UDP Port empfangen wird. Der SoftwareSerial legt fest wo die RX/TX Leitungen für das Display liegen.

#ifndef APSSID
#define APSSID "WIFI_UPD_R"
#define APPSK  "123456789+"
#endif
SoftwareSerial nextion(12, 13); // RX, TX

IPAddress ip(192, 168, 10, 1);
IPAddress gateway(192, 168, 10, 10);
IPAddress subnet(255, 255, 255, 0);

unsigned int recivePort = 9500;

Definieren der Variablen.

// buffers für Empfangene Daten
char packetBuffer[UDP_TX_PACKET_MAX_SIZE + 1]; //buffer to hold incoming packet,
char * values[3];
char * buf[3];
float batt;
unsigned int w_stand_txt;
unsigned int w_stand;

WiFiUDP Udp;

Serielle Schnittstellen, WLAN und das UDP werden gestartet.

void setup() {
  nextion.begin(9600);
  Serial.begin(115200);
  Serial.println();
  Serial.print("Configuring access point...");
  WiFi.softAPConfig(ip, gateway, subnet);
  WiFi.softAP(APSSID, APPSK);
  delay(500);
  Serial.print("AP IP address: ");
  Serial.println(WiFi.softAPIP());
  Serial.printf("UDP server on port %d\n", recivePort);
  Udp.begin(recivePort);

  // Nextion Display Batterie Bild auf leer setzen.
  nextion.print("p0.pic=");
  nextion.print(0);
  sendTOdisplay();
}

Im LOOP Teil werden hier die Datenpakete empfangen und in 3 Werte zerlegt.

// sind Daten vorhanden, lese ein packet
int packetSize = Udp.parsePacket();
if (packetSize) {

  // lese das packet in packetBufffer
  int n = Udp.read(packetBuffer, UDP_TX_PACKET_MAX_SIZE);
  packetBuffer[n] = 0;
  //Empfangene Daten zerlegen
  char* ptr = strtok(packetBuffer, ";");
  for (byte i = 0; i < sizeof(values) / sizeof(values[0]); i++)
  {
    values[i] = ptr;
    ptr = strtok(NULL, ";");
  }

  buf[0] = strtok(values[0], ":");
  batt = atof(strtok(NULL, ":"));     //Batterie Wert
  buf[1] = strtok(values[1], ":");
  buf[2] = strtok(NULL, ":");     // Counter
  buf[3] = strtok(values[2], ":");
  w_stand_txt = atof(strtok(NULL, ":"));     // Messwert Ultraschallsensor

Im Anschluss werden die Werte an das Display übertragen.

//Daten an das Display senden
  nextion.print("batterie.txt=" + cmd + batt);
  nextion.print(" V" + cmd);
  sendTOdisplay();
  nextion.print("zeit.txt=" + cmd + buf[2] + cmd);
  sendTOdisplay();
  nextion.print("w_stand.txt=" + cmd + w_stand_txt + cmd);
  sendTOdisplay();
  w_stand = w_stand_txt * 0.1666667; // Berechnung für die Seule
  w_stand = 100 - w_stand;           // Berechnung für die Seule
  nextion.print("wstand.val=");
  nextion.print(w_stand);
  sendTOdisplay();
  if (batt >= 3.8 && batt <= 4.1)
  {
    nextion.print("p0.pic=");
    nextion.print(4);
    sendTOdisplay();
  }
  if (batt >= 3.5 && batt <= 3.8)
  {
    nextion.print("p0.pic=");
    nextion.print(3);
    sendTOdisplay();
  }
  if (batt >= 3.3 && batt <= 3.5)
  {
    nextion.print("p0.pic=");
    nextion.print(2);
    sendTOdisplay();
  }
  if (batt >= 0 && batt <= 3.3)
  {
    nextion.print("p0.pic=");
    nextion.print(1);
    sendTOdisplay();
  }
  delay(100);
}



Wie wird das Programm auf das Nextion Display geladen?

Ihr packt die brunnen.tft Datei auf eine Micro SD Karte und steckt diese in das Display. Dann das Display mit Spannung versorgen und der Upload wird gestartet. Nach dem Upload das Display wieder von der Spannung trennen und nach dem nächsten Verbinden ist das Programm aktiv.



Hier könnt Ihr euch den Sketch runterladen.

Download Sketch + Display Daten (230kb .zip)




Wenn Ihr hier mit einem Akku arbeitet dann nach dem ausfstecken noch einmal den Reset Knopft drücken.

Sicherlich kann die ein oder andere Stelle noch verbessert werden. Ich hoffe es ist alles soweit verständich. Wenn Ihr fragen habe schickt mir einfach ein Mail. Ich versuche Sie zeitnah zu beantworden.



Folgende Bauteile wurden verwendet:

1x Nextion 3.5″ Discovery Touch Display 480×320 HMI – NX4832F035 - https://ap-url.de/nextion35d
1x Zisterne 4.0 Platinen Set - https://arduino-projekte.info/produkt/zisterne-4-0-platine/
1x Ultraschallentfernungsmesser AJ-SR04M / JSN-SR04T - https://ap-url.de/jsnsr04t
2x Externe Antenne für D1 mini Pro Modelle - https://ap-url.de/extantenne
je nach Wahl einen D1 für ich Empfehle
2x Wemos Lolin D1 mini Pro V2.0 - https://ap-url.de/d1miniprov2

ESP8266 Nextion Display Projekte ultraschallsensor Zisterne Füllstandsanzeige

10 Kommentare

Hallon Tobias,
ich heiße Winfried, Velopedalist ist lediglich mein Nickname.
Klasse das du mir helfen kannst. Als Draht gebundene Version klappt der Reichelt Sketch gut. Jedoch habe ich die Prozentangabe rausgenommen, ist bei den Kleintanks nicht nötig und 5% bei 80 Liter sind eben nur 4 Liter. Um rechtzeitig nachzutanken oder sparsam zu sein, ist die grafische Anzeige viel aussagekräftiger. Ich habe das Ganze mit einen 0,96" OLED aufgebaut, einfach weil die Anzeige blau (weiß) auf dunklem Grund im Dunkeln kaum störend ist, eventuell könnte man ja noch den Kontrast wenn es dunkel ist noch runtersetzen. Vielleicht gibst du mir Hilfestellung, generell zum Wemo und speziell wie man zwischen Sender und Empfänger den Sketch auseinander nimmt. Komplette Auswertung und Umrechnung so das nur noch die für die Anzeige relevanten Daten übertragen werden oder nur die Rohdaten des Wasserstandes und alles Auswertung auf der Empfängerseite. Ich freue mich von dir zu hören und etwas dazu lernen zu können.
Gruß aus dem Ruhrgebiet
Winfried

velopedalist

Hallo velopedalist,
das sollte kein Problem sein, der Sketch von der Zisterne 4.0 kann auf deine Bedürfnisse umgeschrieben werden.
Gruß
Tobias

arduino projekte

Hallo,ich beschäftige mich z.Z. auch mit dem Projekt für Kleintanks in WoMO’s bzw. Booten und habe in einem anderen Beitrag des Verfassers schon auf den Sensor A0221AM hingewiesen. Vielfach wird der A022YYUW auch SEN0311 als wasserdichter Ultraschallsensor beworben. Das ist er auch, aber nicht HC-SR04 kompaktibel, da mit UART Ausgang. Es gibt aber vom gleichen Hersteller den A0221AM mt PWM Ausgang und 1=1 gegen HC-SR04 tauschbar. Der Vorteil des A0221AM gegen über dem AJ-SR04M / JSN-SR04T ist der geringe Blindbereich von ca. 2-3 cm. Leider auch etwa doppelt so teuer. Zu finden bei Aliexpress beim Händler Win-Win.Jetzt meine Frage: ich möchte mit einem Ad-Hoc-Netzwerk meine Daten mittels 2 Wemos D1 Mini übertragen und die Ausgabe auf einem OELD Display anzeigen. Balkengrafik und Literangabe. Ähnlich oder auch gleich wie bei:worldwideweb.reichelt.de/magazin/maker-hub/fuellstandsanzeige-mit-ultraschallsensor-hc-sr04Ich hoffe auf Hilfe, da ich mit fast 75 Jahren nicht mehr ganz fit bin, im Studium das letzte mal programmiern (Fortran) musste und erst jetzt mich mit den Minni’s beschäftige.😀

velopedalist

Hallo Markus,
der Sensor sollte genauso funktionieren wie der den ich einsetzte.

Als Board würde ich den Wemos Lolin D1 mini Pro verwenden.
https://arduino-projekte.info/produkt/wemos-lolin-d1-mini-pro-v2-0/

Gruß
Tobias

arduino projekte

Hallo
Ich finde das Sie da ein sehr schönes Projekt aufgebaut haben.
Das mit der WLAN Verbindung zwischen Sender (Sensor) und Empfänger (Display) würde mir bei meinem Problem echt weiterhelfen. Nun zu meiner Frage:
Geht das auch mit 2 Behältern und unter Verwendung von DYP-A02YYWM Ultraschallsensoren und Dallas DS18B20 Temperatursensoren je 1x pro Tank.
Ich habe das schon als Serial-Verbindung zu laufen, leider aber nur in einen Schuppen und nicht direkt in Wohnhaus
(Wegstecke für Kabel nicht machbar).
Derzeit verwende ich dafür noch ein paar Nano V3 mit CH340, diese würde ich dann ersetzen müssen.
Welche Controller würden Sie empfehlen.

Markus

Hinterlassen Sie einen Kommentar

Bitte beachten Sie, dass Kommentare vor der Veröffentlichung genehmigt werden müssen.