CMM2: V5.07.00b11: json support with full ESP-01 example code
Author | Message | ||||
matherp Guru ![]() Joined: 11/12/2012 Location: United KingdomPosts: 8046 |
V5.07.00b11 now available for download http://geoffg.net/Downloads/Maximite/CMM2_Beta.zip I've added a new edit font OPTION EDIT FONT VERY LARGE for use with high resolution modes and changed the horrible font 3 to a different one that is the same size This release also supports a json parsing function. Attached is some code that uses the openweathermap.org api which is called using an ESP-01 with the default "AT" firmware. You need to substitute your own wifi login details and your own openweathermap api username and key in lines 3, 4, and 14 to get it to work. This is code I previously published to work on the Micromite eXtreme that works without a single change on the CMM2 - Isn't compatibility wonderful ![]() You can see from the local report why I have so much time for coding ![]() option explicit option default none Dim MYID$="myID" Dim APIKEY$="myKEY" Dim report$="weather" dim b$= "GET /data/2.5/"+report$+"?id="+MYID$+"&APPID="+APIKEY$ Dim m%(8000) b$=b$+" HTTP/1.0"+Chr$(13)+Chr$(10)+Chr$(13)+Chr$(10) open "com2:115200,8192" as #1 LongString clear m%() print #1,"AT+CWMODE_CUR=1"+chr$(13)+chr$(10); if NOT OKwait(4000) then errorexit("AT+CWMODE_CUR=1") print #1,"AT+CWJAP_CUR="+chr$(34)+"mySSID"+chr$(34)+","+chr$(34)+"myPASSWORD"+chr$(34)+chr$(13)+chr$(10); if NOT OKwait(10000) then errorexit("AT+CWJAP_CUR=") print #1,"AT+CIPSTART="+chr$(34)+"TCP"+chr$(34)+","+chr$(34)+"api.openweathermap.org"+chr$(34)+",80"+chr$(13)+chr$(10); if NOT OKwait(10000) then errorexit("AT+CIPSTART=") print #1,"AT+CIPMODE=1"+chr$(13)+chr$(10); if NOT OKwait(4000) then errorexit("AT+CIPMODE=1") print #1,"AT+CIPSEND"+chr$(13)+chr$(10); if NOT OKwait(4000) then errorexit("AT+CIPSEND") print #1,b$; getinput(10000) Print "Weather for "+json$(m%(),"name") Print "Temperature is ",val(json$(m%(),"main.temp"))-273 Print "Pressure is ",json$(m%(),"main.pressure") Print json$(m%(),"weather[0].description") print #1,"+++"; pause 1000 print #1,"AT+CIPMODE=0"+chr$(13)+chr$(10); if NOT OKwait(4000) then errorexit("AT+CIPMODE=0") print #1,"AT+CIPCLOSE"+chr$(13)+chr$(10); if NOT OKwait(4000) then errorexit("AT+CIPCLOSE") end function OKwait(timeout as integer) as integer OKwait=1 timer=0 do loop until input$(1,1)="O" or timer>=timeout do loop until input$(1,1)="K" or timer>=timeout if timer>=timeout then OKwait=0 end function sub errorexit(calling as string) print "AT call timeout from - ",calling print #1,"AT+CIPCLOSE"+chr$(13)+chr$(10) end end sub sub getinput(waitpause as integer) local a$ local j%,i%=-1 timer=0 do a$=input$(1,1) If a$<>"" Then LongString append m%(),a$ ' print a$; if a$="{" then if i%=-1 then i%=1 else i%=i%+1 endif endif if a$="}" then i%=i%-1 endif loop while timer<=waitpause and i%<>0 LongString trim m%(),LInStr(m%(),"{")-1 print "" end sub Edited 2021-01-31 01:30 by matherp |
||||
chris Regular Member ![]() Joined: 24/08/2020 Location: United KingdomPosts: 54 |
Sounds great. How is it possible for the regular CMM2 to access Wifi/Internet? Is there a guide somewhere, or does it require the special 'deluxe' model? |
||||
matherp Guru ![]() Joined: 11/12/2012 Location: United KingdomPosts: 8046 |
This is for running on a regular CMM2 - just connect a standard ESP-01 to com port 2 and power it from 3.3V/GND (make sure the CH-PD pin is also pulled high) and you have network access ![]() Edited 2021-01-31 03:39 by matherp |
||||
thwill![]() Guru ![]() Joined: 16/09/2019 Location: United KingdomPosts: 3604 |
@matherp Didn't you say we shouldn't drive an ESP-01 from the CMM2's power rails a couple of months back because it can draw too much current under some loads? Tom |
||||
matherp Guru ![]() Joined: 11/12/2012 Location: United KingdomPosts: 8046 |
Seems to test OK but put a 2.2R resistor in the 3.3V line and a 100uF cap across the ESP-01 if any problems ![]() Edited 2021-01-31 03:43 by matherp |
||||
thwill![]() Guru ![]() Joined: 16/09/2019 Location: United KingdomPosts: 3604 |
Did you really mean a 2.2 ohm resistor ... do they come that small, other than 0 ohm SMDs ? Tom |
||||
matherp Guru ![]() Joined: 11/12/2012 Location: United KingdomPosts: 8046 |
Yes 2R2 as used already on the CMM2 in the power supply to the SDcard. Together with the cap just slows the spikes slightly ![]() Edited 2021-01-31 03:54 by matherp |
||||
lew247![]() Guru ![]() Joined: 23/12/2015 Location: United KingdomPosts: 1676 |
Thank you Peter for all your help |
||||
jirsoft![]() Guru ![]() Joined: 18/09/2020 Location: Czech RepublicPosts: 532 |
This is simply perfect! What kind of JSON parser are you using? For this kind of devices is maybe JSMN one of the best... (I saw in the your other thread, that JSON parser is memory hungry, this one isn't and is also fast) |
||||
thwill![]() Guru ![]() Joined: 16/09/2019 Location: United KingdomPosts: 3604 |
Thanks Peter, I've learnt (and ordered) something new. I think providing photographic evidence that such resistors exist was overkill though ![]() Tom |
||||
Rado Regular Member ![]() Joined: 27/11/2020 Location: CroatiaPosts: 59 |
Wow. This opens up CMM2 to the whole new world of real-life data exchange. We just need something to pack data back to JSON and we can communicate with anything (excluding Death Star v1.0) |
||||
TassyJim![]() Guru ![]() Joined: 07/08/2011 Location: AustraliaPosts: 5696 |
I used the 5V supply with a 3.3V regulator for my ESP interface. I also used com1 so that all the pins are together on the expansion port. ![]() Thank you for json, I wasn't looking forward to doing that in Basic. Jim |
||||
lizby Guru ![]() Joined: 17/05/2016 Location: United StatesPosts: 2817 |
Jim--what high-current 3V3 TO-92 regulator did you use? |
||||
TassyJim![]() Guru ![]() Joined: 07/08/2011 Location: AustraliaPosts: 5696 |
MCP1700 |
||||
lizby Guru ![]() Joined: 17/05/2016 Location: United StatesPosts: 2817 |
Ok, I've got some of those--in Box #10, my notes say, if I can find that. I like your arrangement. |
||||
TassyJim![]() Guru ![]() Joined: 07/08/2011 Location: AustraliaPosts: 5696 |
Now I have to work out how to get SSL working. There aren't many websites that don't use https now. As far as I can see, the necessary functions are in the older firmware that fits the 1MB modules. The later firmware has HTTP functions that would make things simpler but I think you need at least 2MB of flash to run it. Jim |
||||
thwill![]() Guru ![]() Joined: 16/09/2019 Location: United KingdomPosts: 3604 |
Thanks for providing a morning's amusement Peter. I found I had to replace: dim b$= "GET /data/2.5/"+report$+"?id="+MYID$+"&APPID="+APIKEY$ with: dim b$= "GET /data/2.5/"+report$+"?q="+MYID$+"&APPID="+APIKEY$ Otherwise it complained that "manchester,uk" was not a valid city, which I thought was rather opinionated for a computer ![]() I've also found my ESP-01 module to not be terribly stable, sometimes it works, but othertimes it only flashes its LED on initial power-up and then will refuse to flash it further or make a connection. It will then stubbornly refuse for a while whilst I faff with the cables and switch things on and off a couple of times, or attach it to an external power supply and then it will decide it will work again after all even when connected back to the CMM2 3.3V power rail ??? Maybe that resistor and capacitor will help when the former arrives. TBH I'm not convinced that the 3.3V rail is always supplying 3.3V ... sometimes I'm measuring as low as 2.8V - though I must acknowledge there may be a certain amount of incompetence involved. Anyway, if we get many more upgrades then I think I'm going to need a bigger breadboard: ![]() Best wishes, Tom |
||||
RetroJoe![]() Senior Member ![]() Joined: 06/08/2020 Location: CanadaPosts: 290 |
At this rate, we're going to need a Maximite Expansion Interface... |
||||
Nimue![]() Guru ![]() Joined: 06/08/2020 Location: United KingdomPosts: 367 |
I remember networking ZX Spectrums the the interface 1 (https://en.wikipedia.org/wiki/ZX_Interface_1 back when I was a wee girl (~15yo) Nim |
||||
matherp Guru ![]() Joined: 11/12/2012 Location: United KingdomPosts: 8046 |
Here is a simple webserver using the ESP-01 in tcp server mode. Just substitute the login for your network in lines 8 and 9 ![]() Option explicit option default integer const MaxLen=4096 const max=20 ' Global variables const quote=chr$(34) CONST SSID$=quote+"mySSID"+quote CONST SSIDPassword$=quote+"myPASSWORD"+quote ' const crlf$=chr$(13)+chr$(10) const header$="<!DOCTYPE html>" const starttext$="HTTP" const pnf$="<html><body>Page not Found</body></html>" const title$="<html><meta charset="+quote+"UTF-8"+quote+"><head><title>Remote Thermostat</title></head><body>" 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=20 '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=1000 'wait time in readrequest before timeout const password$="123456" 'password to access change mode ' const dspin=42 const relaypin=5 ' Dim arg$(2,max) length 20 DIM integer inbuf(Maxlen\8) 'global input buffer dim integer IPDno 'global IPD numbers used to ensure transmit is on correct IPD dim integer heater,currenttemp,maxtemp,mintemp,setpoint dim integer nparams dim mypage$ 'variables used by main loop only DIM integer obuff(2048\8) 'used to buffer writes to the internet to optimise speed Init: tempr 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(tempr(dspin)*10) tempr 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") pause 2000 'wait for ESP8266 to wake up afer power on open "com2:115200,4096" as #1 close #1 'clear the buffer open "com2:115200,4096" as #1 IF NOT Command(0, "AT",obuff(), 500) THEN END IF NOT Command(0, "AT+RST",obuff(), 10000,"WIFI GOT IP") THEN 'reset and check for valid connection PRINT "Set up WIFI on SSID: ",SSID$ IF NOT Command(0, "AT+CWMODE_DEF=1",obuff(), 5000) THEN END 'set device mode (1=client, 2=AP, 3=both) IF NOT Command(0, "AT+CWLAP",obuff(), 30000) THEN END 'scan for WiFi hotspots longstring print inbuf() 'output the list of valid networks IF NOT Command(0, "AT+CWJAP_DEF="+SSID$+","+SSIDPassword$,obuff(), 20000,"WIFI GOT IP") THEN END 'connect IF NOT Command(0, "AT+CIFSR",obuff(), 5000) THEN END 'check IP address ENDIF IF NOT Command(0, "AT+CIPMUX=1",obuff(), 5000) THEN END IF NOT Command(0, "AT+CIPSERVER=1,80",obuff(), 5000) THEN END Print "Connected" main: DO currenttemp=cint(tempr(dspin)*10) updateheater tempr start dspin,3 maxmin 'update the maximum and minimum and save them if changed if ReadRequest(waittime) then 'process HTML request mypage$=ucase$(parsehtmldata$(nparams)) 'parse the html request and get the page requested if mypage$<>"" then 'real request so do something if mypage$= "INDEX" then drawpage elseif mypage$="D" then if nparams<>0 then 'we have parameters to process 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 endif drawpage else SendText pnf$+crlf$+crlf$'invalid page endif EndSend 'clear the write buffer and terminate the write endif endif LOOP end ' Function parsehtmldata$(paramcount as integer) local a$,b$ local integer buf(Maxlen\8) local integer inpos,startparam,processargs paramcount=0 inpos=lInstr(inbuf(),"GET /",1) if inpos=0 then parsehtmldata$="" else longstring mid buf(),inbuf(),inpos+5,1000 inpos=lInstr(buf(),starttext$,1) If inpos>2 Then 'page request found inpos=inpos-2 a$=lGetStr$(buf(),1,inPos) inpos=Instr(a$,"?") If inpos<>0 Then 'parameters found processargs=1 parsehtmldata$=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 parsehtmldata$=a$ EndIf Else ' no page requested parsehtmldata$="INDEX" EndIf endif End Function sub maxmin local integer 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 updateheater local integer 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 drawpage local integer loopcounter,tempconvert local b$ sendText header$ sendText title$+form$+h2$+code$ sendText tstart$+heat$ if heater then sendText hon$+tend$ else sendText hoff$+tend$ endif sendText tstart$ sendText h3$ b$=s1$+outtemp$(currenttemp)+degree$ b$=b$+s2$+outtemp$(maxtemp)+degree$ b$=b$+s3$+outtemp$(mintemp)+degree$ sendText b$ sendText tend$ sendText check$ sendText tstart$ sendText s4$ for loopcounter=asc("A") to asc("L") if loopcounter=setpoint then sendText s5$+chr$(loopcounter) else sendText s6$+chr$(loopcounter) endif sendText click$ tempconvert=loopcounter-asc("B")+starttemp if loopcounter>asc("A") and loopcounter<asc("L") then sendText str$(tempconvert,2)+degree$ if loopcounter=asc("A") then sendText offstr$ if loopcounter=asc("L") then sendText onstr$ next loopcounter sendText tend$ sendText ending$+crlf$+crlf$ end sub ' function outtemp$(t as integer) local integer tenths=t mod 10 local integer units= t\10 outtemp$=str$(units,3)+"."+chr$(tenths+48) end function ' sub EndSend local b$ pause 25 b$="AT+CIPSEND="+CHR$(IPDno+48)+","+STR$(llen(obuff())) 'send anything still in the buffer IF NOT Command(0,b$,obuff(), 500,">") THEN if lInstr(inbuf(),"link is not valid") then exit sub endif IF NOT Command(1, b$, obuff(),500) THEN END pause 25 IF NOT Command(0, "AT+CIPCLOSE="+CHR$(IPDno+48),obuff(), 500) THEN if lInstr(inbuf(),"UNLINK") then longstring clear obuff() exit sub else end endif endif longstring clear obuff() end sub sub SendText(a$) local b$ if len(a$)+llen(obuff())< 2048 then 'add the new string to the output buffer longstring append obuff(),a$ else 'too big to fit so send the current output buffer pause 25 b$="AT+CIPSEND="+CHR$(IPDno+48)+","+STR$(llen(obuff())) IF NOT Command(0,b$,obuff(),1000,">") THEN if lInstr(inbuf(),"link is not valid") then exit sub endif IF NOT Command(1, b$, obuff(),1000) THEN END longstring clear obuff() longstring append obuff(),a$ endif end sub ' FUNCTION ReadRequest(timeout%) as integer ReadRequest=0 longstring Clear inbuf() timer=0 do while TIMER<timeout% and LOC(#1)=0 loop if TIMER>=timeout% then exit function do while loc(#1)<>0 longstring append inbuf(),input$(min(255,loc(#1)),#1) loop if (NOT lInstr(inbuf(),"favicon",0)) and (NOT lInstr(inbuf(),"CONNECT FAIL",1)) then ReadRequest=1 'junk the favicon requests and disconnects IF lInstr(inbuf(),"IPD,0",1) then IPDno=0 IF lInstr(inbuf(),"IPD,1",1) then IPDno=1 end function ' end sub ' FUNCTION Command(mode, AT$, outdata%(), timeout%, other$) as integer 'send a command and wait for the answer local i%=0,j%,ex%=0 Local a$ Command=0 longstring Clear inbuf() if mode=0 then print #1,AT$+crlf$; else longstring print #1,obuff(); endif TIMER=0 i%=0 do if TIMER>timeout% then ex%=0 EXIT DO endif j%=LOC(#1) if j%>0 then i%=i%+min(255,j%) longstring append inbuf(),input$(min(255,j%),#1) if llen(inbuf())>4 and other$="" then if lInstr(inbuf(),"OK",1) then ex%=1 exit DO endif endif if llen(inbuf())>7 then if lInstr(inbuf(),"ERROR",1) then ex%=2 exit DO endif endif if llen(inbuf())>len(other$)+2 then if lInstr(inbuf(),other$,1) then ex%=3 exit DO endif endif endif loop select case ex% case 0 Print "Timeout : "+AT$ case 1 Command=1 case 2 Print "ERROR : "+AT$ case 3 Command=1 end select end function |
||||