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 KingdomPosts: 8592 |
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 KingdomPosts: 2794 |
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 StatesPosts: 925 |
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 KingdomPosts: 8592 |
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! |
||||
Print this page |