Ultraschall-Füllstandsmessung mit Arduino NodeMCU

User stellen ihre Haussteuerung vor

Moderator: Co-Administratoren

DJDieter
Beiträge: 311
Registriert: 11.01.2008, 14:41
System: Alternative CCU (auf Basis OCCU)
Hat sich bedankt: 33 Mal
Danksagung erhalten: 19 Mal

Ultraschall-Füllstandsmessung mit Arduino NodeMCU

Beitrag von DJDieter » 19.02.2019, 18:24

Hallo Zusammen,

ich habe meinen HM-Levelsensor viewtopic.php?f=18&t=38264 auf einen Arduino NodeMCU umgerüstet. Damit bin ich von der Funkreichweite und des DC der CCU unabhängig und benötige lediglich eine WLAN-Verbindung. Desweiteren kann ich genauere Werte erfassen. Falls jemand Interesse an einem Nachbau hat, hier die Anleitung dazu.

Teileübersicht:
  • NodeMCU
  • Ultraschallmodul US-100 (hat bereits Temperaturkompensation onboard)
  • Je nach Bedarf Stecker, Buchsen und Kabel
Für einen gebündelteren Schall (damit reduziere ich Fehlmessungen an der Tankwand) habe ich mir mit einem 3D-Drucker zwei Schallhörner gedruckt.
Der US-100 ist über die serielle Schnittstelle (Rx/Tx) angeschlossen. Damit wird die Messung nicht durch die Kabellänge verfälscht.
Über die IP-Adresse des NodeMCU kann auf die Webseite zugegriffen werden um Werte auszulesen und zu ändern.

Arduino-Code (der Temperaturwert ist rein informativ und kann auch weggelassen werden):

Code: Alles auswählen

#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <RunningMedian.h>

// CONSTANTS:
const int DISTANCE_RANGE_BEGIN = 1635; // Maximale Distanz in mm (Sensor > Tankboden)
const uint32_t MEASUREMENT_PAUSE = 600;

// ******* Netzwerkeinstellungen, bitte anpassen! *******
const char* ssid = "#####"; // SSID des vorhandenen WLANs
const char* password = "#################"; // Passwort für das vorhandene WLAN
IPAddress gateway(***,***,***,***); // IP-Adresse des WLAN-Gateways MIT KOMMA
IPAddress subnet(255,255,255,0); // Subnetzmaske
IPAddress ip(***,***,***,***); // feste IP-Adresse für den NodeMCU MIT KOMMA
const char* host = "***.***.***.***"; // IP-Adresse der CCU (mit Punkten!)
ESP8266WebServer server(80); // Webserver initialisieren auf Port 80

// ******* Sensoreinstellungen *******
unsigned long deltaMessungSekunden = 10; //Zeitintervall (Sekunden) nach dem eine Messung erfolgt

// ******* Einstellungen fuer Meldungen an die CCU, bitte anpassen! *******
String levelCCUname = "CUxD.CUX9000018:1.SET_STATE"; // CUxD-Gerät "Ölstand"
String tempCCUname = "Tanktemperatur"; // Bezeichnung der CCU-Systemvariable für die gemessene Temperatur
unsigned long deltaMeldungSekunden = 600; // Zeitintervall (Sekunden) nach dem eine CCU-Meldung erfolgt (0 bedeutet nie)
int deltaLevel = 1; // Füllhöheänderung bei der eine CCU-Meldung erfolgt (0 bedeutet nie)
int deltaTemp = 1; // Temperaturaenderung (°C) bei der eine CCU-Meldung erfolgt (0 bedeutet nie)

// US-100 ultrasonic rangefinder:
 unsigned int  HByte = 0, LByte = 0;
 int level = 0, levelCCU = 0, temp = 0, tempCCU = 0, junk, US100temp = 0, Average = 0, Distance = 0;
 RunningMedian US100distance = RunningMedian(27);

unsigned long jetztMillis = 0;
unsigned long deltaMessungMillis = deltaMessungSekunden * 1000, letzteMessungMillis = 0;
unsigned long deltaMeldungMillis = deltaMeldungSekunden * 1000, letzteMeldungMillis = 0;
String antwort = "", meldung = "",letzteMeldungCCUzeit = "";

String zeitstempel()// Betriebszeit als Stunde:Minute:Sekunde
{
  char stempel[10];
  int lfdStunden = millis()/3600000;
  int lfdMinuten = millis()/60000-lfdStunden*60;
  int lfdSekunden = millis()/1000-lfdStunden*3600-lfdMinuten*60;
  sprintf (stempel,"%03d:%02d:%02d", lfdStunden, lfdMinuten, lfdSekunden);
  return stempel;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void setup()
{
// WLAN-Verbindung herstellen
  WiFi.config(ip, gateway, subnet); // auskommentieren, falls eine dynamische IP bezogen werden soll
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);

// Verbindungsaufbau abwarten
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
  }

// HTTP-Anfragen bearbeiten
  server.on("/", wurzel_behandlung);
  server.on("/level", melde_level);
  server.on("/zeit", melde_zeit);
  
// HTTP-Server starten
  server.begin();

// Startwerte fuer Zeittrigger
  letzteMessungMillis = millis();
  letzteMeldungMillis = millis();
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void loop()
{    
// auf HTTP-Anfragen warten
  server.handleClient();

  jetztMillis = millis();

// neue Messung falls Zeitintervall erreicht
  if(jetztMillis - letzteMessungMillis > deltaMessungMillis)
  {
    messung();
  }

// neue Meldung an die CCU falls Zeitintervall erreicht
  if(!deltaMeldungMillis == 0 && jetztMillis - letzteMeldungMillis > deltaMeldungMillis)
  {
    melde_CCU();
  }

// neue Meldung an die CCU falls Ölstandänderung den Schwellwert erreicht
  if(!deltaLevel == 0 && abs(level - levelCCU) >= deltaLevel)
  {
    melde_CCU();
  }
  
// neue Meldung an die CCU falls Temperaturaenderung den Schwellwert erreicht
  if(!deltaTemp == 0 && abs(temp - tempCCU) >= deltaTemp) // Neue Meldung wg. Temperaturaenderung
  {
    melde_CCU();
  }
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void messung() // Sensor abfragen
{
  // Serielle Schnittstelle für US-100 öffnen
  Serial.begin(9600);
  delay(100);
  int DataToAvg = 9;
  for (int avgloop = 1; avgloop < (DataToAvg + 1); avgloop++)
  {
   Serial.flush(); // Clear the serial1 buffer. 
   Serial.write(0x55); // Send a "distance measure" command to US-100
   delay(200); // US100 response time depends on distance.
   if (Serial.available() >= 2)  // at least 2 bytes are in buffer
    {
        HByte = Serial.read(); // Read both bytes
        LByte = Serial.read();
        Distance = (HByte * 256 + LByte);
        delay(200);
    }
    US100distance.add(Distance);
  }

  Average = US100distance.getAverage(27);

  level = (DISTANCE_RANGE_BEGIN - Average);
  

// Read temperature from the US-100 ultrasonic rangefinder's temp sensor at the top of the tank. The tank air heats up in the sun.
  Serial.flush(); 
   while (Serial.available() >= 1)  // seemed like flush() was not working so I added this.
   { 
       junk = Serial.read();
   }
    Serial.write(0x50); // send command to request temperature byte.
    delay(50); // temp response takes about 2ms after command ends.
    if (Serial.available() >= 1) 
    {
        US100temp = Serial.read();
        if ((US100temp > 1) && (US100temp < 130)) 
        {
           US100temp -= 45; // Correct by the 45 degree offset of the US100. 
        }
    }
    Serial.end();
  temp = US100temp + korrTemp;
  letzteMessungMillis = jetztMillis;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void melde_CCU()// Werte an die CCU melden
  {
  WiFiClient client; // Webclient initialisieren
  const int httpPort = 8181;
  if (!client.connect(host, httpPort)) // mit dem CCU-Port 8181 verbinden
  {
     delay(100);
    return;
  }
  String url = "/xy.exe?antwort1=dom.GetObject('" + levelCCUname + "').State('" + level + "')"
  "&antwort2=dom.GetObject('" + tempCCUname + "').State('" + temp + "')";
  client.println(String("GET ") + url + " HTTP/1.1\r\n" + "Host: " + host + "\r\n" + "Connection: close\r\n\r\n"); // Daten an CCU melden
  delay(100);
  
  int i = 0;
  while(client.available()) // Antwort der CCU zeilenweise auslesen
    {
    i ++;
    String zeile = client.readStringUntil('\r'); // Zeitstempel der CCU ausfiltern und merken
    if (i == 7)
    {
      letzteMeldungCCUzeit = zeile;
    }
  }
  letzteMeldungMillis = jetztMillis; // gemeldete Daten merken
  levelCCU = level;
  tempCCU = temp;
}   
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void wurzel_behandlung()// bei Aufruf des Root-Verzeichnisses
{
  String betriebszeit = zeitstempel();
  antwort = "WeMos Oelstandsensor\n";
  antwort = antwort + "\tBetriebszeit: " + betriebszeit + " (Std:Min:Sek)\n";
  antwort = antwort + "\tVerbunden mit: " + ssid + "\n";
  int rssi = WiFi.RSSI();
  antwort = antwort + "\tSignalstaerke: " + String(rssi) + " dBm\n\n";
  antwort = antwort + "Letzte Messwerte\n\tFuellhoehe: " + String(level) + " mm\n";
  antwort = antwort + "\tTemperatur: " + String(temp) + " *C\n\n";
  antwort = antwort + "Letzte Datenuebertragung CCU: " + letzteMeldungCCUzeit + "\n";
  antwort = antwort + "\tGemeldete Fuellhoehe: " + String(levelCCU) + " mm\n";
  antwort = antwort + "\tGemeldete Temperatur: " + String(tempCCU) + " *C\n\n";
  antwort = antwort + "Ausloeser fuer Datenuebertragung CCU\n";
  antwort = antwort + "\tZeitintervall: " + String(deltaMeldungSekunden) + " Sekunden\n";
  antwort = antwort + "\tFuellhoehedifferenzwert: " + String(deltaLevel) + " mm\n";
  antwort = antwort + "\tTemperaturdifferenzwert: " + String(deltaTemp) + " *C\n\n";
  antwort = antwort + "HTTP-Befehlsuebersicht:\n";
  antwort = antwort + "\"192.168.178.72/level\"           gibt die zuletzt gemessenen Fuellhoehe aus (mm)\n";
  antwort = antwort + "\"192.168.178.72/temp\"            gibt die zuletzt gemessene Temperatur aus (Grad Celsius)\n";
  antwort = antwort + "\"192.168.178.72/zeit\"            gibt den CCU-Zeitstempel aus, an dem die letzte Meldung an die CCU erfolgte\n";
  antwort = antwort + "\"192.168.178.72/temp?delta=\"     setzt den Temperatur-Differenzwert in *C, der eine CCU-Meldung ausloest\n";
  antwort = antwort + "\"192.168.178.72/level?delta=\"    setzt den Fuellhoehe-Differenzwert in mm, der eine CCU-Meldung ausloest\n";
  antwort = antwort + "\"192.168.178.72/zeit?delta=\"     setzt die Zeitspanne (in Sekunden), nach der spaetestens eine CCU-Meldung erfolgt\n";
  antwort = antwort + "\"192.168.178.72/zeit?mess=\"      definiert das Messintervall (in Sekunden)\n";
  server.send(300, "text/plain", antwort);
  delay(150);
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void melde_level() // bei Aufruf von ".../level"
{
  String delta = server.arg("delta");
  String korr = server.arg("korr");
  if (delta != "")
  {
    deltaLevel = delta.toFloat();
    server.send(200, "text/plain", "Fuellhoehedifferenzwert fuer CCU-Meldungen auf " + delta + " mm gesetzt.");
    delay(100);
  }
  else
  {
    server.send(200, "text/plain", String(level));
    delay(100);
  }
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void melde_temp() // bei Aufruf von ".../temp"
{
  String delta = server.arg("delta");
  String korr = server.arg("korr");
  if (delta != "")
  {
    deltaTemp = delta.toFloat();
    server.send(200, "text/plain", "Temperaturdifferenzwert fuer CCU-Meldungen auf " + delta + " *C gesetzt.");
    delay(100);
  }
  else
  {
    server.send(200, "text/plain", String(temp));
    delay(100);
  }
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void melde_zeit()// bei Aufruf von ".../zeit"
  {
  String delta = server.arg("delta");
  String mess = server.arg("mess");
  if (delta != "")
  {
    deltaMeldungSekunden = delta.toInt();
    deltaMeldungMillis = deltaMeldungSekunden * 1000;
    server.send(200, "text/plain", "Zeitintervall fuer CCU-Meldungen auf " + delta + " Sekunden gesetzt.");
    delay(100);
  }
  else if (mess != "")
  {
    deltaMessungMillis = mess.toInt() * 1000;
    server.send(200, "text/plain", "Zeitintervall fuer Sensorabfrage auf " + mess + " Sekunden gesetzt.");
    delay(100);
  }
  else
  {
    server.send(200, "text/plain", "Letzte Aktualisierung der CCU\n" + letzteMeldungCCUzeit);
    delay(100);
  }
}
Die gemessene Entfernung sende ich an ein CUxD Universal-Wrapper-Device (90), das mir mit den Werten der Peiltabelle meines Öltanks dann den entsprechenden Füllstand anzeigt.

Hier noch ein Bild des fertigen Gerätes:
IMG_1414.JPG
Raspberry PI 4 mit RaspberryMatic, 4 LAN-Gateways, zwei HmIP-HAP und 248 Geräte
CUxD mit 357 Kanälen auf 64 Geräten
Zusatzsoftware: XML-API, CUxD-Highcharts, NEO-Server, Programmedrucken, CUxD, E-Mail, Philips Hue, Messenger, CCU-Historian, JB-HP-Devices, HomeKit HomeMatic
Anbindungen: Wolf eBus; NodeMCU-Ultraschall-Füllstandsmessung mit Temperatureinfluß; Fußbodenheizung mit Rücklauftemperaturbegrenzer (RTL)

Mathias
Beiträge: 1780
Registriert: 03.11.2010, 10:25
System: CCU
Wohnort: Aachen
Hat sich bedankt: 58 Mal
Danksagung erhalten: 258 Mal
Kontaktdaten:

Re: Ultraschall-Füllstandsmessung mit Arduino NodeMCU

Beitrag von Mathias » 19.02.2019, 19:58

Tolle Erweiterung.

Gruß
Mathias

PS: Ich brauche auch einen 3D-Drucker!

shooter
Beiträge: 81
Registriert: 04.09.2018, 18:41
Hat sich bedankt: 19 Mal
Danksagung erhalten: 3 Mal

Re: Ultraschall-Füllstandsmessung mit Arduino NodeMCU

Beitrag von shooter » 24.04.2019, 12:05

Tolles Projekt!
Notgedrungen (da meine Ölheizung leider für jeden verfügbaren (bezahlbaren) Durchflusssensor zu wenig Öl verbraucht) möchte ich das jetzt für eine Tankanlage mit 5 Öltanks umsetzen.

Du hast nicht zufällig vor die Druckdatei (*.stl) zu veröffentlichen?

Viele Grüße,

Florian

DJDieter
Beiträge: 311
Registriert: 11.01.2008, 14:41
System: Alternative CCU (auf Basis OCCU)
Hat sich bedankt: 33 Mal
Danksagung erhalten: 19 Mal

Re: Ultraschall-Füllstandsmessung mit Arduino NodeMCU

Beitrag von DJDieter » 24.04.2019, 12:19

Aber gerne. In der zip-Datei sind zwei Versionen des Schallhorns.
Schallhorn.zip
(451.33 KiB) 203-mal heruntergeladen
Raspberry PI 4 mit RaspberryMatic, 4 LAN-Gateways, zwei HmIP-HAP und 248 Geräte
CUxD mit 357 Kanälen auf 64 Geräten
Zusatzsoftware: XML-API, CUxD-Highcharts, NEO-Server, Programmedrucken, CUxD, E-Mail, Philips Hue, Messenger, CCU-Historian, JB-HP-Devices, HomeKit HomeMatic
Anbindungen: Wolf eBus; NodeMCU-Ultraschall-Füllstandsmessung mit Temperatureinfluß; Fußbodenheizung mit Rücklauftemperaturbegrenzer (RTL)

DJDieter
Beiträge: 311
Registriert: 11.01.2008, 14:41
System: Alternative CCU (auf Basis OCCU)
Hat sich bedankt: 33 Mal
Danksagung erhalten: 19 Mal

Re: Ultraschall-Füllstandsmessung mit Arduino NodeMCU

Beitrag von DJDieter » 24.04.2019, 12:26

Und hier noch der Deckel:
Oeltankdeckel.zip
(26.88 KiB) 177-mal heruntergeladen
Raspberry PI 4 mit RaspberryMatic, 4 LAN-Gateways, zwei HmIP-HAP und 248 Geräte
CUxD mit 357 Kanälen auf 64 Geräten
Zusatzsoftware: XML-API, CUxD-Highcharts, NEO-Server, Programmedrucken, CUxD, E-Mail, Philips Hue, Messenger, CCU-Historian, JB-HP-Devices, HomeKit HomeMatic
Anbindungen: Wolf eBus; NodeMCU-Ultraschall-Füllstandsmessung mit Temperatureinfluß; Fußbodenheizung mit Rücklauftemperaturbegrenzer (RTL)

shooter
Beiträge: 81
Registriert: 04.09.2018, 18:41
Hat sich bedankt: 19 Mal
Danksagung erhalten: 3 Mal

Re: Ultraschall-Füllstandsmessung mit Arduino NodeMCU

Beitrag von shooter » 24.04.2019, 15:10

Perfekt, vielen Dank!

Einen Verdrahtungsplan hast Du nicht zufällig?
Ich versuche das gerade auf einen anderen Sensor (liegt hier noch, ein Ultrasonic Ranger V2.0) umzusetzen.
Leider ist die Beschriftung anders als beim US-100...bin nichtmal sicher ob der mit RX und TX läuft...

Neben VCC und GND hat der noch NC und SIG....

Der Plan ist, wenn es denn läuft, alle 5 Tanks mit einem Sensor auszustatten und diese dann auf einem esp zusammenlaufen zu lassen...

Gruß,

Flo

DJDieter
Beiträge: 311
Registriert: 11.01.2008, 14:41
System: Alternative CCU (auf Basis OCCU)
Hat sich bedankt: 33 Mal
Danksagung erhalten: 19 Mal

Re: Ultraschall-Füllstandsmessung mit Arduino NodeMCU

Beitrag von DJDieter » 24.04.2019, 16:04

Da kann ich dir leider nicht weiterhelfen, da ich den Sensor nicht kenne. Falls er nicht über eine serielle Schnittstelle kommuniziert, mußt du einen Großteil des Codes umschreiben, da die meisten US-Sensoren nur die Laufzeit des Schalls zurücksenden.
Raspberry PI 4 mit RaspberryMatic, 4 LAN-Gateways, zwei HmIP-HAP und 248 Geräte
CUxD mit 357 Kanälen auf 64 Geräten
Zusatzsoftware: XML-API, CUxD-Highcharts, NEO-Server, Programmedrucken, CUxD, E-Mail, Philips Hue, Messenger, CCU-Historian, JB-HP-Devices, HomeKit HomeMatic
Anbindungen: Wolf eBus; NodeMCU-Ultraschall-Füllstandsmessung mit Temperatureinfluß; Fußbodenheizung mit Rücklauftemperaturbegrenzer (RTL)

dondaik
Beiträge: 12925
Registriert: 16.01.2009, 18:48
Wohnort: Steingaden
Hat sich bedankt: 1599 Mal
Danksagung erhalten: 222 Mal

Re: Ultraschall-Füllstandsmessung mit Arduino NodeMCU

Beitrag von dondaik » 24.04.2019, 18:49

schon mal tante G zu dem sensor befragt ? oder youtube :mrgreen:
so zwei drei antworten kommen da und sogar eine eigen lib hat der sensor .... ggf auch schwachstellen ( habe aber nicht weitergelesen )
-------
!!! der download der handbüchern auf den seiten von eq3 und das lesen der tips und tricks kann das hm-leben sehr erleichtern - das nutzen der suche nach schlagworten ebenso :mrgreen: !!!
wer schreibfehler findet darf sie behalten.

shooter
Beiträge: 81
Registriert: 04.09.2018, 18:41
Hat sich bedankt: 19 Mal
Danksagung erhalten: 3 Mal

Re: Ultraschall-Füllstandsmessung mit Arduino NodeMCU

Beitrag von shooter » 25.04.2019, 11:35

Moin zusammen,

der Testaufbau läuft NC stand in dem Fall wirklich für "not connected", der Sensor hat also einen Digitalausgang.... :shock:

Aber wie gesagt, der Testaufbau läuft soweit (der Sensor ist aber für eine ernsthafte Messung unbrauchbar), der Code ist umgeschrieben und heute Abend geht der Kram auf den 3D-Drucker und wird dann testweise eingebaut.

Die richtigen Sensoren sind bereits auf dem Weg zu mir (allerdings aus China...wird also dauern).

Da ich mehrere Sensoren Messen lassen möchten entfällt auch die Serielle Messung via RX TX. Es wird über Trigger und Echo laufen, ich hoffe die Auswertung übernimmt die Blbliothek...sonst muss ich selber ran...

Gruß,

Flo

shooter
Beiträge: 81
Registriert: 04.09.2018, 18:41
Hat sich bedankt: 19 Mal
Danksagung erhalten: 3 Mal

Re: Ultraschall-Füllstandsmessung mit Arduino NodeMCU

Beitrag von shooter » 15.05.2019, 14:02

Hier mal ein kleines Statusupdate:

Der Sensor läuft und liefert auch Daten (Teile sind alle angekommen und verbaut) Die Tankverschraubungen etc sind auch fertig gedruckt.

Ich könnte nochmal Hilfe beim CUxD brauchen:
Ein Universal Wrapper Device (90) hab ich angelegt, die Adresse im Script angepasst. Doch wie bekomme ich da jetzt die Daten rein, irgendwie steige ich da nicht durch.

Die Tanktemperatur wir sauber in der angelegten SysVar abgelegt.

Gruß,

Flo

PS: Die Messung aller 5 Tanks werde ich wohl mit SoftwareSerial realisieren, dazu baue ich dann aber einen neuen Prototypen.

Antworten

Zurück zu „Projektvorstellungen“