Contolling solar themal

Place your projects here
Post Reply
FrankM
Posts: 6
Joined: Fri Feb 12, 2021 2:26 pm
Has thanked: 1 time
Been thanked: 1 time

Contolling solar themal

Post by FrankM »

Hello dear forum members,

I built a control for my solar thermal and wrote the program with Annex WiFi. Unfortunately, some parts of the program are not yet working as I wanted. Anything goes roughly, however, the program only stops about every two to three days with an 'out of memory error' which is usually displayed within or at the end of the website.
So far I haven't found a solution for this.

Maybe someone from the forum would like to have a look at the program together with the circuit diagram and the photos of my assembly. Maybe it's a simple mistake, but despite many hours of 'triqal and error', I was unable to guarantee stable operation over several weeks.

I would appreciate your feedback!

Frank Martens



I did syntax highlight with annex toolkit, but was not able to put the coloured text in here!

CODE: 0yzeittest.bas
--------------------------------------------------------------------------------

Code: [Local Link Removed for Guests]

'Solarthermie - Steuerung
versions$="/program/0yzeittest.bas"
version2$=versions$ + ""
wlog "reset reason: ";
wlog BAS.VER$
select case bas.ResetReason
  case 0: wlog "normal startup"
  case 1: wlog "hardware WDT"
  case 2: wlog "exception reset"
  case 3: wlog "software WDT"
  case 4: wlog "software restart"
  case 5: wlog "wake up from deep sleep"
  case 6: wlog "external reset"
end select
ONERROR GOTO Error_Handler

select case wifi.status
case 0 : wifi$= "WiFi Leerlauf (IDLE)"
case 1 : WIFI$=  "NO SSID AVAILABLE"
case 2 : WIFI$=  "SCAN COMPLETED"
case 3 : wifi$=  "CONNECTED"
case 4 : WIFI$=  "CONNECTION FAILED"
case 5 : WIFI$=  "CONNECTION LOST"
case 6 : WIFI$=  "DISCONNECTED"
end select
wlog "WiFi-Status "; WIFI$

' after electrical power outage  the ESP is faster than my WiFi
while wifi.status <> 3  ' wartet bis die WLAN-Verbindung steht
wlog "WiFi-Status= "; wifi.status
pausex 10000
reboot
wend





'OPTION.WLOG 1  '  mit 0 ausschalten1  - erst ab 1.42.3
OPTION.NTPSYNC  ' Zeit synchronisieren
gosub definitionen  ' intiialisiert die pins und andere Variablen
interrupt CLK, encoder
gosub fehleralt ' zeigt den Inhalt der error.txt Datei an
wlog "Pumpenlaufzeit mit Datum "; file.read$("/pumpdauer.txt")
file.save "/pumplaufzeit.txt", str$(0)  'fuer neuen Tag die Laufzeit auf 0 setzen - erst im Echtbetrieb aktivieren

datumstart=DATEUNIX(date$) + timeunix(time$) + (CES-1)*3600  ' vorher CES-1
tageslaenge= 1440*60  ' initial 24-Stunden Tageslaenge
nexttag   = datumstart + tageslaenge
datumstart$   =unixdate$(datumstart)
uhrzeitstart$ =unixtime$(datumstart)
datumnext$    =unixdate$(nexttag)
uhrzeitnext$  =unixtime$(nexttag)
wlog "date$, time$ "; date$, time$
gosub analyseflag1
wlog "jetzt vor erstem Start der webseite "
gosub website  'erster Start der Website
ONHTMLRELOAD website

tageslaenge=0
nexttag   = 0
datumstart$=""
uhrzeitstart$=""
datumnext$=""
uhrzeitnext$=""


'########################################### main loop ################################
progstart:

status=0 : nexttag=0 : aktstatus=0
datumstart=DATEUNIX(date$) + timeunix(time$) + (CES-1) * 3600
mitternacht=DATEUNIX(date$) + timeunix("23:59:59") + (CES-1) * 3600
restlaenge= mitternacht-datumstart
aufgang_std = val(left$(sy$,2))
aufgang_min = val(mid$(sy$, 4,2))
tageslaenge=restlaenge +( aufgang_std*60 + aufgang_min )*60   ' Sekunden bis Sonnenaufgang
nexttag   = datumstart + tageslaenge
'wlog "restlaenge  "; restlaenge; "*****************************************************"
'wlog "auf_std   aufmin "; aufgang_std, aufgang_min
DO
  logzeit=millis
  'status = nexttag -  datumstart   'Sekunden bis Neustart der Programmloop
  aktstatus = nexttag - (DATEUNIX(date$) + timeunix(time$) + (CES-1) * 3600)  'verbleibende Sekunden bis Neustart der Programmloop
  i = i + 1 ' loop-counter
  datumstart$   =unixdate$(datumstart) + "  "
  uhrzeitstart$ =unixtime$(datumstart)
  datumnext$    =unixdate$(nexttag) + "  "
  uhrzeitnext$  =unixtime$(nexttag)
  aktstatus$    = "Restzeit " + unixtime$(aktstatus)
  gosub licht
  gosub temperatur
  gosub logik ' analysiert Licht und Temperatur
   if (licht > lichtmin) then gosub tft_ausgabe
  logzeit2=(millis-logzeit)/1000
  wlog "Rundenlaufzeit in Do/while Schleife "; logzeit2
  gosub analyseflag1  
LOOP while aktstatus >0


wlog "bin vor GOTO progstart";
gosub analyseflag1  ' neue Sonnenzeiten errechnen
' wlog "dateunix von ",dateunix(sonnenauf$), dateunix(sonnenunter$)
' hier könnte der test auf sonnenaufgang/untergang gespeichert werden


pumpenlaufzeitgesamt$ = file.read$("/pumplaufzeit.txt") ' reads pump time of the ending day
pu$= date$ + "=" +  pumpenlaufzeitgesamt$ + "#"  ' save pump time together with a time stamp for later analysis
file.append "/pumpdauer.txt", pu$                ' does not work correctly
file.save "/pumplaufzeit.txt", str$(0)  ' pump time in new day is zero
ONHTMLRELOAD website

goto progstart
'########################################## end main loop ############################
end


logik:  ' ######################### analysis of light and temperature conditions for activationg the pump - or not ###########################
' if saverflag=1 then  tft.fill 0 : saverflag=0

if (licht > lichtmin) then hell=1 else hell=0
select case hell
  case 0
    pumpe=1     :wlog "es ist dunkel"
    gosub relais_aus
    saverflag=1
    gosub screensaver
  case 1
    gosub logik1
   ' wlog "es ist hell  "; licht, lichtmin
end select
clear=0


logik1:  ' only called if there is enough light
if hell=0 then goto endelogik  'return
if saverflag=1 then  tft.fill 0 : saverflag=0 
warm=0: kalt=0
gosub temperatur
if tempoben < 16 then warm=2 : goto vorcase  ' if temperature in solarpanel is under 16 Celsius pump off

if  (tempunten > tempoben)       then warm=2   'wenn rücklauf wärmer als Vorlauf, Pumpe ausschalten
if  (tempoben-tempunten) > delta then warm=1
if  (tempoben-tempunten) < delta then warm=0   'wenn gemessene Temperaturdifferenz grösser als Sollwert, warm=1 ansonsten warm=0

vorCase:
SELECT CASE warm
  CASE 0: wlog "Fluid kalt ";tdelta, delta               'warm=0
    gosub temperatur
    t0=fix(tempoben)                 'miss die temp oben und merke sie dir
    pumpzeit=millis                                ' fuer pumpzeitberechnung
    gosub relais_an                                ' Pumpe an für 60 Sekunden
    TFT.TEXT.POS 48, 30
    tft.text.size 2
    tft.print "Wait1"                                ' Anzeige TFT Wait
    pumpe=0 : pumpe$ = "60 sek"
    'TFT.RECT 100, 35, 10, 10,TFT.RGB(0,255, 0),1    ' grünes Rechteck daneben (Pumpe an)
    wlog pumpe$
    pausex 60000  ' laeuft fuer 1 Minute
    gosub temperatur                                ' erneute Temperaturmessung
    t1=fix(tempoben)
    wlog "T_oben vor kurzlauf   T_oben danach    :"; t0,t1
    if tempoben-tempunten < delta then gosub relais_aus
    ' gosub relais_aus
    pumpe$=""
    tt=10
    do until tt=1  ' delay in minutes for waiting until the next test if fluid will be warmer than before
      anzeige$="Wait"+ str$(tt) + " Min" + " WARM=" + str$(warm)
      tft$="Wait" + str$(tt)
      wlog anzeige$ '"Warte jetzt 10 Minuten"
      pumpe$ = tft$  'str$(tt)  'anzeige$ '   Warte jetzt 10 min"
      TFT.TEXT.POS 48, 30
      tft.text.size 2
      tft.print "Wait  "
      TFT.TEXT.POS 48, 30
      tft.text.size 2
      tft.print tft$                 ' Anzeige TFT Wait
      pumpe=1
      gosub temperatur
      if tempoben-tempunten > delta then  tt=2  'print "tempoben-tempunten>delta" 'exit do
      pausex 60000                                ' ansonsten warte 1 Minute  mal tt
      tt = tt - 1
    loop
    '  end if
  CASE 1: wlog "Fluid warm ";tdelta, delta
    pumpe=0: pumpe$ = "Normalbetrieb - Pumpe an"
    gosub relais_an
    'pause 1000
    
  CASE 2: wlog "Fluid invers ";tdelta, delta
    pumpe=1: pumpe$ = "T_invers - Pumpe aus"
    gosub relais_aus
END SELECT
endelogik:

warm=3
return



relaiszustand: ' hier wird die Dauer der Pumpenlaufzeit berechnet
if pin(relais)=0 then  pumpstart=timeunix(time$): pumpend=pumpstart
if pin(relais)=1 then  pumpend=timeunix(time$)
pumpenlaufzeitgesamt$ = file.read$("/pumplaufzeit.txt") 'gespeicherte Pumpenlaufzeit holen
pumpenlaufzeit=pumpend-pumpstart: pumpdauer$=unixtime$(pumpenlaufzeit)
pumpenlaufzeitgesamt = val(pumpenlaufzeitgesamt$) + pumpenlaufzeit
pumpenlaufzeitgesamt$= unixtime$(pumpenlaufzeitgesamt)
file.save "/pumplaufzeit.txt", str$(pumpenlaufzeitgesamt)
return



licht: '----------------------------------------------------------------------------------
licht$="    " : lichtmin = 550 : licht=0 : tt=10 : hell=0 : lichtfarb$=""'fuer Helligkeitsmessung  lichtmin=550
licht = adc

' gets data from solar inverter in kW for display on website only
p$=wget$("192.168.44.27/raw_data/",80)
ppv= val(mid$(p$,INSTR(p$, "PowerPV=")+8, 3))

' reads an alternative llight-sensor from another site  - actually not used because too slow
goto licht1
p$=wget$("192.168.44.28/",80)
'  wlog "/";p$;"/"
p= INSTR(p$, "Helligkeit </td>  <td> </td>  <td> ")
'wlog "pos helligkeit /"; p; "/"
h$=mid$(p$,p+3, 39)
'wlog "h$ /";h$; "/"
t$=right$(h$,8)
'wlog "right$  /";t$; "/ "
hellig = val(t$)
wlog "Helligkeit Dach= "; hellig
'###################################
licht=hellig

licht1:
if (licht > lichtmin) then
  hell=1
  licht$="hell "
  lichtfarb$= "<font color=green>"
  lichtfarbcss$ =  |  color: blue;|
  lichtfarbcss$ = lichtfarbcss$ + |  background-color: yellow;|

else
  licht$="dunkel "
  hell=0  ' vorher 1
  lichtfarb$="<font color=white>"
  lichtfarbcss$ =  |  color: brown;|
  lichtfarbcss$ = lichtfarbcss$ + |  background-color: black;|

endif

return

temperatur:'--------------------------------------------------------------------------------
goto temp1
'wlog "Tempr$  ";tempr$(onewire) ' fuer Berechnungen um die Zuordnung der Temperatursensoren
if WORD$(tempr$(onewire),1)="288465cf332001ed" then tnummer=2   '2 ist fuer oben
if WORD$(tempr$(onewire),1)="28435577332001fe" then tnummer=1
if WORD$(tempr$(onewire),1)="28ff5d30501704e1" then tnummer=2
if WORD$(tempr$(onewire),1)="28aa7e3b1e130225" then tnummer=1
' wlog "tempnummer=2  "; tempr$(onewire,2)
' wlog "tempnummer=1  "; tempr$(onewire,1)
temp0: ' holt sich die tempoben aus dem WLAN
'tt$=wget$("192.168.44.6/", 80)
'tt$="28"
'wlog "     Temp oben   /";tt$;"/  "
't=val(tt$)
'tt$=""
temp1:
t = val(tempr$(onewire,2)) 
tempoben$ = str$(t,"%2.0f")  :  tempoben= fix(val(tempoben$))    'Temperatur Dach, als Integer
t = val(tempr$(onewire,1)) 
't = 22  'fuer test
tempunten$ = str$(t,"%2.0f") :  tempunten= fix(val(tempunten$))   'Temperatur Rücklauf
tdelta= abs(tempoben-tempunten)  'rem gemessene Temperaturdifferenz
t=0
return

relais_an: '----------------------------------------------------------------------
gosub licht
pin(relais) = 0  ' relais anschalten1
pumpe=0 : pumpe$="an "
farb$="<font color=green>" :  TFT.RECT 100, 55, 10, 10,TFT.RGB(0,255, 0),1   ' ' grünes Rechteck daneben (Pumpe an)
if (licht > lichtmin) then gosub tft_ausgabe
return

relais_aus: '----------------------------------------------------------------------
pin(relais) = 1  ' relais ausschalten1
pumpe=1 : pumpe$="aus"
farb$="<font color=blue>"
TFT.RECT 100, 55, 10, 10,TFT.RGB(255,0, 0),1    ' rotes Rechteck daneben (Pumpe aus)
if (licht > lichtmin) then gosub tft_ausgabe
return


tft_ausgabe: '---------------------------------------------------------------------
'wlog "Bin im TFT "
gosub temperatur

if clear=0 then   ' bei erstmaligem Aufruf TFT schwarz und Bild laden
  tft.fill 0:
  TFT.BMP "/Praes128inv.bmp", 0, 0,0
  clear=1
end if
' after 800 calls of this routine clear and refresh the TFT
cl = cl + 1
'wlog "Clearzähler  "; cl; "  "; time$
if cl = 800 then
cl=0
clear=0
tft.fill 0:
TFT.BMP "/Praes128inv.bmp", 0, 0,0
end if

TFT.TEXT.POS 10, 3
tft.text.size 1
tft.print "       "
TFT.TEXT.POS 10, 3
tft.text.size 1
tft.print licht$ ' zeigt hell oder dunkel auf TFT
TFT.TEXT.POS 10, 20
tft.text.size 1
tft.print "     "
TFT.TEXT.POS 10, 20
tft.print licht ' zeigt den adc-Wert für Licht an



TFT.TEXT.POS 64, 3
tft.text.size 2
tft.print tempoben$  ' zeigt Temperatur oben mit 1 Nachkommstelle
TFT.TEXT.POS 48, 30
tft.print "Pump"
if (pumpe=0) or (tdelta>delta) then  TFT.RECT 100, 55, 10, 10,TFT.RGB(0,255, 0),1   'rem grünes Rechteck
if (pumpe=1) or (tdelta<delta) then  TFT.RECT 100, 55, 10, 10,TFT.RGB(255,0, 0),1   'rem rotes rechteck

if tdelta > delta then
  TFT.LINE 35,66,40,55,TFT.RGB(0,255,0)  'rem grünes Dreieck (delta)
  TFT.LINE 40,55,47,67,TFT.RGB(0,255,0)
  TFT.LINE 47,67,35,66,TFT.RGB(0,255,0)
else
  TFT.LINE 35,66,40,55,TFT.RGB(255,0,0)  ' rem rotes Dreieck
  TFT.LINE 40,55,47,67,TFT.RGB(255,0,0)
  TFT.LINE 47,67,35,66,TFT.RGB(255,0,0)
endif
tft.text.size 2
TFT.TEXT.POS 4, 52
'tft.print "  "
tft.print delta   ' zeigt eingestellte Temperaturhysteres
TFT.TEXT.POS 55, 52
tft.print "  "
TFT.TEXT.POS 55, 52
tft.print tdelta
TFT.TEXT.POS 14, 75
tft.text.size 1
if warm=1 then tft.print "warm"
if warm=0 then tft.print "kalt"
TFT.TEXT.POS 42, 75
tft.text.size 1
tft.print "Pump"          'Sunset "


TFT.TEXT.POS 14, 83
tft.text.size 1
tft.print "SA"
TFT.TEXT.POS 30, 83
tft.text.size 1
tft.print left$(sonnenauf$,5)

TFT.TEXT.POS 14, 90
tft.text.size 1
tft.print "SA"
TFT.TEXT.POS 30, 90
tft.text.size 1
tft.print left$(sonnenunter$,5)


linie: ' shows a rectangular representing the solar elevation angle
divisor=  160/24  
inkrement=stunde
xb=divisor*inkrement
if (sae<0) then
 yb = 64 
 yko =  32 + abs(sae)  
 else
 yb=    64-sae 
 yko=sae
 endif
tft.rect xb,yb, divisor,yko, TFT.RGB(0,245,255),1
yb=0: xb=0: yko=0

TFT.TEXT.POS 40, 106  ' calculates angle between sun and solar panel 
tft.text.size 1
dw=48 ' Dachneigung 
neig= 180-(dw+sae)
neig$="Treffwinkel "+ str$(int(neig))
tft.print neig$ 

nx = 30 * sin(neig*kk) + 20
ny = 30 * cos(neig*kk) + 20


tft.line 0, 0, nx, ny, TFT.RGB(0,245,255)
tft.line nx, ny, 0, 0, TFT.RGB(255,0,0)



TFT.TEXT.POS 76, 75
tft.text.size 1
tft.print  unixtime$(pumpenlaufzeitgesamt) 
TFT.TEXT.POS 4, 96
tft.text.size 1
tft.print i     ' loop-counter
tft.text.size 2
TFT.TEXT.POS 64, 90
tft.print tempunten$   ' zeigt Temperatur unten mit 1 Nachkommstelle
TFT.TEXT.POS 4, 115
tft.text.size 1
tft.print ana_datum$     'Datum vom Zeitserver
TFT.TEXT.POS 68, 115
tft.text.size 1
tft.print ana_uhr$      ' Uhrzeit vom Zeitserver
return






encoder:  'rotate to change temperature hysteresis
if ( pin(dt)=0) OR (pin(clk)=0)  then
  wlog
  if pin(CLK) = pin(DT) then delta = delta + 1
  if pin(CLK) <> pin(DT) then delta = delta - 1
  if delta>9 then delta=0
  if delta < 0 then delta =9
  file.save "/delta.txt", str$(delta)
  wlog "delta_mod", delta
end if
tft.text.size 2
TFT.TEXT.POS 4, 52
tft.print delta
return




website:
cls
autorefresh 100
ana_datum$=date$  'unixdate$(date$)  'datumstart)
ana_uhr$  =time$  'unixtime$(time$)  'datumstart)
A$ = ""

A$ = A$ + |<style>|
'A$ = A$ + | <code>width: 20em;<br>box-sizing: content-box;</code>|
A$ = A$ + ||
A$ = A$ + ||
A$ = A$ + |h1 { |
A$ = A$ + |  color: red;|
A$ = A$ + |  background-color: beige;|
'A$ = A$ + |  border: thin solid;|
A$ = A$ + | <code>width: 10em;<br>box-sizing: content-box;</code>|
A$ = A$ + |  font-size: 1.5em;|
A$ = A$ + |}|
A$ = A$ + ||
A$ = A$ + |p {|
A$ = A$ + lichtfarbcss$     '|  color: brown;|
A$ = A$ + |}|
A$ = A$ + ||
A$ = A$ + ||
A$ = A$ + |</style>|
A$ = A$ + ||
A$ = A$ + "</head>"
A$ = A$ + "<h1>SolarthermieKeller</h1>"
A$ = A$ + "<p><strong>"+ "IP = " + word$(IP$,1) + " " + wifi$ + "</strong>.&nbsp;</p>"
A$ = A$ + "<p><strong>"+  version2$ + "</strong>.&nbsp;</p>"
A$ = A$ + "<p><strong>"+ lichtfarb$ +"Helligkeit = " + licht$ + "</strong>.&nbsp;</p>"
A$ = A$ + "<table>"
A$ = A$ + "  <thead>"
A$ = A$ + "    <tr>"
A$ = A$ + "      <th></th>"
A$ = A$ + "      <th></th>"
A$ = A$ + "    </tr>"
A$ = A$ + "  </thead>"
A$ = A$ + "  <tbody>"
A$ = A$ + "    <tr>"
A$ = A$ + "      <td>Programmstart</td>"
A$ = A$ + "      <td>" + datumstart$   + "</td> "
A$ = A$ + "      <td>" + uhrzeitstart$ + "</td>"
A$ = A$ + "    </tr>"
A$ = A$ + "    <tr>"
A$ = A$ + "      <td>Aktuell </td>"
A$ = A$ + "      <td>" + ana_datum$ + "</td>"
A$ = A$ + "      <td>" + ana_uhr$ + "</td>"
A$ = A$ + "    </tr>"
A$ = A$ + "    <tr>"
laufzeit= TIMEUNIX(time$) - TIMEUNIX(uhrzeitstart$) : laufzeit$ = UNIXTIME$(laufzeit)
A$ = A$ + "      <td>Laufzeit </td>"
A$ = A$ + "      <td>" +   laufzeit$ + "</td>"
A$ = A$ + "      <td>" +   aktstatus$ + "</td>"
A$ = A$ + "    </tr>"
A$ = A$ + "    <tr>"
A$ = A$ + "      <td>Sunrise "+left$(datumeuro$, 5) + "</td>"
A$ = A$ + "      <td>" + sy$ + "</td>"
A$ = A$ + "      <td>Sunset</td>"
A$ = A$ + "      <td>" + sz$ + "</td>"
A$ = A$ + "    </tr>"
A$ = A$ + "    <tr>"
A$ = A$ + "      <td>Taglänge " + saa$ +"</td>"
A$ = A$ + "      <td>Lux</td>"
A$ = A$ + "      <td>" + str$(licht,"%5.0f") + "</td>"
A$ = A$ + "    </tr>"
A$ = A$ + "    <tr>"
A$ = A$ + "      <td>Diff_soll</td>"
A$ = A$ + "      <td>Diff_ist   </td>"
A$ = A$ + "      <td>T_oben    </td>"
A$ = A$ + "      <td>T_unten   </td>"
A$ = A$ + "    </tr>"
A$ = A$ + "    <tr>"
A$ = A$ + "      <td>" +  str$(delta) + "</td>"
A$ = A$ + "      <td>" +  str$(tdelta) + "</td>"
A$ = A$ + "      <td>" + tempoben$ + "</td>"
A$ = A$ + "      <td>" + tempunten$ + "</td>"
A$ = A$ + "    </tr>"
A$ = A$ + "    <tr>"
A$ = A$ + "      <td><strong>" + farb$ + "Pumpe </strong></td>"
A$ = A$ + "      <td><strong>" + farb$ + pumpe$ + "</strong> </td>"
A$ = A$ + "      <td>P</td>"
A$ = A$ + "    </tr>"
A$ = A$ + "    <tr>"
A$ = A$ + "      <td>Pumpenlaufzeit</td>"
A$ = A$ + "      <td>" + unixtime$(pumpenlaufzeitgesamt) + "</td>"
A$ = A$ + "    </tr>"
A$ = A$ + "    <tr>"
A$ = A$ + "      <td>Sonnenhöhe°</td>"
A$ = A$ + "      <td>" + str$(sae,"%2.1f") + "</td>"   
A$ = A$ + "    </tr>"

A$ = A$ + "    <tr>"
A$ = A$ + "      <td>Azimut°</td>"
A$ = A$ + "      <td>" + str$(sah,"%2.1f") + "</td>"   
A$ = A$ + "    </tr>"

A$ = A$ + "    <tr>"
A$ = A$ + "      <td>Auftreffwinkel°</td>"
A$ = A$ + "      <td>" + str$(neig,"%2.1f") + "</td>"  
A$ = A$ + "    </tr>"

A$ = A$ + "    <tr>"
A$ = A$ + "      <td>PV-Leistung°</td>"
A$ = A$ + "      <td>" + str$(ppv,"%2.1f") + "</td>"   
A$ = A$ + "    </tr>"

A$ = A$ + "  </tbody>"
A$ = A$ + "</table>"



'A$ = a$ +  "Deklination          "+ str$( st)   + "<br>"
'A$ = a$ +  "noon_std             "+ sx$  + "<br>"

'A$ = a$ +  "Laenge               "+ str$(laenge) + "<br>"
'A$ = a$ +  "Breite               "+ str$(breite)  + "<br>"
A$ = a$ +  "Fehler alt :" + contentalt$  + "<br>"
A$ = a$ +  "-----------------------------------------"
A$ = A$ + |  </body>|
A$ = A$ + "<br>"
A$ = A$ + "<br>"
a$ = a$ +  "<ZeigTabelle type='button'></button>"
a$ = a$ +  BUTTON$("ZeigTabelle", jump1)

A$ = A$ + "<br>"
A$ = A$ + "<br>"
a$ = a$ +  "<Plus type='button'></button>"
a$ = a$ +  BUTTON$("Plus", plus1)

A$ = A$ + "<br>"
A$ = A$ + "<br>"
a$ = a$ +  "<Minus type='button'></button>"
a$ = a$ +  BUTTON$("Minus", minus1)

endewebsite:
wlog "Laenge des a$  "; len(a$)
html a$
'refresh
a$=""
return

Jump1: ' shows table with date and lenght of pumping - does not work correctly until now
pr$= file.read$ ("/pumpdauer.txt")
wlog pr$
wlog
wlog
laenge=len(pr$)
v=1
aaa:
wortzahl = WORD.COUNT( pr$ ,"#")
for i = 0 to wortzahl-2
in$ = mid$( pr$, i*17+1+i, 17)
web$ = web$ + str$(i) + " " + in$ + " <br>"
wlog "Zaehler "; i; "String "; in$
next
cls
html web$
web$=""
refresh
wlog  "##########################################  Clicked on Button1   ###########"
Return


plus1: ' increments temperature hysteresis
delta = delta + 1
  if delta>9 then delta=0
  if delta < 0 then delta =9
  file.save "/delta.txt", str$(delta)
  wlog "delta_mod", delta
tft.text.size 2
TFT.TEXT.POS 4, 52
tft.print delta
refresh
return

minus1: ' decrements temperature hysteresis
delta = delta - 1
  if delta>9 then delta=0
  if delta < 0 then delta =9
  file.save "/delta.txt", str$(delta)
  wlog "delta_mod", delta
tft.text.size 2
TFT.TEXT.POS 4, 52
tft.print delta
refresh
return










screensaver:
zufallx= RND(20)
zufally=rnd(55)
tft.fill blau  '0
tft.text.size 2  '10 zeichen auf 128 px
saver$=aktstatus$ + " " + str$(licht)  'version$
'saver$="12345678901234"
breite=len(saver$)
TFT.TEXT.POS zufallx-1.1*breite, zufally
tft.print  saver$;
pausex 100
interrupt CLK, encoder
return



definitionen: ' --------------------------------------------------------------------------
rot=61440: gelb1=65261: gelb2=65218: blau=255:  green=4080 : schwarz=0
relais=4  ' at GPIO 4 = D2
scl=14 ' D5 for Display
sda=13  'D7 for display 
onewire=10: ' D12 one-wire
SW = 3 : DT = 16 : CLK = 12 : debounce=100  ' pins forr Rotary-encoder
clear=0 ' clears the TFT when it is called for the first time 
pumpstart=0 : pumpend=0 :pumpdauer$="" : pumpenlaufzeit=0 :  pumpenlaufzeitgesamt=0  'for length of running the pump
CES=2 ' 1 für MEZ, 2 für MESZ
licht$="    " : lichtmin = 550 : licht=0 : tt=10 'fuer Helligkeitsmessung  auch in Unterprog licht nochmals
warm=3: kalt=0 : hell=0
logzeit2=0:
i = 1  ' Loopzähler
delta  = 5  ' Hysterese fuer Temperaturdifferenz
' nur einmal am anfang file.save "/delta.txt", str$(5)
delta$ = file.read$("/delta.txt") : delta = val(delta$)'liest eingestellte Temperaturdifferenz aus Datei
'  wlog "Definitionen Delta aus Datei=";delta
pin.mode relais, output :   pin(relais) = 1  ' relais aus
interrupt relais, relaiszustand ' interrupt bei Schalten des Relais
pumpe=1 : pumpe$="aus"  ' fuer Darstellung auf TFT
'TFT.INIT 15, 0, 3, 128, 160, 1  ' Desktop tft.init CS, DC, orientierung, pixel_x, pixel_y, variable unbekannt
TFT.INIT 2, 5, 3, 128, 128, 1    ' Keller  tft.init CS, DC, orientierung, pixel_x, pixel_y, variable unbekannt
tft.fill 0   'clears TFT
TFT.BMP "/Praes128inv.bmp", 0, 0,0  ' loads graphic to TFT
gosub licht  ' first measure light during initialization
gosub temperatur ' first measure temperatures  during initialisation

'declaration of rotary encoder
pin.mode SW, input, pullup
pin.mode DT, input, pullup
pin.mode CLK, input, pullup
interrupt CLK, encoder  ' Beim Drehen des encoders wird interrupt ausgeloest

saverflag=1
datumeuro$="NN"
sonnenauf$="NN"
sonnenunter$="NN"
tageslaenge$="NN"
Aktstatus$="NN"
farb$="<font color=red>"
aufruf=0
neig=0
return

Error_Handler:
Print "Error text "; BAS.ErrMsg$
Print "Error num  "; BAS.ErrNum
Print "Error line "; BAS.ErrLine
content$ = BAS.ErrMsg$ + ",Errnum " + str$(BAS.ErrNum) + ",Errline " + str$( BAS.ErrLine)+ "  " + date$ + "  " + time$
file.save "/error.txt", content$
wlog content$
onerror skip 20
return ' returns to the line following the error

fehleralt:
if FILE.EXISTS("/error.txt") = 0 then return
contentalt$= FILE.READ$("/error.txt")
wlog "alte Fehlermeldung :"; contentalt$
return

change: ' returns formatted time strings from minutes for selected parameters
Uhrzeiten$=""
svoll= mn/60               ':' wlog "svoll   ";svoll
stund    = int(svoll)      ': wlog "stund   ";stund
minvoll = (svoll-stund)*60  ' wlog "minvoll ";minvoll
minut = int(minvoll)      ' : wlog "minut   ";minut
sekvoll = minvoll-minut    ': wlog "sekvoll "; sekvoll
sek = sekvoll*60           ': wlog "sek     "; sek
sekund=int(sek)            ': wlog "int(sek)"; sekund
if (stund < 10) then stund$="0" + str$(stund) else stund$= str$(stund)
if (minut < 10)  then minut$="0" + str$(minut) else minut$= str$(minut)
if (sekund <10) then sekund$="0"+ str$(sekund) else sekund$=str$(sekund) 
uhrzeiten$= stund$ + ":" + minut$ + ":" + sekund$
mn=0
return


analyseflag1:
anamillis = millis  ' call of sunrise/sunset etc. only every 20th call
if aufruf < 2 then
goto weiter
end if
aufruf=aufruf+1
if aufruf=20 then 
aufruf =0
endif
return




weiter:  ' calculation of sunrise/sunset etc.
sv$="": se$="" :sx$="":saa$ ="": sab$=""  ':sy$="":sz$=""
sv=0: se=0 :sx=0:sy=0:sz=0:saa =0: sab=0
day = 0 : Boll =0: jd = 0: sg = 0 : si = 0: sj = 0: sk = 0: sl = 0: sm = 0: sn = 0: so = 0: sp = 0
sq = 0 : sr = 0: ss = 0: st = 0: su = 0: sv = 0: sw =0: sx = 0: mn=0
sy = 0: sz = 0: saa= 0: sab= 0: sac =0: sad= 0: sae=0 : sah = 0
sx$="" :sy$="" : sonnenauf$="" : sz$="" : sonnenunter$=""
saa$="": sab$="" : hoehe$="" 

wlog  "Bin in Analyse  Aufrufnummer :";aufruf
aufruf=aufruf+1
ana_datum$=date$  'unixdate$(date$)  'datumstart)
ana_uhr$  =time$  'unixtime$(time$)  'datumstart)
'wlog "Zaehler  "; i  '; ana_datum$; "    anauhr$  "; ana_uhr$
latitude =  52.5234    ' Koordinaten zuHause
longitude = 13.4114
breite = latitude
laenge = longitude
jahr=val("20"+right$(ana_datum$,2)) : tag=val(left$(ana_datum$,2)): monat=val(mid$(ana_datum$,4,2))
stunde= val(left$(ana_uhr$, 2)) :Minute= val(mid$(ana_uhr$,4,2)) : sek = val(right$(ana_uhr$,2))
if (stunde < 10) then stunde$="0" + str$(stunde) else stunde$= str$(stunde)
if (minute < 10)  then minute$="0" + str$(minute) else minute$= str$(minute)
if (sek <10) then sek$="0"+str$(sek) else sek$=str$(sek) 
if (monat <10) then monat$="0"+str$(monat) else monat$=str$(monat) 
if (tag <10) then tag$="0"+str$(tag) else tag$=str$(tag) 

datum$=tag$ +"." + monat$ + "." + str$(jahr)
testdatum$= datum$ + "  " + stunde$ + ":" + Minute$ + ":" + sek$
datumeuro$=datum$
LY = 0 ' Ermittlung ob schaltjahr oder nicht 
IF jahr/4 - INT(jahr/4) = 0 AND jahr/100 - INT(jahr/100) < > 0 THEN LY = 1 ' LY=1 = schaltjahr
Schaltjahr=365+ly
ud1$="00/01/"+str$(jahr-2000) : uu1$="00:00:00" 'Stunde 0 am 1. Tag des Jahres
if (tag <10) then utag$="0"+str$(tag) else utag$=str$(tag) 
if (monat <10) then umonat$="0"+str$(monat) else umonat$=str$(monat) 
ud2$=utag$+"/"+ umonat$ + "/" + str$(jahr-2000)
tz_sek1 = dateunix(ud1$)+ timeunix(uu1$) ' sekunden bis 00 uhr am 1.1.des Jahres
tz_sek2 = dateunix(ud2$)+ timeunix(time$)
utage$ = unixdate$(tz_sek2-tz_sek1)
utageszahl= (tz_sek2-tz_sek1)/86400
tageszahl =  tag +(153*monat-162)/5
tageszahl2 = (monat-1)*30.3 + tag - stunde/24

kk= 0.01745329251
kk = Pi/180   'zur Umrechnung von Grad- in Bogenmass

if (monat >3) and (monat<11) then gmt=1 else gmt=0 ' Hier genaue regel ab wann MEZ oder MESZ
n = (Tageszahl-1+((stunde+minute/60)-12)/24)/schaltjahr   'Jahresteil:
n2= (Tageszahl2-1+((stunde+minute/60)-12)/24)/schaltjahr   'Jahresteil:
n3 =(uTageszahl-1+((stunde+minute/60)-12)/24)/schaltjahr   'Jahresteil:


'------------------------------------
se =(stunde*3600 + minute*60 + sek )/86400  'fraktion des Tages
se1= se*1440
'wlog "Min nach Mittern se1 "; se1

mn=se*1440  : gosub change
se$=uhrzeiten$
tagheute= dateunix(ana_datum$)+ timeunix(ana_uhr$) + 3600
exel= tagheute/86400 + 25569
sf = exel+2415018.5+se-ces/24 ' sf ist Julian days following  excel -method vNOAA Solar Calculations
' berechnung des Julianischen Datums
 if monat > 2 then
      year = jahr
      month = monat
  else
      year = jahr-1
      month = monat+12
      endif
  day = tag  + (stunde*3600 + minute*60 + sek - 3600 * CES)/86400  ' inklusive Tagesbruchteil
  Boll = 2 - fix(year/100) + fix(year/400)
' following calculations made of the formulas of NOAA Solar Calculations
jd = fix(365.25*(year+4716)) + fix(30.6001*(month+1)) + day + Boll - 1524.5
sg = (sf-2451545)/36525        ' Julianisches Jahrhundert
si = (280.46646+sg*(36000.76983 + sg*0.0003032)) mod 360   ' Geom Mean Long Sun (deg)
sj = 357.52911+sg*(35999.05029 - 0.0001537*sg)             ' Geom Mean Anom Sun (deg)
sk = 0.016708634-sg*(0.000042037+0.0000001267*sg)          'Eccent Earth Orbit
sl = sin(sj*kk)*(1.914602-sg*(0.004817+0.000014*sg))+sin(2*sJ*kk)*(0.019993-0.000101*sg)+ sin(3*sj*kk)*0.000289  ' Sun Eq of Ctr
sm = si+sl   ' Sun True Long (deg)
sn = sj+sl  ' Sun True Anom (deg)
so = (1.000001018*(1-sK*sK))/(1+sK*COS(kk*sN))  'Sun Rad Vector (AUs)
sp = sM-0.00569-0.00478*SIN(kk*(125.04-1934.136*sG))  'Sun App Long (deg)
sq = 23+(26+((21.448-sG*(46.815+sG*(0.00059-sg*0.001813))))/60)/60  ' Mean Obliq Ecliptic (deg)
sr = sQ+0.00256*COS(kk*(125.04-1934.136*sG))  ' Obliq Corr (deg)
ss = (ATAN(COS(kk*sR)*SIN(kk*sP)/COS(kk*sP)))/kk  'Sun Rt Ascen (deg) 
st = (ASIN(SIN(kk*sr)*SIN(kk*sp)))/kk  ' Sun Declin (deg)
su = TAN(kk*(sR/2))*TAN(kk*(sR/2))  ' var y
sv = 4*(sU*SIN(2*kk*(sI))-2*sK*SIN(kk*(sJ))+4*sK*sU*SIN(kk*(sJ))*COS(2*kk*(sI))-0.5*sU*sU*SIN(4*kk*(sI))-1.25*sK*sK*SIN(2*kk*(sJ)))/kk  'Eq of Time (minutes) 
mn=sv  : gosub change
sv$=uhrzeiten$
sw = (ACOS(COS(kk*(90.833))/(COS(kk*(latitude))*COS(kk*(sT)))-TAN(kk*(latitude))*TAN(kk*(sT))))/kk  'HA Sunrise (deg)  
sx = (720-4*longitude-sV +ces * 60)/1440'  Solar Noon (LST)
mn=sx*1440  : gosub change
sx$=uhrzeiten$
sy = sX-sW*4/1440  ' Sunrise Time (LST)
mn=sy*1440   : gosub change
sy$=uhrzeiten$
sonnenauf$=sy$
sz = sx+sW*4/1440  ' Sunset Time (LST)
mn=sz*1440  : gosub change
sz$=uhrzeiten$
sonnenunter$=sz$
saa= 8*sW  '  Sunlight Duration (minutes)
mn=saa  : gosub change
saa$=uhrzeiten$
'se =(stunde*3600 + minute*60 + sek)/86400  'fraktion des Tages
sab= sE*1440+sV+4*longitude-60*CES  mod 1440  ' True Solar Time (min)
mn=sab  : gosub change
sab$=uhrzeiten$

' berechne sac
if (sAB/4 < 0) then
      sac = sAB/4+180
  else
      sac = sAB/4-180  ' Hour Angle (deg)
endif
sad= (ACOS(SIN(kk*(latitude))*SIN(kk*(sT))+COS(kk*(latitude))*COS(kk*(sT))*COS(kk*(sAC))))/kk ' Solar Zenith Angle (deg) 
sae= 90-sAD  ' Solar Elevation Angle (deg)
hoehe$=str$(sae)
if (sAC>0) then
sah = ACOS(((SIN(kk*(latitude))*COS(kk*(sAD)))-SIN(kk*(sT)))/(COS(kk*(latitude))*SIN(kk*(sAD))))/kk+180 mod 360
else
sah = (540-(ACOS(((SIN(kk*(latitude))*COS(kk*(sAD)))-SIN(kk*(sT)))/(COS(kk*(latitude))*SIN(kk*(sAD)))))/kk )mod 360   ' Solar Azimuth Angle (deg cw from N)
endif



ausgabeastro:
' goto kurzvorende
wlog "------------------------------------------------"
wlog "Testdatum           "; testdatum$
wlog "JD  nach exel        sf "; sf
wlog "JD  nach eigen       jd "; jd
wlog "Jul.Jh               sg ";sg
if ly=0 then schaltjahr$="nein" 
if ly=1 then schaltjahr$=" ja "
wlog "Schaltjahr?             "; schaltjahr$
wlog "----------------------------------------------------"
wlog "Geom Mean Long Sun (deg)              si  "; si
wlog "Geom Mean Anom Sun (deg)              sj  "; sj
wlog "Eccent Earth Orbit                    sk  "; sk
wlog "Sun Eq of Ctr                         sl  "; sl
wlog "Sun True Long (deg)                   sm  "; sm
wlog "Sun True Anom (deg)                   sn  ";sn
wlog "Sun Rad Vector (AUs)                  so  ";so
wlog "Sun App Long (deg)                    sp  ";sp
wlog "Mean Obliq Ecliptic (deg)             sq  ";sq
wlog "Obliq Corr (deg)                      sr  ";sr
wlog "Sun Rt Ascen (deg)                    ss  ";ss
wlog "Sun Declin (deg)                      st  ";st
wlog "var y                                 su  ";su
wlog "Eq of Time (minutes)                  sv  ";sv, "  ";sv$
wlog "Anteil d Tages se                     se  ";se, "  ";se$
wlog "HA Sunrise (deg)                      sw  ";sw
wlog "Solar Noon (LST)                      sx  ";sx, "  ";sx$
wlog "Sunrise Time (LST)                    sy  ";sy, "  ";sy$
wlog "Sunset Time (LST)                     sz  ";sz, "  ";sz$
wlog "Sunlight Duration (minutes)          saa  ";saa, "  ";saa$ 
wlog "True Solar Time (min)                sab  ";sab, "  ";sab$
wlog "Hour Angle (deg)      ooo            sac  "; sac
wlog "Solar Zenith Angle (deg)             sad  ";sad
kurzvorende:
wlog "Solar Elevation Angle (deg)          sae  "; sae
wlog "Solar Azimuth Angle (deg cw from N)  sah  ";sah
anamillis2 = (millis-anamillis)/1000
'wlog "Dauer analyse seit 1. Aufruf  ";anamillis2
endeanalyse:
pausex 5000
return

taster_auswerten  ' from earlier module
tast = PIN(taster)
if tast=0 then tastdruck=1
if tast=1 then tastdruck=1
wlog "Grüne Taste = "; tast;
wlog "  Tastdruck "; tastdruck
pausex 10
return


sub pausex(t)
local x
x=millis + t
while millis < x
wend
end sub




end
You do not have the required permissions to view the files attached to this post.
barneyb
Posts: 15
Joined: Sat Jun 12, 2021 7:00 am
Has thanked: 8 times
Been thanked: 2 times

Re: Contolling solar themal

Post by barneyb »

Hi frank, nice project, in my opinion i would strip it back to basic. potential nested gosubs, interrupts with more than a couple of lines are a recipe for stack heap collisions. I would remove all the solar calcs etc, hysteresis, web display and just put the ldr in a loop with if endif, until it runs for more than days without error.
x=0
loop
if ldr > setpoint
if x ==0
if outside temp > outsidesetpoint
pump on
endif
endif
after running some time
if return temp > input temp
x=1
endif
if return temp < input temp
x-0
endif
endif
you get the idea
also display your temps onto your display, their is a number of times i have seen erratic temp ic outputs
once your happy its ran without flaw the additional code can be added, if not then power transients from pump switching would be the next place to look
i always set variables in select case or interrupt routines then check them in the loop.
hope of some use cheeers barney
User avatar
PeterN
Posts: 366
Joined: Mon Feb 08, 2021 7:56 pm
Location: Krefeld, Germany
Has thanked: 171 times
Been thanked: 203 times
Contact:

Re: Contolling solar themal

Post by PeterN »

Very nice project. My Respect!

I can not point to THE ONE PROBLEM. Can only try to follow your code and look for potential problems.

WLOG: I remember some discussions about problems arising from the excessive use of WLOG. Not sure if this is still relevant in the latest ANNEX-version.
WAIT: You use one endless loop, no timers to call the subs. No WAIT to deal with events (and maybe it gives time for some more things ???)
GOSUB: I see some GOSUBS in multiline statements. There is a remark in the help file to avoid this.
Variable I: Is not used, but endlessly incremented in the main loop - and is additionally used in a for next loop of a sub
RAM: A lot of variables for the limited RAM of the ESP8266. Maybe RAMFREE can give a hint where the RAM gets low.

Ich hoffe das hilft dir etwas weiter :-)
Viel Glück
User avatar
cicciocb
Site Admin
Posts: 1900
Joined: Mon Feb 03, 2020 1:15 pm
Location: Toulouse
Has thanked: 407 times
Been thanked: 1271 times
Contact:

Re: Contolling solar themal

Post by cicciocb »

Hi FranckM,
very nice project, I agree with PeterN about the respect!

I activated the code colouring for your code using the (code=annex)(/code) around the code (using square brackets '[' and ']' )
User avatar
Electroguard
Posts: 836
Joined: Mon Feb 08, 2021 6:22 pm
Has thanked: 268 times
Been thanked: 317 times

Re: Contolling solar themal

Post by Electroguard »

Hi, some debugging thoughts...

I know Pause is blocking, so I'm assuming the reason for you doing your own PauseX while/wend user sub alternative may be that it is not blocking ... but I haven't actually tested to see yet.

The reason I mention it is that interrupts plus continuous polling can cause conflict, because if a non-blocking interrupt handler does not return to its original trigger conditions then it may not be able to return the status quo before being moved on with a new iteration of the loop, thereby leaving things permanently messed up.

So adding an appropriate housekeeping pause at the end of the main loop could offer opportunity for tidying up previous messes before new event triggers can mess things up even worse.
Pause for as long as can be afforded to allow for interrupts being interrupted by other interrupts.
Even safer if all event handlers (anything starting with 'ON...') can branch to a disable subroutine to turn off all interrupts until the current one is handled, then branch to an enable subroutine which turns all interrupts back on again just before returning.


If the end-of-loop pause is added into an extra subdir which is branched to at the end of loop it can also be used to monitor for potential pending problems, such as compare the start of the loop ramfree value with the end of the loop ramfree for flagging up some sort of warning if they differ by more than a set amount.

Also, creeping memory leakage can be flagged every iteration by checking if ramfree drops below a preset threshold level.

In both cases. it offers opportunity to rescue the situation before disaster strikes, by branching to a rescue subroutine which could save important variables before rebooting back up to full strength again.

Additionally, program progress could be monitored using a breadcrumb trail, whereby suspected subroutines set a variable to their subdir name (eg: breadcrumb$ = "this_current subdir"), so in the event of a reboot the saved breadcrumb$ variable would show the offending last subdir which caused the error.
It could be possible for the problem to be caused by an interrupting event handler of course, so don't forget to also add breadcrumb tracking to all event handler subdirs.

When you know which subdir is the culprit, modify it to record its initial ramfree for comparing with its erroring ramfree, and possibly record its variable values, to help to pin down the actual cause of the problem.
Even if you cannot discover the cause, it offers opportunity to completely re-write the offending subdir using different methods to avoid the problem.


Or perhaps rather than waiting for disaster to come knocking, schedule a regular nightly reboot.


If you wish to pin down the problem rather than just avoid it, you could raise the 'out of memory' threshold using Option.Lowram to a safer higher value and define your own OnError routine to trap 'out of memory' errors before they actually cause any problems, allowing opportunity to record and investigate prior to the inevitable eventual reboot.

Impressive effort, BTW.
FrankM
Posts: 6
Joined: Fri Feb 12, 2021 2:26 pm
Has thanked: 1 time
Been thanked: 1 time

Re: Contolling solar themal

Post by FrankM »

Dear Barney,

thank you for your comments!

Some years I had a temperature control only with the two temperatures (roof and basement). This was not very effective, so I made a program (in C) , that looked first if it is bright. If it was so, then the pump was started for about 30 seconds. So the temperature sensor which is about 150 cm distant from the solar thermal collector could 'feel" if the fluid is already warmer than before. My old machine could not do anything with WiFi and collecting the data for later analysis was very difficult.
So I decided to build a new temperature controller with this features. Because I am not very familiar with C++ (Arduino), I found AnnexBasic described in ELEKTOR and I thought, this could help for my project.
So I began last year and did it stepwise as you recommend it.

Meanwhile I have tried how much RAM is free after different subroutines, I have made the website a bit more slim and the free memory sttles dow at approx. 22000 Bytes.
So I will try this for the next days!

Yours sincerely

Frank Martens

[Local Link Removed for Guests] wrote: [Local Link Removed for Guests]Fri Jul 09, 2021 3:15 am Hi frank, nice project, in my opinion i would strip it back to basic. potential nested gosubs, interrupts with more than a couple of lines are a recipe for stack heap collisions. I would remove all the solar calcs etc, hysteresis, web display and just put the ldr in a loop with if endif, until it runs for more than days without error.
x=0
loop
if ldr > setpoint
if x ==0
if outside temp > outsidesetpoint
pump on
endif
endif
after running some time
if return temp > input temp
x=1
endif
if return temp < input temp
x-0
endif
endif
you get the idea
also display your temps onto your display, their is a number of times i have seen erratic temp ic outputs
once your happy its ran without flaw the additional code can be added, if not then power transients from pump switching would be the next place to look
i always set variables in select case or interrupt routines then check them in the loop.
hope of some use cheeers barney
FrankM
Posts: 6
Joined: Fri Feb 12, 2021 2:26 pm
Has thanked: 1 time
Been thanked: 1 time

Re: Contolling solar themal

Post by FrankM »

Dear PeterN,

thank you for your comments!

I know the problems with nested gosubs etc, but this is not the first version of the code (I try intermittently since last year, when AnnexBasic 1.42 was published.
The WLOGs are only for testing purposes, the Wait was exchanged wit the sub pausex(t), which I had found in the forum, but it seemed to do the same as pause xxx
The variable i is used to show the loops on the TFT, the second variable i I have changed to ii.
Your best idea was to look for the remaining memory.
Immediate after the begin of the code there is RAM available - from 32736 to 34064 ( during these test the program code was reduced). RAM decreases after Start to approx. 23500, if I open the website, it decreases during the presentation of the website from 20344 to 15800 immediately after the command 'a$="".
If I am 'clicking" the website every two seconds, the available RAM decreases, and today morning I could induce an 'out of memory'-error. Meanwhile the code for the website is reduced and since some hours the out-of-memory-error did not occur.

Thank you for your ideas - I will post here updates of my experiences!

Frank Martens
[Local Link Removed for Guests] wrote: [Local Link Removed for Guests]Fri Jul 09, 2021 5:34 am Very nice project. My Respect!

I can not point to THE ONE PROBLEM. Can only try to follow your code and look for potential problems.

WLOG: I remember some discussions about problems arising from the excessive use of WLOG. Not sure if this is still relevant in the latest ANNEX-version.
WAIT: You use one endless loop, no timers to call the subs. No WAIT to deal with events (and maybe it gives time for some more things ???)
GOSUB: I see some GOSUBS in multiline statements. There is a remark in the help file to avoid this.
Variable I: Is not used, but endlessly incremented in the main loop - and is additionally used in a for next loop of a sub
RAM: A lot of variables for the limited RAM of the ESP8266. Maybe RAMFREE can give a hint where the RAM gets low.

Ich hoffe das hilft dir etwas weiter :-)
Viel Glück
FrankM
Posts: 6
Joined: Fri Feb 12, 2021 2:26 pm
Has thanked: 1 time
Been thanked: 1 time

Re: Contolling solar themal

Post by FrankM »

Dear Electroguard,

thank you for analyzing my program!

Some ideas I could try today, as recommended by PeterN - see my answer to him (e.g. RAMFREE).
If these changes will not assure a stable operation for days and weeks, then I will try to realize your other recommendations.

Thank you!

Frank Martens
[Local Link Removed for Guests] wrote: [Local Link Removed for Guests]Fri Jul 09, 2021 1:38 pm Hi, some debugging thoughts...

I know Pause is blocking, so I'm assuming the reason for you doing your own PauseX while/wend user sub alternative may be that it is not blocking ... but I haven't actually tested to see yet.

The reason I mention it is that interrupts plus continuous polling can cause conflict, because if a non-blocking interrupt handler does not return to its original trigger conditions then it may not be able to return the status quo before being moved on with a new iteration of the loop, thereby leaving things permanently messed up.

So adding an appropriate housekeeping pause at the end of the main loop could offer opportunity for tidying up previous messes before new event triggers can mess things up even worse.
Pause for as long as can be afforded to allow for interrupts being interrupted by other interrupts.
Even safer if all event handlers (anything starting with 'ON...') can branch to a disable subroutine to turn off all interrupts until the current one is handled, then branch to an enable subroutine which turns all interrupts back on again just before returning.


If the end-of-loop pause is added into an extra subdir which is branched to at the end of loop it can also be used to monitor for potential pending problems, such as compare the start of the loop ramfree value with the end of the loop ramfree for flagging up some sort of warning if they differ by more than a set amount.

Also, creeping memory leakage can be flagged every iteration by checking if ramfree drops below a preset threshold level.

In both cases. it offers opportunity to rescue the situation before disaster strikes, by branching to a rescue subroutine which could save important variables before rebooting back up to full strength again.

Additionally, program progress could be monitored using a breadcrumb trail, whereby suspected subroutines set a variable to their subdir name (eg: breadcrumb$ = "this_current subdir"), so in the event of a reboot the saved breadcrumb$ variable would show the offending last subdir which caused the error.
It could be possible for the problem to be caused by an interrupting event handler of course, so don't forget to also add breadcrumb tracking to all event handler subdirs.

When you know which subdir is the culprit, modify it to record its initial ramfree for comparing with its erroring ramfree, and possibly record its variable values, to help to pin down the actual cause of the problem.
Even if you cannot discover the cause, it offers opportunity to completely re-write the offending subdir using different methods to avoid the problem.


Or perhaps rather than waiting for disaster to come knocking, schedule a regular nightly reboot.


If you wish to pin down the problem rather than just avoid it, you could raise the 'out of memory' threshold using Option.Lowram to a safer higher value and define your own OnError routine to trap 'out of memory' errors before they actually cause any problems, allowing opportunity to record and investigate prior to the inevitable eventual reboot.

Impressive effort, BTW.
Post Reply