Home
JAQForum Ver 20.06
Log In or Join  
Active Topics
Local Time 09:36 05 May 2024 Privacy Policy
Jump to

Notice. New forum software under development. It's going to miss a few functions and look a bit ugly for a while, but I'm working on it full time now as the old forum was too unstable. Couple days, all good. If you notice any issues, please contact me.

Forum Index : Microcontroller and PC projects : MM2: Web controlled Thermostat

Author Message
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 8592
Posted: 09:29am 02 Dec 2014
Copy link to clipboard 
Print this post

Another port from Picaxe but something completely different this time, no cFunctions or complex peripherals just ordinary Basic logic.

This is one of a range of web controlled applications I had built to allow remote control of a friend's holiday home.

Try it live on matherp.noip.me, the update code for resetting the max/min or changing the thermostat setting is 123456

The "starttemp" constant defines the range of thermostat values displayed. "Hysteresis" defines how sensitive the output is. A setpoint of 24 and hysteresis of 2 says that the heater will switch off at 24.2 degrees and on at 23.8

I use the TLN13UA06 wifi uart as the web interface (look for "2.4G Wireless Serial Wifi Module Embedded Uart-Wifi Module" on Aliexpress) but it should work on any wired or wifi uart that supports TCPIP server mode. The code does not include set up of the wifi access parameters as I set these up separately and these will vary between different types of modules.

The important module is get gethtmldata$. This parses the html request from the browser, gets rid of all the spurious junk, and returns the web page required and the number of arguments - if any. The arguments themselves are stored in the arg$ array. It has a timeout parameter as an input. This allows control to return to the main program for processing data, in the case of this example reading the temperature and controlling a heater whilst also waiting for user input.

The main loop processes the results of gethtmldata$ and acts as required. The main loop should be implemented as a "SELECT CASE" but I'm having a problem with this. I've reported the problem to Geoff but it is probably me doing something stupid.

The html text is stored as multiple CONST strings. All variable data to be output is done using STR$ with a fixed length. This allows me to pre-calculate the length of the web page and include this in the page header (Content-Length:nnnn). I do this because some of the cheap IP uarts do not support a timeout to trigger the browser to write the screen. Setting an exact length means the browser knows when to display. It is critical for this calculation that all "print #" statements are terminated with ";" so that extra cr lf characters are not sent.

The code does not use any EXIT statements. I really dislike these and they are the most likely area to find firmware bugs or just limitations on use.

The basic framework of this code is infinitely multi-purpose for simple interactive web sites. The slog work is then creating the html and whatever program logic is required for the application.

Obviously there are an infinite ways of skinning this particular cat but I find this way quick and flexible.

Hope this is useful






CPU 40
option explicit
option default integer
option autorun on
'
const max=20
' Global variables
Dim arg$(2,max) length 20
dim crlf$ length 2
dim heater,currenttemp,maxtemp,mintemp,setpoint
'
const starttext$="HTTP"
const pnf$="<html><body>Page not Found</body></html>"
const title$="<html><head><title>Remote Thermostat</title></head><body>"
const content$="Content-Length:"
const ending$="</form></body></html>"
const form$="<form name='f1' method='get' action='D'>"
const h2$="<h2 align='left'>Remote Thermostat V3.0</h2>"
const code$="Update Code: <input type='text' name='S' size='6' value='000000'><br>"
const tstart$="<p><TABLE BORDER='1' CELLSPACING='0' CELLPADDING='5'>"
const heat$="<TR><TD>Heating</TD>"
const hon$="<TD BGCOLOR='#ff0000'> On</TD></TR>"
const hoff$="<TD BGCOLOR='#00ff00'>Off</TD></TR>"
const tend$="</TABLE></p>"
const h3$="<TR><TD></TD><TD>Temperature< ;/TD></TR>"
const s1$="<TR><TD>Current</TD><TD>"
const s2$="<TR><TD>Max</TD><TD>"
const s3$="<TR><TD>Min</TD><TD>"
const check$="<input name='C' type='checkbox' value='R' onClick='this.form.submit()'> Reset Max/Min"
const s4$="<TR><TD>Thermostat</TD>"
const s5$="<TD><input name='R' type='radio' checked='checked' value='"
const s6$="<TD><input name='R' type='radio' value='"
const click$="' onClick='this.form.submit()'> "
const degree$="°C<br></TD>"
const offstr$="Off<br></TD>"
const onstr$="On<br></TD>"
const ON=1
const off=0
'
' Set up parameters
'
const starttemp=18 'set the lowest temperature on the thermostat select radio buttons
const hysteresis=2 ' 2 tenths of a degree either side of the setpoint
const waittime=750 'wait time in gethtmldata$ before timeout
const password$="123456" 'password to access change mode
'
const dspin=26
const relaypin=16
'
init:
dim nparams,i,j,page$ 'variables used by main loop only
ds18B20 start dspin,3 'measure temp to 0.0625 degree accuracy
mintemp=1000 'set to silly value to force reset if not saved
maxtemp=-1000
currenttemp=cint(ds18B20(dspin)*10)
ds18B20 start dspin,3
var restore 'recover the setpoint and min and max temps if already saved
maxmin 'update the maximum and minimum and save them if changed
heater=off
pin(relaypin)=heater
setpin relaypin,dout
setpoint=asc("E")
crlf$=chr$(13)+chr$(10)
Open "com1:38400, 1024" As #1
'
MAIN:
Do
currenttemp=cint(ds18B20(dspin)*10)
updateheater
ds18B20 start dspin,3
maxmin 'update the maximum and minimum and save them if changed
page$=ucase$(gethtmldata$(waittime,nparams))
if page$="" then goto proctemp
if page$="FAVICON.ICO" then goto proctemp 'Do nothing we don't support an icon at the moment
if page$<> "INDEX" then goto notindex
drawpage
goto proctemp
notindex:
if page$<>"D" then goto notD
if nparams<>0 then 'we have parameters to process
endif
if arg$(0,0)="S" and arg$(1,0) = password$ then
if arg$(0,1)="C" and arg$(1,1)="R" then 'reset the max min to current
maxtemp=currenttemp
mintemp=currenttemp
var save setpoint,maxtemp,mintemp
endif
if arg$(0,1)="R" then 'change the setpoint and update the heater if applicable
setpoint=asc(arg$(1,1))
var save setpoint,maxtemp,mintemp
updateheater
endif
endif
drawpage
goto proctemp
notD:
print page$,len(page$)," Invalid page"
j=len(pnf$)
sendheader
print #1,content$+str$(j)+crlf$+crlf$;
print #1,pnf$+crlf$+crlf$;
proctemp:
endif
Loop
end
'
sub drawpage
local loopcounter,tempconvert,charcount
'count the characters in the output, some cheap ip uarts do not implement timeouts correctly
'this approach ensures that the browser knows the transmission is complete
charcount=2+len(title$)+LEN(form$)+len(h2$)+len(code$)+len(h eat$)+len(hon$) 'hoff$ same length as hon$
charcount=charcount+3*(len(tstart$)+len(tend$))+ len(h3$)
charcount=charcount+ len(s1$) + len(s2$) + len(s3$) + 15 + 3*len(degree$) +len(check$) + len(s4$)
charcount=charcount+11*(len(s6$)+1)+len(s5$)+1 + 12*len(click$)
charcount=charcount+10*(len(degree$)+2)+len(offstr$)+len(ons tr$)+len(ending$)
sendheader
print #1,content$+str$(charcount)+crlf$+crlf$;
print #1,title$+form$+h2$+code$;
print #1,tstart$;
print #1,heat$;
if heater then
print #1,hon$;
else
print #1,hoff$;
endif
print #1,tend$;
print #1,tstart$;
print #1,h3$;
print #1,s1$+outtemp$(currenttemp)+degree$;
print #1,s2$+outtemp$(maxtemp)+degree$;
print #1,s3$+outtemp$(mintemp)+degree$
print #1,tend$;
print #1,check$;
print #1,tstart$;
print #1,s4$;
for loopcounter=asc("A") to asc("L")
if loopcounter=setpoint then
print #1,s5$+chr$(loopcounter);
else
print #1,s6$+chr$(loopcounter);
endif
print #1,click$;
tempconvert=loopcounter-asc("B")+starttemp
if loopcounter>asc("A") and loopcounter<asc("L") then print #1,str$(tempconvert,2)+degree$;
if loopcounter=asc("A") then print #1,offstr$;
if loopcounter=asc("L") then print #1,onstr$;
next loopcounter
print #1,tend$;
print #1,ending$+crlf$+crlf$;
end sub
'
function outtemp$(t)
local tenths=t mod 10
local units= t\10
outtemp$=str$(units,3)+"."+chr$(tenths+48)
end function
'
sub updateheater
local hcalc
if setpoint=asc("L") then
pin(relaypin)=ON
heater=on
endif
if setpoint=asc("A") then
pin(relaypin)=OFF
heater=oFF
endif
if setpoint>=asc("B") and setpoint<=asc("K") then
hcalc=(setpoint-asc("B")+starttemp)*10 'setpoint temperature * 10
hcalc = hcalc + hysteresis
if currenttemp>=hcalc then 'turn heating off
pin(relaypin)=OFF
heater=oFF
endif
hcalc = hcalc - hysteresis - hysteresis
if currenttemp<=hcalc then 'turn heating on
pin(relaypin)=ON
heater=oN
endif
endif
end sub
'
sub maxmin
local update=off
if currenttemp>maxtemp then
maxtemp=currenttemp
update=on
endif
if currenttemp<mintemp then
mintemp=currenttemp
update=on
endif
if update then var save setpoint,maxtemp,mintemp
end sub
'
sub sendHeader
print #1,"HTTP/1.1 200 OK"+crlf$;
print #1,"Content-type: text/html"+crlf$;
print #1,"Connection: close"+crlf$;
end sub
'
Function gethtmldata$(timeout,paramcount)
local a$,b$,inpos,startparam,processargs
a$=""
paramcount=0
timer=0
Do While Input$(1,#1)<>"G" and timer<timeout: Loop
if timer>=timeout then
gethtmldata$=""
else
do
loop while loc(#1)<4
a$=input$(4,#1)
if a$<>"ET /" then
gethtmldata$=""
else
pause 50
a$=Input$(250,#1)
inpos=Instr(a$,starttext$)
If inpos>2 Then 'page request found
inpos=inpos-2
a$=Left$(a$,inPos)
Do
b$=Input$(100,#1)
Loop Until b$="" 'clear out any data in the buffer
inpos=Instr(a$,"?")
If inpos<>0 Then 'parameters found
processargs=1
gethtmldata$=Left$(a$,inpos-1)
a$=Mid$(a$,inpos+1)
Do
arg$(0,paramcount)=""
arg$(1,paramcount)=""
inpos=Instr(a$,"=")
startparam=1
arg$(0,paramcount)=Mid$(a$,startparam,inpos-startparam)
startparam=inpos+1
inpos=Instr(a$,"&")
If inpos<>0 Then
arg$(1,paramcount)=Mid$(a$,startparam,inpos-startparam)
a$=Mid$(a$,inpos+1)
paramcount=paramcount+1
Else
arg$(1,paramcount)=Mid$(a$,startparam)
paramcount=paramcount+1
processargs=0
EndIf
Loop while processargs
Else
gethtmldata$=a$
EndIf
Else ' no page requested
gethtmldata$="INDEX"
EndIf
endif
endif
End Function





 
WhiteWizzard
Guru

Joined: 05/04/2013
Location: United Kingdom
Posts: 2794
Posted: 11:31am 02 Dec 2014
Copy link to clipboard 
Print this post

The Code Guru strikes again

You should seriously consider writing a book of code samples - all good stuff you are supplying to this forum.

I just had a quick look at your link - nice and simple

Off to study your code now - Thanks again . . . .
For everything Micromite visit micromite.org

Direct Email: whitewizzard@micromite.o
 
viscomjim
Guru

Joined: 08/01/2014
Location: United States
Posts: 925
Posted: 03:28am 03 Dec 2014
Copy link to clipboard 
Print this post

Matherp, I agree with WW, you should write a book!!! This is great code. Thanks and keep it coming!!!
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 8592
Posted: 03:41am 03 Dec 2014
Copy link to clipboard 
Print this post

Kind words, thanks -pity there is a bug in the code

The statement:

setpoint=asc("E")

in the INIT: section should appear before the "VAR RESTORE" statement so that if there is a saved setpoint it is used after a power up or reset.

Sorry!
Edited by matherp 2014-12-04
 
Print this page


To reply to this topic, you need to log in.

© JAQ Software 2024