ESP8266 und Füllstand des Öltanks

Begonnen von peter_w, 08 Mai 2016, 17:53:06

Vorheriges Thema - Nächstes Thema

peter_w

Hallo zusammen,

ich möchte gerne mein Projekt mit dem ESP8266 vorstellen.
Ich habe vor einiger Zeit den Inhalt meines Heizöltanks wie hier beschrieben: http://www.forum-raspberrypi.de/Thread-haussteuerung-heizoel-tankstand-oder-verbrauchs-fernablesung-mit-raspi-geloest-beitrag-21?pid=115200#pid115200 überwacht. Der dafür eingesetzte Raspi beklagte sich fast täglich wegen Unterforderung.
Also habe ich auf den ESP8266 umgestellt.
Für die Entwicklung habe ich mir eine NodeMCU besorgt https://en.wikipedia.org/wiki/NodeMCU mit dem integrierten USB Adapter ist schon mal eine Hürde genommen.
Für den produktiven Einsatz habe ich mir das ESP-201 besorgt, da er gut einlötbar ist und einige IOs mehr hat als der originale ESP8266.
Die Hardware schaut also jetzt so aus:
https://www.dropbox.com/s/4mjcll78ntur80n/oil.png?dl=0


  • ein Temperatursensor : DHT22
  • ein Ultraschallmodul:  DYP-ME007Y
  • ein Spannungswandler 5V auf 3,3V
  • das Modul ESP-201

Dank der NodeMCU habe ich die Software  in LUA geschrieben.
Idee:  Über die Temperaturmessung mit dem DHT22 wird die Ultraschallmessung genauer, da die Geschwindigkeit von Ultraschall u.A. von der Lufttemperatur abhängt. Na ja und damit es dem ESP-201 nicht ganz so langweilig ist die Höhe zu messen  :)

Ach ja, und es gibt eine kleine Internetseite über die man ein paar Daten auslesen kann, ist auch nur zum Testen gedacht, die Daten landen ja letztlich in FHEM.

Die Software die seit einigen Tagen läuft schaut so aus:
----------------------------------------------------------------
-- Measure the distance to the oil surface in an oil tank
-- by a ultrasonic sensor.
-- A median value is taken from several measurements.
-- The oil volume in the tank is calculated.
-- The calculated volume is transferred to FHEM
----------------------------------------------------------------

-- define GPIO pins
   -- for the ultrasonic sensor
GpioTrigger = 1 -- GPIO5
GpioEcho    = 7 -- GPI13     
   -- for the temperature sensor
GpioDht22   = 2 -- GPIO4

-- Timer for cyclic measurement
MeasureTimerId  = 0         -- id of a timer, possible values 0..6
MeasureTime     = 20000     -- time in us after which a new measure cycle shall be started

-- data of the oil tank
tankheight  = 150.0     -- in cm
tankvolume  = 10900.0   -- in l
offset      = 2         -- offset of the sensor in cm

-- data for distance measurement
DefaultTemperature = 20     -- temperature which is used for distance calculation if temperature sensor failed
SelfHeating        = 0.3    -- depending on the board and the place where the sensor is mounted there is a self-heating effect which have to be compensated

-- configure the median calculation
MedianArrayValues   = 47    -- shall be an odd number
MedianPosition      = 23     -- the middle position of the array  for 3 = 2, for 11 = 6, 23 = 11, 47 = 23 ....   

-- Data for oil measurement
Distance = NIL          -- distance between sensor and oil surface in cm, raw value
DistanceMedian = NIL    -- median of the distance between sensor and oil surface in cm
OilLevel = NIL          -- distance between tank bottom and oil surface
liter = 0               -- oil in the tank in liter
measureCounter = 0      -- just a counter incremented with each measueremnt, can be used to see if system is still alive and didn't reboot


-- Globale variables internal used
StartTime = NIL         -- time where measurement has started
StopTime = NIL          -- time where measurement has stopped
DistanceTable = {}      -- table with raw distances used to generate median
SonicSpeed = 343.2      -- default value for sonic speed, will be calculated later by temperature
temp       = 21
humi       = 50

-- WiFi connection
WiFiSSID    = "SSID" -- Please enter your SSID
WiFiPwd     = "PWD" -- Please enter your WiFi Password

-- FHEM
FhemIp                  = "192.168.0.210"   -- IP address of FHEM server
FhemPort                = 8083              -- Port of FHEM server you find it in your browser "http://192.168.0.210:8083/fhem"
FhemDevice              = "oil"   -- the device where the data shall be stored
FhemReadingLiter        = "liter"       -- the reading of the device to store the data
FhemReadingEntfernung   = "entfernung"
FhemReadingfuellhoehe   = "fuellhoehe"
FhemReadingTemperatur   = "temperature"
FhemReadingHumidity     = "humidity"
                                        -- %%20 is the escaped version of %20 because this string will be used with string.format
                                        -- &XHR=1 is used not to be redirected to the FHEM web page -> the answer from the web server is in this case much smaller, only "HTTP/1.1 200 OK" without any content
FhemDataSendStr         = "GET /fhem?cmd=setreading%%20%s%%20%s%%20%s&XHR=1 HTTP/1.1\r\nHost: %s:%s\r\nConnection: keep-alive\r\nAccept: */*\r\n\r\n')"

SendLinesTable           = {}              -- here will be the strings stored which are used to transfer data to FHEM
SendLineNr                 = 1             -- used to track the number of lines

-- WEB site
AutoRefresh = MeasureTime/1000      -- automatic refresh of the web side in seconds

---------------------------
-- Calculate the amount of oil in the tank by the  measured distance to the oil level in cm.
-- Returns liter or NIL if an invalid distance is measured
---------------------------
function DistanceToLiter(distance)
  OilLevel = tankheight - distance - offset         -- from the bottom of the tank to the oil surface
  if OilLevel > 0 and OilLevel < tankheight then    -- check for valid level
    liter = tankvolume / tankheight * OilLevel      -- what we have in the tank in liter
    return string.format("%.d",liter) 
  else
    return NIL                                      -- invalid value
  end 
end

----------------------------
-- calculate days, hours minutes and seconds from a counter "Counter" which is incremented every "cycletime" seconds
-- returns a table with the elements
----------------------------
function CounterToUpTime(Counter,cycletime)
  ret = {}
  Counter = Counter * cycletime -- now the counter is in seconds
  ret["days"] = string.format("%.d",Counter / 60 / 60 /24) 
  Counter = Counter - ret["days"]*24*60*60
  ret["hours"] = string.format("%.d",Counter / 60 / 60)
  Counter = Counter - ret["hours"]*60*60
  ret["minutes"] = string.format("%.d",Counter / 60)
  Counter = Counter - ret["minutes"]*60
  ret["seconds"] = string.format("%.d",Counter)
  return ret
end

----------------------------
-- Start WEB server
---------------------------
function StartWebserver()
    print("Prepare Webserver")
    srv=net.createServer(net.TCP)       -- create the server
    srv:listen(80, function(conn)       -- start the server
    conn:on("receive",                  -- this event is triggered if the server receive a request
        function(client,request)        -- just a simple function which answers, it doesn't matter what was requested   
            val = CounterToUpTime(measureCounter,MeasureTime/1000) -- calculate days, hours, minutes and seconds from the measure counter
            UpTimeStr = string.format("%.d days %.d hours %.d minutes %.d seconds ",val["days"],val["hours"],val["minutes"],val["seconds"])
            local line = ""             -- sending ich line separate with client:send leads to a crash, so I build one string           
            line = line..'HTTP/1.1 200 OK\n\n'
            line = line..'<!DOCTYPE HTML>\n'           
            line = line..'<html>\n'
            line = line..'<head><meta  content="text/html; charset=utf-8>"\n'
            line = line..'<title>ESP8266 Ultrasonic</title>\n'
            line = line..'<meta http-equiv="refresh" content="'..AutoRefresh..'" />\n'  -- automatic refresh every x seconds
            line = line..'</head>\n<body>'
            line = line.."<h1> Ultrasonic Web Server</h1>"
            line = line.."<table>"
            line = line.."<tr><th>Name</th><th>value</th></tr>"                 -- table with data
            line = line.."<tr><td>distance</td><td>"..Distance.."</td></tr>"
            line = line.."<tr><td>Oil</td><td>"..liter.."</td></tr>"
            line = line.."<tr><td>Temperature</td><td>"..temp.."</td></tr>"
            line = line.."<tr><td>Humidity</td><td>"..humi.."</td></tr>"
            line = line.."<tr><td>Up time</td><td>"..UpTimeStr.."</td></tr>"
            line = line.."</table>"
            line = line..'</body></html>\n'
            client:send(line)
        end)
   
    conn:on("sent",                     -- this event is triggered if data is sent
        function(conn)
            conn:close()                -- as all data out, connection can be closed now
            -- collectgarbage()
        end)
    end)
    wifi.sta.eventMonStop(unregister_all)   -- unregister the event which starts the web server because the server is up now
end

----------------------------
-- Start measurement
---------------------------
function TriggerMeasurement()
  -- to start measurement, set trigger for 10us to 0 
  StartTime = tmr.now()  -- set start timer here, to be sure that it is pre set
  --gpio.write(GpioTrigger,gpio.LOW) 
  -- StartTime = tmr.now()
  --gpio.write(GpioTrigger,gpio.HIGH)
  gpio.serout(GpioTrigger,0,{10,10})
  StartTime = tmr.now()      -- the best value of the start timer   
end

---------------------------
-- Fill a table with all lines which shall be send to FHEM
---------------------------
function PrepareDataToSend()
    SendLinesTable = {} -- empty table
    SendLineNr = 1      -- line number which shall be sent next. Init to 1 because LUA start arrays with index 1       
   
    table.insert(SendLinesTable,string.format(FhemDataSendStr,FhemDevice,FhemReadingLiter,liter,FhemIp,FhemPort))
    table.insert(SendLinesTable,string.format(FhemDataSendStr,FhemDevice,FhemReadingEntfernung,DistanceMedian,FhemIp,FhemPort))
    table.insert(SendLinesTable,string.format(FhemDataSendStr,FhemDevice,FhemReadingfuellhoehe,OilLevel,FhemIp,FhemPort))
    table.insert(SendLinesTable,string.format(FhemDataSendStr,FhemDevice,FhemReadingTemperatur,temp,FhemIp,FhemPort))
    table.insert(SendLinesTable,string.format(FhemDataSendStr,FhemDevice,FhemReadingHumidity,humi,FhemIp,FhemPort))     
end

---------------------------
-- Send data to FHEM
---------------------------
function SendData()
    -- prepare connect to FHEM server by generating a socket
    sk=net.createConnection(net.TCP, 0)
    -- Receive event which will be fired if the answer is received
    sk:on("receive",
        function(conn, payload)
            -- print("receive :",payload)
            -- now we can send the next line or close the connection
            if SendLineNr <= table.getn(SendLinesTable) then
                sk:send(SendLinesTable[SendLineNr])     -- event driven send the next line
                SendLineNr = SendLineNr + 1
            else
                sk:close()  -- all lines have been sent, we can close the connection
            end
        end) 
    -- Connect event, will be fired if the connection has been established   
    sk:on("connection",
        function(conn, payload)
            -- print("connect :",payload)
            -- prepare data for transfer                     
            -- print("Send: ",SendLinesTable[SendLineNr])
            -- as we are connected, we now can send the data
            sk:send(SendLinesTable[SendLineNr])
            SendLineNr = 2      -- first line has been sent, this will be the next to send
        end)
    -- connect to FHEM server     
    sk:connect(FhemPort,FhemIp)           
end

--------------------------
-- Event which will be called by the falling edge of the echo GPIO
-- Here the complete calculation of the oil volumn is done
--------------------------
function EchoPinEdge()
  StopTime = tmr.now()  -- current time in us
  print("Heap: ",node.heap())
  print("Counter",measureCounter)
  measureCounter = measureCounter + 1
  status, temp, humi, temp_dec, humi_dec = dht.readxx(GpioDht22)
  if status == dht.OK then
    temp = temp - SelfHeating            -- correct the self-heating effect
    print("DHT22: ",status,temp,humi)
    SonicSpeed = 331.5 + (0.6 * temp)   -- calculation of sonic speed in air by temperature
  else
    print( "DHT error." )
    SonicSpeed = 331.5 + (0.6 * DefaultTemperature)   -- without temperature sensor we use a default value
  end 
  if StopTime > StartTime then -- check that we get a good time value
    -- calculate the distance from the time the echo is needed
    -- at 20°C the velocity of sound is round about 343,2 m/s, depending on the temperature
    -- we have to divide by two because we measure the double way
    Distance = ((StopTime - StartTime) /1000 / 1000 * SonicSpeed) / 2 * 100 -- distance in cm
    print("Entfernung: ",Distance)
    -- insert into a table to generate the median
    table.insert(DistanceTable,Distance)   
    -- check if enough values collected
    if table.getn(DistanceTable) == MedianArrayValues then
        -- sort the table
        table.sort(DistanceTable)
        -- the median value is the one in the middle
        DistanceMedian = DistanceTable[MedianPosition]
        DistanceTable = {} -- delete table elements
        -- calculate the amount of oil in the tank
        liter = DistanceToLiter(DistanceMedian)
        if liter ~= nil then  -- NIL is one possible value from the calculation function
            print ("Enfernung median , liter",DistanceMedian,liter)
            PrepareDataToSend()
            SendData()           
        end   
    end 
  end
end

--------------------------
--------------------------

print ("Start Script")
print ("Boot reason",node.bootreason())

-- prepare GPIO
gpio.mode(GpioTrigger,gpio.OUTPUT)  -- trigger pin will start a measueremnt cycle
gpio.mode(GpioEcho,gpio.INPUT)      -- echo pin receives the run time of the echo

-- prepare event for rising edge on the echo pin
gpio.trig(GpioEcho, 'both',
    function(level)
        if level == 1 then
            StartTime = tmr.now()      -- rising edge = start measure the time 
        else
            EchoPinEdge()              -- stop measure the time
        end
    end
    )    -- function EchoPinEdge will be called to do the calculation


StartTime = tmr.now()   -- pre-set the start timer
TriggerMeasurement()    -- start the first measurement

-- start measurement timer for cyclic measurement
tresult = tmr.alarm(MeasureTimerId,MeasureTime,tmr.ALARM_AUTO,TriggerMeasurement)

print("Measurement timer started = ",tresult)

--- connect to WIFI

wifi.setmode(wifi.STATION)          -- mode = connect to a router
wifi.sta.config(WiFiSSID,WiFiPwd)   -- set configuration for wifi
wifi.sta.autoconnect(1)             -- connect to wifi
print("IP_address ",wifi.sta.getip())     

--- start web server if IP address available 
wifi.sta.eventMonReg(wifi.STA_GOTIP,StartWebserver) -- register function StartWebserver which will be called if we get an ip address
wifi.sta.eventMonStart()                            -- activate registered monitor

-- from here on, all functions are event based
-- MeasureTime is triggered cyclic and start a new measurement

Damit füttere ich ein FHEM dummy device mit den anfallenden Daten:
https://www.dropbox.com/s/nu849tcatrinul2/DummyDevice.jpg?dl=0
Und bekomme Grafiken wie die:
https://www.dropbox.com/s/l9gaff1rh2ic9vs/VolHeizoel.jpg?dl=0
Ich hoffe die Kommentare in der Software helfen beim Verständnis, ist ja kein Hexenwerk.  :)
Release  : 5.8
Raspberry Pi 3
CUL V 1.63 CSM868 HomeMatic (SCC)
HM-CC-RT-DN,HM-LC-Bl1PBU-FM,HM-LC-Sw1PBU-FM,HM-SEC-SCo,HM-WDS10-TH-O

Maergsche

Hallo, Genau an das selbe Projet wollte ich mich auch ran machen ...


Welches NoteMCU Image hast du genommen (hast du einen Link) ?
Aktuell bekomme ich die Fehlermeldung "not enough memory"!?!?

peter_w

Ich verwende folgendes Teil:

http://www.amazon.de/ESP8266-ESP-201-Remote-Serial-Transceiver-Wireless/dp/B0182CJF26

was auch noch helfen könnte, ich habe mir die Firmware genau auf meine Bedürfnisse hin erzeugen lassen, das geht hier:
http://nodemcu-build.com/

Enthalten sind bei mir die Module: dht,file,gpio,net,tmr,uart und wifi

Da war eigentlich reichlich Platz.

Release  : 5.8
Raspberry Pi 3
CUL V 1.63 CSM868 HomeMatic (SCC)
HM-CC-RT-DN,HM-LC-Bl1PBU-FM,HM-LC-Sw1PBU-FM,HM-SEC-SCo,HM-WDS10-TH-O

Maergsche

#3
Hallo Peter,

Danke dir! Das war schon mal die erste Hürde ...

Nun bekomme ich noch folgende Meldung beim Starten des Skriptes:

Start Script
Boot reason 2
Measurement timer started = true
IP_address 192.168.161.68 255.255.255.0 192.168.161.1
init.lua:216: bad argument #2 to 'eventMonReg' (string expected, got nil)
stack traceback:
[C]: in function 'eventMonReg'
init.lua:216: in main chunk
[C]: in function 'dofile'
stdin:1: in main chunk
>



peter_w

Versuch bitte mal meine original Datei nur mit Änderung von:

WiFiSSID
WiFiPwd

Ansonsten benötige ich natürlich den Code wo der Fehler angezeigt wird, also Zeile 216.

Ich tippe mal das ist:

wifi.sta.eventMonReg(wifi.STA_GOTIP,StartWebserver)

StartWebserver ist eine Funktion die aufgerufen wird und bei Dir scheint da die Funktion zu fehlen.



Release  : 5.8
Raspberry Pi 3
CUL V 1.63 CSM868 HomeMatic (SCC)
HM-CC-RT-DN,HM-LC-Bl1PBU-FM,HM-LC-Sw1PBU-FM,HM-SEC-SCo,HM-WDS10-TH-O

Maergsche

Hallo Peter,

Du hattest natürlich recht...  Schande auf mein Haupt ;-)
Ich hatte wegen des Memory Problemes etwas auskommentiert und sogar Teile des Codes entfernt. Letzteres hatte ich beim einkommentieren vergessen hinzuzufügen! 

Das Script läuft bei mir nun soweit!  Ein Probleme habe momentan aber noch. Nach 10 bis 20 Messungen startet mein Esp8266 einfach neu. Ich bin gerade dabei herauszufinden an welcher Stelle dies stattfindet, indem ich mir an diversen möglichen stellen Ausgaben auf die Konsole schreibe.

Ich nutze übrigens ein WeMos D1 mini V2,  als Ultraschall Sensor einen HC SR04 und für die Temp den DHT22.

Gruß und Dank
Marc


peter_w

Hi Marc,
bei mir startet es alle paar Stunden neu, damit kann ich leben weil die Funktion nicht beeinträchtigt ist.
Wenn du was findest habe ich aber trotzdem großes Interesse.
Ich habe das Script in compilierter Form abgelegt, das schafft noch etwas mehr Platz.
Peter
Release  : 5.8
Raspberry Pi 3
CUL V 1.63 CSM868 HomeMatic (SCC)
HM-CC-RT-DN,HM-LC-Bl1PBU-FM,HM-LC-Sw1PBU-FM,HM-SEC-SCo,HM-WDS10-TH-O

Maergsche

Hallo Peter,

das Script läuft inzwischen seit einer Stunde stabil... ich habe den HC-SR04 (der zuvor am Kabel hing) nun auf mein Board gesteckt und noch einem "Abblockkondernsator" verpasst. Sprich die Kabellängen reduziert und einen 1 μF Kondernsator zwischen VCC und Ground des HC-SR04 gesetzt.

Am Code hatte ich dann, ggf aus diesem Grund, die Zeile  "measureCounter = measureCounter + 1" ans Ende der Funktion verlagern und um "table.insert(DistanceTable,Distance)" noch ein "if" bauen müssen, da bei mir immer die erste Messung nach dem Reset stark abwich:

 
if measureCounter > 0 then
    table.insert(DistanceTable,Distance)   
end 


Die Daten kommen nun auch schon im Fhem an ;-)

Kannst du mir ggf. die Definiton vom Filelog und Plot aus deinem Fhem zur Verfügung stellen? Dann kann ich mir schon mal Gedanken mach wie ich das ganze am kompaktesten und am sinnvollsten zusammenlöten kann!

An dieser Stelle möchte ich schon mal vielen Dank, für die Bereitstellung deines Projektes (inkl. Code) und deine Mithilfe, übermitteln!

Gruß Marc



peter_w

Hi Marc,

freut mich wenn es Jemand brauchen kann.

Ich habe das nach ein paar tagen Probe Betrieb bei dem ich den Abstand zur Zimmerdecke vermessen habe, auf eine Lochrasterplatine gebracht.

Die erste falsche Messung ist mir auch aufgefallen, die fliegt aber dank Median eh raus.

Nach der Umstellung vom Raspi (damit habe ich das schon ein paar monate betrieben) auf den ESP sind die Werte auch viel stabiler geworden.

Folgendes ist mir bisher noch aufgefallen.


  • Wenn man das Webinterface länger verwendet, dann geht die Eigenerwärmung des ESP und damit auch des DHT22 hoch wodurch die Messung natürlich verfälscht wird.
  • Ab und zu ging Nachts für ca eine Stunde die Temperatur um ein Grad hoch. Das sit jetzt aber schon seit ein paar Tagen nicht passiert.

Mal gespannt wie deine Langzeiterfahrung sind.

Was das Filelog angeht: Ich verwende seit einiger Zeit DbLog. Davor habe ich die LOG Datei direkt vom Öl-Mess Raspi direkt auf den FHEM Raspi geschoben und im FHEM Raspi ein fakelog angelegt.
Somit kann ich dir leider nicht helfen.

Gruß
   Peter
Release  : 5.8
Raspberry Pi 3
CUL V 1.63 CSM868 HomeMatic (SCC)
HM-CC-RT-DN,HM-LC-Bl1PBU-FM,HM-LC-Sw1PBU-FM,HM-SEC-SCo,HM-WDS10-TH-O

Maergsche

#9
Hallo,

mein HC SR-04 scheint mit dem "gpio.serout()" nicht klargekommen zu sein .... mit den von mir gesetzten "print's" funktionierte es (da war dann der gemessende Abstand nicht korrekt), nachdem ich die "print's" wieder entfernt  hatte stürzte der ESP8266 dann immer wieder ab....

Also habe ich den Trigger dahingehend geändert, dass über die Funktion "gpio.write()" der TriggerPin hochgesetzt wird. Die GPIO's GpioTrigger und  GpioEcho werden nun über die Funktion "gpio.trig()" getriggert und die Variablen Starttime und Stoptime gesetzt.

Anbei mein Code:

----------------------------------------------------------------
-- Measure the distance to the oil surface in an oil tank
-- by a ultrasonic sensor.
-- A median value is taken from several measurements.
-- The oil volume in the tank is calculated.
-- The calculated volume is transferred to FHEM
----------------------------------------------------------------

-- define GPIO pins
-- for the ultrasonic sensor
GpioTrigger = 1 -- GPIO5
GpioEcho    = 7 -- GPI13     
   -- for the temperature sensor
GpioDht22   = 2 -- GPIO4

-- Timer for cyclic measurement
MeasureTimerId  = 0         -- id of a timer, possible values 0..6
MeasureTime     = 10000     -- time in us after which a new measure cycle shall be started

-- data of the oil tank
tankheight  = 167.0     -- in cm
tankvolume  = 4500.0   -- in l
offset      = 10         -- offset of the sensor in cm

-- data for distance measurement
DefaultTemperature = 20     -- temperature which is used for distance calculation if temperature sensor failed
SelfHeating        = 0.3    -- depending on the board and the place where the sensor is mounted there is a self-heating effect which have to be compensated

-- configure the median calculation
MedianArrayValues   = 47    -- shall be an odd number
MedianPosition      = 23    -- the middle position of the array  for 3 = 2, for 11 = 6, 23 = 11, 47 = 23 ....   

-- Data for oil measurement
Distance = NIL          -- distance between sensor and oil surface in cm, raw value
DistanceMedian = NIL    -- median of the distance between sensor and oil surface in cm
OilLevel = NIL          -- distance between tank bottom and oil surface
liter = 0               -- oil in the tank in liter
measureCounter = 0      -- just a counter incremented with each measueremnt, can be used to see if system is still alive and didn't reboot


-- Globale variables internal used
StartTime = 0           -- time where measurement has started
StopTime = 0            -- time where measurement has stopped
DistanceTable = {}      -- table with raw distances used to generate median
SonicSpeed = 343.2      -- default value for sonic speed, will be calculated later by temperature
temp       = 21
humi       = 50

-- WiFi connection
WiFiSSID    = "YourSSID"        -- Please enter your SSID
WiFiPwd     = "YourPassword"     -- Please enter your WiFi Password

-- FHEM
FhemIp                  = "192.168.161.25"   -- IP address of FHEM server
FhemPort                = 8083              -- Port of FHEM server you find it in your browser "http://192.168.0.210:8083/fhem"
FhemDevice              = "Oeltank"   -- the device where the data shall be stored
FhemReadingLiter        = "Liter"       -- the reading of the device to store the data
FhemReadingEntfernung   = "Entfernung"
FhemReadingfuellhoehe   = "Fuellhoehe"
FhemReadingTemperatur   = "Temperature"
FhemReadingHumidity     = "Humidity"
                                        -- %%20 is the escaped version of %20 because this string will be used with string.format
                                        -- &XHR=1 is used not to be redirected to the FHEM web page -> the answer from the web server is in this case much smaller, only "HTTP/1.1 200 OK" without any content
FhemDataSendStr         = "GET /fhem?cmd=setreading%%20%s%%20%s%%20%s&XHR=1 HTTP/1.1\r\nHost: %s:%s\r\nConnection: keep-alive\r\nAccept: */*\r\n\r\n')"

SendLinesTable           = {}              -- here will be the strings stored which are used to transfer data to FHEM
SendLineNr                 = 1             -- used to track the number of lines

-- WEB site
AutoRefresh = MeasureTime/1000      -- automatic refresh of the web side in seconds

---------------------------
-- Calculate the amount of oil in the tank by the  measured distance to the oil level in cm.
-- Returns liter or NIL if an invalid distance is measured
---------------------------
function DistanceToLiter(distance)
    OilLevel = tankheight - distance - offset         -- from the bottom of the tank to the oil surface
    if OilLevel > 0 and OilLevel < tankheight then    -- check for valid level
        liter = tankvolume / tankheight * OilLevel      -- what we have in the tank in liter
        return string.format("%.d",liter)
    else
        return NIL                                      -- invalid value
    end
end

----------------------------
-- calculate days, hours minutes and seconds from a counter "Counter" which is incremented every "cycletime" seconds
-- returns a table with the elements
----------------------------
function CounterToUpTime(Counter,cycletime)
    print ("CounterToUpTime")
    ret = {}
    Counter = Counter * cycletime -- now the counter is in seconds
    ret["days"] = string.format("%.d",Counter / 60 / 60 /24)
    Counter = Counter - ret["days"]*24*60*60
    ret["hours"] = string.format("%.d",Counter / 60 / 60)
    Counter = Counter - ret["hours"]*60*60
    ret["minutes"] = string.format("%.d",Counter / 60)
    Counter = Counter - ret["minutes"]*60
    ret["seconds"] = string.format("%.d",Counter)
    return ret
end

----------------------------
-- Start WEB server
---------------------------
function StartWebserver()
    print("Prepare Webserver")
    srv=net.createServer(net.TCP)       -- create the server
    srv:listen(80, function(conn)       -- start the server
    conn:on("receive",                  -- this event is triggered if the server receive a request
        function(client,request)        -- just a simple function which answers, it doesn't matter what was requested   
            val = CounterToUpTime(measureCounter,MeasureTime/1000) -- calculate days, hours, minutes and seconds from the measure counter
            UpTimeStr = string.format("%.d days %.d hours %.d minutes %.d seconds ",val["days"],val["hours"],val["minutes"],val["seconds"])
            local line = ""             -- sending ich line separate with client:send leads to a crash, so I build one string           
            line = line..'HTTP/1.1 200 OK\n\n'
            line = line..'<!DOCTYPE HTML>\n'           
            line = line..'<html>\n'
            line = line..'<head><meta  content="text/html; charset=utf-8>"\n'
            line = line..'<title>ESP8266 Ultrasonic</title>\n'
            line = line..'<meta http-equiv="refresh" content="'..AutoRefresh..'" />\n'  -- automatic refresh every x seconds
            line = line..'</head>\n<body>'
            line = line.."<h1> Ultrasonic Web Server</h1>"
            line = line.."<table>"
            line = line.."<tr><th>Name</th><th>value</th></tr>"                 -- table with data
            line = line.."<tr><td>distance</td><td>"..Distance.."</td></tr>"
            line = line.."<tr><td>Oil</td><td>"..liter.."</td></tr>"
            line = line.."<tr><td>Temperature</td><td>"..temp.."</td></tr>"
            line = line.."<tr><td>Humidity</td><td>"..humi.."</td></tr>"
            line = line.."<tr><td>Up time</td><td>"..UpTimeStr.."</td></tr>"
            line = line.."</table>"
            line = line..'</body></html>\n'
            client:send(line)
        end)
   
        conn:on("sent",                     -- this event is triggered if data is sent
        function(conn)
            conn:close()                -- as all data out, connection can be closed now
            -- collectgarbage()
        end)
    end)
    wifi.sta.eventMonStop(unregister_all)   -- unregister the event which starts the web server because the server is up now
end




---------------------------
-- Fill StartTime and StopTime
---------------------------
function GetTimes(GpioEchoPin)    -- based on https://github.com/sza2/node_hcsr04/blob/master/src/hcsr04.lua
    if GpioEchoPin == 1 then
        StartTime = tmr.now()      -- rising edge = start measure the time
        gpio.trig(GpioEcho, "down")
    else
        StopTime = tmr.now()       -- stop measure the time
    end
end
   
---------------------------
-- Trigger the GPIO
---------------------------
function TriggerGPIO()
    gpio.trig(GpioEcho, "up", GetTimes)
    gpio.write(GpioTrigger, gpio.HIGH)
    tmr.delay(100)
    gpio.write(GpioTrigger, gpio.LOW)
    tmr.delay(30000)
end

---------------------------
-- Fill a table with all lines which shall be send to FHEM
---------------------------
function PrepareDataToSend()
    SendLinesTable = {} -- empty table

    SendLineNr = 1      -- line number which shall be sent next. Init to 1 because LUA start arrays with index 1       
   
    table.insert(SendLinesTable,string.format(FhemDataSendStr,FhemDevice,FhemReadingLiter,liter,FhemIp,FhemPort))
    table.insert(SendLinesTable,string.format(FhemDataSendStr,FhemDevice,FhemReadingEntfernung,DistanceMedian,FhemIp,FhemPort))
    table.insert(SendLinesTable,string.format(FhemDataSendStr,FhemDevice,FhemReadingfuellhoehe,OilLevel,FhemIp,FhemPort))
    table.insert(SendLinesTable,string.format(FhemDataSendStr,FhemDevice,FhemReadingTemperatur,temp,FhemIp,FhemPort))
    table.insert(SendLinesTable,string.format(FhemDataSendStr,FhemDevice,FhemReadingHumidity,humi,FhemIp,FhemPort))     
end

---------------------------
-- Send data to FHEM
---------------------------
function SendData()
    -- prepare connect to FHEM server by generating a socket
    sk=net.createConnection(net.TCP, 0)
    -- Receive event which will be fired if the answer is received
    sk:on("receive",
        function(conn, payload)
            -- print("receive :",payload)
            -- now we can send the next line or close the connection
            if SendLineNr <= table.getn(SendLinesTable) then
                sk:send(SendLinesTable[SendLineNr])     -- event driven send the next line
                SendLineNr = SendLineNr + 1
            else
                sk:close()  -- all lines have been sent, we can close the connection
            end
        end) 
    -- Connect event, will be fired if the connection has been established   
    sk:on("connection",
        function(conn, payload)
            -- print("connect :",payload)
            -- prepare data for transfer                     
            -- print("Send: ",SendLinesTable[SendLineNr])
            -- as we are connected, we now can send the data
            sk:send(SendLinesTable[SendLineNr])
            SendLineNr = 2      -- first line has been sent, this will be the next to send
        end)
    -- connect to FHEM server     
    sk:connect(FhemPort,FhemIp)           
end

--------------------------
-- Event which will be called by the falling edge of the echo GPIO
-- Here the complete calculation of the oil volumn is done
--------------------------
function Main()
    print("Heap: ",node.heap())
    print("Counter",measureCounter)

    status, temp, humi, temp_dec, humi_dec = dht.readxx(GpioDht22)
    if status == dht.OK then
        temp = temp - SelfHeating            -- correct the self-heating effect
        print("DHT22: ",status,temp,humi)
        SonicSpeed = 331.5 + (0.6 * temp)   -- calculation of sonic speed in air by temperature
    else
        print( "DHT error." )
        SonicSpeed = 331.5 + (0.6 * DefaultTemperature)   -- without temperature sensor we use a default value
    end
    TriggerGPIO()  -- trigger the gpio port
    if StopTime > StartTime then -- check that we get a good time value
        -- calculate the distance from the time the echo is needed
        -- at 20°C the velocity of sound is round about 343,2 m/s, depending on the temperature
        -- we have to divide by two because we measure the double way
        Distance = ((StopTime - StartTime) /1000 / 1000 * SonicSpeed) / 2 * 100 -- distance in cm
        print("Entfernung: ",Distance)
        table.insert(DistanceTable,Distance)   -- insert into a table to generate the median
        if table.getn(DistanceTable) == MedianArrayValues then -- check if enough values collected
            table.sort(DistanceTable)    -- check if enough values collected
            DistanceMedian = DistanceTable[MedianPosition]    -- the median value is the one in the middle
            DistanceTable = {}    -- delete table elements
            liter = DistanceToLiter(DistanceMedian)    -- calculate the amount of oil in the tank
            if liter ~= nil then  -- NIL is one possible value from the calculation function
                print ("Enfernung median , liter",DistanceMedian,liter)
                PrepareDataToSend()
                SendData()           
            end   
        end
    else
    print("Sonic error.")
    end
    measureCounter = measureCounter + 1   
end

--------------------------
--------------------------

print ("Start Script")
print ("Boot reason",node.bootreason())

-- prepare GPIO
gpio.mode(GpioTrigger,gpio.OUTPUT)  -- trigger pin will start a measueremnt cycle
gpio.mode(GpioEcho,gpio.INPUT)      -- echo pin receives the run time of the echo

-- start Main timer for cyclic measurement
tresult = tmr.alarm(MeasureTimerId,MeasureTime,tmr.ALARM_AUTO,Main)

print("Measurement timer started = ",tresult)

--- connect to WIFI
wifi.setmode(wifi.STATION)          -- mode = connect to a router
wifi.sta.config(WiFiSSID,WiFiPwd)   -- set configuration for wifi
wifi.sta.autoconnect(1)             -- connect to wifi
print("IP_address ",wifi.sta.getip())     

--- start web server if IP address available
wifi.sta.eventMonReg(wifi.STA_GOTIP,StartWebserver) -- register function StartWebserver which will be called if we get an ip address
wifi.sta.eventMonStart()                            -- activate registered monitor

-- from here on, all functions are event based
-- MeasureTime is triggered cyclic and start a new measurement


Die Devices in Fhem sind bei mir in Fhem wie folgt angelegt:


define Oeltank dummy
attr Oeltank room Keller
define FileLog_Keller_Oeltank FileLog ./log/Keller_Oeltank-%Y.log Oeltank
attr FileLog_Keller_Oeltank logtype text
attr FileLog_Keller_Oeltank room Keller


mbrak

Hallo zuzsammen,

ich möchte mir auch so einen Tanksensor zusammenbauen.

Welche Widerstände habt ihr für den Spannungsteiler des ECHO Signals genommen? Die selben wie beim Raspi?

Was kommt sonst noch an das ESP-201 Modul an Beschaltung? Blockkondensator ist klar. Da gibts doch noch ein Chip-Enable oder so.
Wie habt ihr das mit dem Boot-Mode gemacht?

Könnt ihr mir hier mal unter die Arme greifen?

Vielen Dank und Gruß Michael

peter_w

#11
Hallo Michale,

ich habe die Schaltung genau so umgesetzt wie auf dem Bild zu sehen.
Der Widerstand an Trigger hat 470 Ohm, den hatte ich vergessen (ist jetzt drin).
Release  : 5.8
Raspberry Pi 3
CUL V 1.63 CSM868 HomeMatic (SCC)
HM-CC-RT-DN,HM-LC-Bl1PBU-FM,HM-LC-Sw1PBU-FM,HM-SEC-SCo,HM-WDS10-TH-O

mbrak

Danke Peter,

Ich habe jetzt noch eine blöde Frage. Wie bekomm ich das Lua Script auf den esp-201?
Welche Software brauche ich dazu? Nen USB-serial Adapter hab ich hier noch liegen. Das ist kein Problem. Die arduino Software kann ja jetzt auch den esp-201 programmieren, aber das ist es sicher nicht oder?

Gruß Michael

peter_w

Hi Michael,

Bei deinem USB auf Seriell Adapter solltest du sicher sein dass er 3,3V macht und nicht 5V sonst gibt es eine nette Rauchwolke.
Ich habe dafür folgendes verwendet: http://www.amazon.de/XCSOURCE%C2%AE-FT232RL-Konverter-Adapter-TE321/dp/B014SJK2NS man kann das Teil zwischen 5V und 3,3V umstellen.

Wie man es zum Flashen verkabeln muss ist hier beschrieben: https://www.adlerweb.info/blog/2015/12/06/bitbastelei-176-esp8266-mit-der-arduino-ide/esp8266_esp_201_module_pinout_diagram_cheat_sheet_by_adlerweb-d9iwmqp-2

Dann muss noch die Firmware auf den 201er die man sich hier bauen lassen kann: http://www.amazon.de/XCSOURCE%C2%AE-FT232RL-Konverter-Adapter-TE321/dp/B014SJK2NS
und wie man das flasht ist hier beschrieben: https://nodemcu.readthedocs.io/en/dev/en/flash/

Ich verwende die "Entwicklungsumgebung" Esplorer  http://esp8266.ru/esplorer/
Damit kannst du den Quelltext laden und laufen lassen.

Ich denke damit hast du eine schöne Zusammenfassung wofür ich ein Weilchen suchen musste  ;)
Release  : 5.8
Raspberry Pi 3
CUL V 1.63 CSM868 HomeMatic (SCC)
HM-CC-RT-DN,HM-LC-Bl1PBU-FM,HM-LC-Sw1PBU-FM,HM-SEC-SCo,HM-WDS10-TH-O

mbrak

Hi Peter

ganz vielen Dank dafür.
Vielleicht hilft es dem ein oder anderen auch noch :)

Zur Firmware hab ich noch ne Frage. Integer oder Float? Hab ich eben schon bauen lassen :)


Gruß Michael

peter_w

Release  : 5.8
Raspberry Pi 3
CUL V 1.63 CSM868 HomeMatic (SCC)
HM-CC-RT-DN,HM-LC-Bl1PBU-FM,HM-LC-Sw1PBU-FM,HM-SEC-SCo,HM-WDS10-TH-O

Edi77

Ich wollte mal fragen ob es Qualitätsprobleme beim DYP-ME007Y gegeben hat?
Master FHEM 6 als VM auf ESX Ubuntu 20.04 LTS mit MAXCube/MAX!/FS20|TabletUI|Flightradar|Tasmota|TTN Lora|CCU3 HomematicIP|RPi mit GammaScout|MQTT EasyESP 8266|LuftdatenInfo|deCONZ HUEDev|probemon|Siemens Logo|P4D|3D PRINTER RAISE3D

peter_w

Ich habe mir zwei bestellt und wärend der Entwicklungsphase teilweise mit beiden parallel gearbeitet. Bisher funktionieren beide.
Also keine mir bekannten Qualitätsprobleme.
Release  : 5.8
Raspberry Pi 3
CUL V 1.63 CSM868 HomeMatic (SCC)
HM-CC-RT-DN,HM-LC-Bl1PBU-FM,HM-LC-Sw1PBU-FM,HM-SEC-SCo,HM-WDS10-TH-O

kvo1

Hallo Peter,
sehr interessant, muß ich mir mal "vormerken"  ;)
RPi1: mit CUL: HM-CC-RT-DN,HM-ES-PMSw1-Pl,HM-LC-BL1-FM,HM-LC-Bl1PBU-FM,HM-LC-SW1-PL2,HM-SCI-3-FM,HM-SEC-SC-2,KFM-Sensor
RPi2: Viessmann(optolink) mit 99_VCONTROL.pm,
Cubietruck: Wheezy / Apache / Owncloud
Cubietruck: Armbian(Jessie) / fhem 5.7 / LMS 7.9
RPi3: (Test) mit 7" Touch  &  HM-MOD-RPI-PCB

mbrak

Guten Morgen,

kann mir bitte mal jemand behilflich sein?

Ich habe nun die Hardware komplett. Habe dann als erstes die Firmware, bestehend aus den 7 Modulen, die Peter genannt hatte auf den ESP201 geflashed. Das geht einwandfrei.
Im ESplorer wird bei booten des ESP auch die passende Firmware angezeigt. Soweit alles ok.
Wenn ich dann das LUA Script aus dem ersten Beitrag hochladen will, bricht ESplorer bei ca. 70% ab mit der Fehlermeldung, das es einen Timeout gab und der ESP nicht mehr antworten würde.
Wenn ich nun sämtliche Kommentare aus dem LUA Script entferne, kann ich das Script vollständig hochladen. Bekomme aber eine PANIK Meldung sobald dieses ausgeführt wird.

Was nun ????
Ist das Script so speicherfüllend, das die Kommentare schon zuviel sind?
Kann mir evtl. jemand, bei dem das ganze schon läuft sein möglicherweise angepasstes Script zusenden?

Vielen Dank
Michael

peter_w

Hallo Michael,

ich habe die Datei inc. der Kommentare auf dem ESP201 und nicht den Eindruck dass der Speicher knapp war.
Ich hätte zwei Ideen:

1. Hast du vielleicht zu viele Komponenten in der Firmware ?
Ich habe mir hier eine Firmware bauen lassen: http://nodemcu-build.com/  die verwendeten Module sind: dht, file, gpio, net, tmr, uart, wifi mehr brauchte ich für das Projekt nicht, Ich habe die float Varainte bauen lassen.

2. Ist evt. noch eine weitere Software im Flash ?
In ESPlorer mal mit "Format" im rechten Fenster den Speicher aufräumen lassen

Gruß
   Peter
Release  : 5.8
Raspberry Pi 3
CUL V 1.63 CSM868 HomeMatic (SCC)
HM-CC-RT-DN,HM-LC-Bl1PBU-FM,HM-LC-Sw1PBU-FM,HM-SEC-SCo,HM-WDS10-TH-O

Franz Tenbrock

Hallo
nachdem ich mit esp easy den DYB Sensor nicht ans laufen bekomme, versuche ich das dann hiermit.

Wie man es zum Flashen verkabeln muss ist hier beschrieben: https://www.adlerweb.info/blog/2015/12/06/bitbastelei-176-esp8266-mit-der-arduino-ide/esp8266_esp_201_module_pinout_diagram_cheat_sheet_by_adlerweb-d9iwmqp-2

das hier finde ich leichter verständlich, wenn es denn richtig wäre.
http://flower-platform.com/2015/12/16/esp8266-with-at-commands-flashingupdating-the-firmware-step-by-step/

geht das irgendwie auch mit einer nodemcu ?, mag zwar etwas teurer sein, bringt aber Stromversorgung gleich mit.

Spannungsteiler für das Echosignal auch mit 4,7K und 2.2k ( aus einr HC-SR04Anleitung )
möglich? Mit espeasy klappt es mit diesem Sensor?


Das flashen hab ich vor Monaten schon mal gemacht... irgendwie wird das dann wohl klappen
cubi3, Cul 868, ESA2000WZ, EM1000GZ,  FS20, dashboard, 1-Wire, Max Thermos, Max Wandthermo, Max Lan, Fritzbox callmonitor, , nanocul, HM Led16, HM Bewegungsmelder, HM Schalter, RPi, banana, ESP8266, DoorPi

justme1968

ich bin gerade dabei mein vor jahren angefangen projekt mit einem panstamp auf einen esp8266 umzustellen. damals hatte ich schon rausgefunden das es wichtig ist die temperatur zu messen und mit einzubeziehen. sonst sind die abgweichungen des gemessenen abstands unter umständen sehr gross.

hat jemand von euch schon erfahrungen in wie fern die luftfeuchte ebenfalls eine rolle spielt? hat jemand schon eine formel die diese mit einbezieht?

oder ist sie nicht relevant? vielleicht weil im tank heizöl dampf ist der sowieso die luft verdrängt? wäre dann nicht auch eher die schallgeschwindigkeit in diesem medium statt normaler luft wichtig?

gruss
  andre
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

targa

Ich habe eine optische Lösung, die muss nicht kompensiert werden, nur interpoliert an den Stellen, an denen die Stahlbaender an dem Tank angebracht sind.

Tedious

Ich wollte das Thema noch einmal aufgreifen ;)

Ich hab das ganze angepasst und baue das grade nach. Läuft ncoh nicht ganz, ich hab noch Probleme mit dem Code, aber ich wollte zumindest ein Feedback geben ;) WLan wird inzwischen anders definiert, man muss die Parameter bei den neueren Firmwares in eine Tabelle übergeben:

station_cfg={}
station_cfg.ssid="NODE-AABBCC"
station_cfg.pwd="password"
wifi.sta.config(station_cfg)


Zudem steigt er im Moment noch aus - ich hab versehentlich den DTH falsch herum angeschlossen und gegrillt - der neue sollte heute ankommen;) Anbei mein Schaltschema, und ein Bild vom Aufbau. Nicht schlagen, ich bin Naturwissenschaftler und hab mir das Löten selbst beigebracht ;) Der DHT kommt an die Steckbuchse, Ultraschall an die Terminalklemmen (rechts Strom, links Trig/Echo)


FHEM auf Proxmox-VM (Intel NUC) mit 4xMapleCUN (433,3x868) und Jeelink, HUE, MiLight, Max!, SonOff, Zigbee, Alexa, uvm...

Zander1st

Hallo,
also ich versuche schon verzweifelt das Script zum laufen zu bekommen, aber leider schaff ich es nicht.
Ich bekomme immer diese Fehlermedung:
Start Script
Boot reason   2   6
Measurement timer started =    true
oeltank2.lua:277: bad argument #1 to 'config' (config table not found!)
stack traceback:
   [C]: in function 'config'

und ich check einfach nicht woran es ligen kann.
Das script startet und der dht wird ausgelesen.
Nur die wlan verbindung klappt irgendwie nicht

Vielleicht kann mir einer ja hier helfen Danke

Tedious

Das Problem und die Lösung steht exakt einen Post über deiner Frage....  ::)
FHEM auf Proxmox-VM (Intel NUC) mit 4xMapleCUN (433,3x868) und Jeelink, HUE, MiLight, Max!, SonOff, Zigbee, Alexa, uvm...

Marco2017XY

#27
Hallo,
ich brauche dringend Hilfe, ich bekomme immer die gleiche Fehlermeldung.
----------------------------
TEST3.lua       : 13688 bytes
----------------------------
Total file(s)   : 1
Total size      : 13688 bytes

Total : 519821 bytes
Used  : 14056 bytes
Remain: 505765 bytes

> dofile("TEST3.lua")
not enough memory
Ich habe mir auch die Module zusammenstellen lassen(dht,file,gpio,net,tmr,uart und wifi)

Was mache ich falsch?
bin seit 1 Jahr am diesem Problem komme nicht weiter bin am verzweifeln.
Danke fuer Eure Hilfe.