![]() |
Forum Index : Microcontroller and PC projects : MM2: webserver with AT mode ESP8266
Author | Message | ||||
matherp Guru ![]() Joined: 11/12/2012 Location: United KingdomPosts: 9992 |
There are lots of versions of firmware for the ESP8266 but the simplest is the basic "AT" firmware provided by the chip maker Expressif. The firmware is included in the SDK (1.5.4.1) and can be flashed with their own flash download tool. Details for how to the flash the chip are in the documentation Although there are easier ways of creating a webserver, using for example ESP8266Basic, the AT approach is easy to understand and seems very reliable (tested on Chrome, Edge and IE11). The attached code is for a simple remote controlled thermostat but contains all the elements needed to set up the ESP8266 from "as-flashed" state and connect it to a router. ![]() HTML requests are particularly verbose so I'm using a few of Geoff's excellent long string CFunctions that he includes in the Micromite distribution to allow strings over 255 characters to be parsed and/or printed The key routines in the code are: Command, SendText and EndSend. Command sends an AT sequence to the ESP8266. It includes a timeout and optional search string. In general AT commands indicate success with "OK" and failure with "ERROR". However, some AT commands report OK before completing execution so the optional search string allows you to wait for something other than "OK" to indicate completion. Command returns 1 if successful and 0 if an error or timeout has occurred. An example of using the function is: [code]IF NOT Command("AT+RST", 10000, "WIFI GOT IP") THEN 'reset and check for valid connection[/code] This resets the ESP8266 and waits for up to 10000 msec for the string "WIFI GOT IP" which indicates that the ESP8266 has connected to your network. If this command fails the attached code includes all the commands needed to connect to the network from scratch SendText does what it says. It sends a text string to the network. Typically this would be some HTML. EndSend flushes the output buffer and sends the completion indicator which allows the page on the browser to complete. In the attached code just change the constants CONST SSID$=quote+"mySSID"+quote CONST SSIDPassword$=quote+"mySSIDpassword"+quote to attach to your router. The code also includes a generic HTML request parser which should be generally useful option autorun on Option explicit option default NONE const MaxLen=4096 const max=20 ' Global variables const quote=chr$(34) CONST SSID$=quote+"mySSID"+quote CONST SSIDPassword$=quote+"mySSIDpassword"+quote ' const starttext$="HTTP" const pnf$="<html><body>Page not Found</body></html>" const title$="<html><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=26 const relaypin=16 ' Dim arg$(2,max) length 20 DIM FLOAT inbuf(Maxlen\4) 'global input buffer dim string CRLF=chr$(13)+chr$(10) length 2 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 page$ 'variables used by main loop only DIM obuff$="" 'used to buffer writes to the internet to optimise speed Init: 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") pause 2000 'wait for ESP8266 to wake up afer power on open "com1:115200,4096" as #1 close #1 'clear the buffer open "com1:115200,4096" as #1 IF NOT Command("AT",500) THEN END IF NOT Command("AT+RST",10000,"WIFI GOT IP") THEN 'reset and check for valid connection PRINT "Set up WIFI on SSID: ",SSID$ IF NOT Command("AT+CWMODE_DEF=1",5000) THEN END 'set device mode (1=client, 2=AP, 3=both) IF NOT Command("AT+CWLAP",30000) THEN END 'scan for WiFi hotspots printlong(inbuf()) 'output the list of valid networks IF NOT Command("AT+CWJAP_DEF="+SSID$+","+SSIDPassword$,20000,"WIFI GOT IP") THEN END 'connect IF NOT Command("AT+CIFSR",5000) THEN END 'check IP address ENDIF IF NOT Command("AT+CIPMUX=1",5000) THEN END IF NOT Command("AT+CIPSERVER=1",5000) THEN END Print "Connected" main: DO currenttemp=cint(ds18B20(dspin)*10) updateheater ds18B20 start dspin,3 maxmin 'update the maximum and minimum and save them if changed if ReadRequest(waittime) then 'process HTML request page$=ucase$(parsehtmldata$(nparams)) 'parse the html request and get the page requested if page$<>"" then 'real request so do something if page$= "INDEX" then drawpage elseif page$="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 FLOAT buf(Maxlen\4) local integer inpos,startparam,processargs paramcount=0 inpos=SInstr(inbuf(),"GET /",1) if inpos=0 then parsehtmldata$="" else SMId buf(),inbuf(),inpos+5 inpos=SInstr(buf(),starttext$,1) If inpos>2 Then 'page request found inpos=inpos-2 a$=SGetStr(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 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 printLONG(ret!()) 'used for debugging local integer i%=Slen(ret!()),j%=1 DO if i%>255 then print SGetStr(ret!(),j%,255); i%=i%-255 j%=j%+255 else PRINT SGetStr(ret!(),j%,i%) i%=0 endif loop while i%>0 end sub sub EndSend local b$ b$="AT+CIPSEND="+CHR$(IPDno+48)+","+STR$(len(obuff$)) 'send anything still in the buffer IF NOT Command(b$,500,">") THEN if SInstr(inbuf(),"link is not valid") then exit sub endif IF NOT Command(obuff$,500) THEN END IF NOT Command("AT+CIPCLOSE="+CHR$(IPDno+48),500) THEN if SInstr(inbuf(),"UNLINK") then obuff$="" exit sub else end endif endif obuff$="" end sub sub SendText(a$) local b$ if len(a$)+len(obuff$)< 240 then 'add the new string to the output buffer obuff$=obuff$+a$ else 'too big to fit so send the current output buffer b$="AT+CIPSEND="+CHR$(IPDno+48)+","+STR$(len(obuff$)) IF NOT Command(b$,500,">") THEN if SInstr(inbuf(),"link is not valid") then exit sub endif IF NOT Command(obuff$,500) THEN END obuff$=a$ endif end sub ' FUNCTION ReadRequest(timeout%) as integer ReadRequest=0 SClear inbuf() timer=0 do while TIMER<timeout% and LOC(#1)=0 loop pause 200 if TIMER>=timeout% then exit function do while loc(#1)<>0 SAddStr inbuf(),input$(min(255,loc(#1)),#1) loop if (NOT SInstr(inbuf(),"favicon",0)) and (NOT SInstr(inbuf(),"CONNECT FAIL",1)) then ReadRequest=1 'junk the favicon requests and disconnects IF SInstr(inbuf(),"IPD,0",1) then IPDno=0 IF SInstr(inbuf(),"IPD,1",1) then IPDno=1 end function ' end sub ' FUNCTION Command(AT$, timeout%, other$) as integer 'send a command and wait for the answer local i%=0,j%,ex%=0 Local a$ Command=0 SClear inbuf() print #1,AT$+crlf; 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%) SAddStr inbuf(),input$(min(255,j%),#1) if Slen(inbuf())>4 and other$="" then if SInstr(inbuf(),"OK",1) then ex%=1 exit DO endif endif if Slen(inbuf())>7 then if SInstr(inbuf(),"ERROR",1) then ex%=2 exit DO endif endif if Slen(inbuf())>len(other$)+2 then if SInstr(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 ' function min(a as integer, b as integer) as integer min=a if(a>=b)then min=b end function CSub SAddStr float, string 00000000 10800011 00000000 10A0000F 00000000 90A30000 8C820000 00623021 1060000A AC860000 24420004 00822021 24A50001 00831821 90A20000 A0820000 24840001 1483FFFC 24A50001 03E00008 00000000 End CSub CFunction SGetStr(float, integer, integer) string 00000000 3C029D00 27BDFFE0 8C420040 AFB00018 AFA50010 AFA60014 AFBF001C 00808021 0040F809 24040100 8FA50010 1200001E 8FA60014 10A0001D 8FBF001C 50C0001C 8FB00018 8CA30000 5060001C A0400000 8E050000 00A3202B 14800017 240400FF 8CC60000 2CC70100 24A50001 00C7200B 00A32823 00A4302B 0086280A 10A0000A A0450000 24630003 02038021 00452821 00401821 92040000 A0640001 24630001 1465FFFC 26100001 8FBF001C 8FB00018 03E00008 27BD0020 A0400000 8FBF001C 8FB00018 03E00008 27BD0020 End CFunction CSub SClear float 00000000 54800001 AC800000 03E00008 00000000 End CSub CSub SMid float, float, integer, integer 00000000 1080001F 00000000 10A0001D 00000000 10C0001B 00000000 50E0001D 3C07000F 8CE70000 8CC20000 50400015 A0800000 8CA60000 00C2182B 14600013 24C60001 00C23023 240300FF 2CC80100 0068300A 00E6182B 00E3300B 10C00009 AC860000 24420003 00A22821 00861821 90A20000 A0820004 24840001 1483FFFC 24A50001 03E00008 00000000 03E00008 A0800000 1000FFE4 24E7423F End CSub CFunction SInstr(float, string, integer) integer 00000000 10C00002 00005021 8CCA0000 50800027 00002021 50A00025 00002021 90A90000 8C8B0000 01695823 016A102B 1440001E 008A2021 10000004 90AC0001 016A102B 14400019 24840001 90820004 544CFFFB 254A0001 51200010 25440001 00801821 10000006 24020001 90670005 91080000 15070006 24630001 00C01021 24460001 0049382B 14E0FFF8 00A64021 5449FFEB 254A0001 25440001 00002821 00801021 03E00008 00A01821 00002021 00002821 00801021 03E00008 00A01821 End CFunction CFunction SLen(float) integer 00000000 10800005 00003821 8C860000 00C01021 03E00008 00E01821 00003021 00C01021 03E00008 00E01821 End CFunction |
||||
nasi Newbie ![]() Joined: 08/07/2015 Location: AustraliaPosts: 16 |
Hey matherp. So that web session I see above is that what you are accessing on the ESP8266 unit. Gee I might skip straight to that instead all these other crazy things I had in mind. It looks alot simpler for remote access of my controlled resources. Might take me a bit to learn those CFunctions though. :) Cool Richard |
||||
matherp Guru ![]() Joined: 11/12/2012 Location: United KingdomPosts: 9992 |
Here is the latest version with a BME280 used for inside temp/pressure/humidity and a DS18B20 used for the outside temperature ![]() I've also connected the reset line on the ESP8266 to the Micromite to hold it in reset while the power stabilises. Code attached 2016-11-08_110612_ATWIFI.zip They act just like normal Basic functions and are fully documented by Geoff in the PDF in the Micromite distribution |
||||
nasi Newbie ![]() Joined: 08/07/2015 Location: AustraliaPosts: 16 |
This is fantastic. Opened up a can a worms for me there friend. I so totally love this stuff. Its exciting Thanks MatherP Richards |
||||
WhiteWizzard Guru ![]() Joined: 05/04/2013 Location: United KingdomPosts: 2925 |
@matherp Excellent code once again from you which I have got my head round (most of it). Not yet tried it out for real so sorry if the following is a 'silly' question: Can you enlighten me as to the functionality of the 'Update Code' input box on the browser end - thanks |
||||
matherp Guru ![]() Joined: 11/12/2012 Location: United KingdomPosts: 9992 |
It is a security feature. Without knowing the 6 digit code you can't reset the max/min temps or change the thermostat setting. In the supplied code it is set to "123456" but should of course be changed before use. |
||||
CaptainBoing![]() Guru ![]() Joined: 07/09/2016 Location: United KingdomPosts: 2137 |
If you are doing WEB stuff, you might find these functions useful. B64is used a lot in HTTP for obfusctaion Base 64 en/decode functions (not entirely my original work but can't find credit - sorry) ================================================================================================== ' needs OPTION BASE 0 CONST Mime$="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" FUNCTION B64Enc$(x$) LOCAL INTEGER c1,c2,c3,w1,w2,w3,w4,n LOCAL STRING SO$ FOR n=1 TO LEN(x$) STEP 3 c1=ASC(MID$(x$,n,1)): c2=ASC(MID$(x$,n+1,1)+CHR$(0)): c3=ASC(MID$(x$,n+2,1)+CHR$(0)): w1=INT(c1/4): w2=(c1 AND 3)*16+INT(c2/16) IF LEN(x$)>=n+1 THEN w3=(c2 AND 15)*4+INT(c3/64) ELSE w3=-1 IF LEN(x$)>=n+2 THEN w4=c3 AND 63 ELSE w4=-1 SO$=SO$+ME$(w1)+ME$(w2)+ME$(w3)+ME$(w4) NEXT B64Enc$=SO$+"==" END FUNCTION FUNCTION B64Dec$(x$) LOCAL INTEGER w1,w2,w3,w4,n LOCAL STRING SO$ FOR n=1 TO LEN(x$) STEP 4 w1=MD(MID$(x$,n,1)): w2=MD(MID$(x$,n+1,1)): w3=MD(MID$(x$,n+2,1)): w4=MD(MID$(x$,n+3,1)) IF w2>=0 THEN SO$=SO$+CHR$(((w1*4+INT(w2/16)) AND 255)) IF w3>=0 THEN SO$=SO$+CHR$(((w2*16+INT(w3/4)) AND 255)) IF w4>=0 THEN SO$=SO$+CHR$(((w3*64+w4) AND 255)) NEXT B64Dec$=SO$ END FUNCTION FUNCTION ME$(i) IF i>=0 THEN ME$=MID$(Mime$,i+1, 1) ELSE ME$="" END FUNCTION FUNCTION MD(x$) IF LEN(x$)=0 THEN MD=-1 ELSE MD=INSTR(Mime$,x$)-1 END FUNCTION Used a lot when passing arguments and web resource names... converst stuff like %20%2e etc to the original characters (this is my original work) URLDecode function ================================= FUNCTION URLDecode$(a$) LOCAL INTEGER z LOCAL STRING b$,c$ z=1 DO z=INSTR(z,a$,"%") IF z=0 OR z=LEN(a$) THEN EXIT DO b$=MID$(a$,z,3) c$=CHR$(VAL("&h"+MID$(b$,2))) a$=Replace$(a$,b$,c$) END IF LOOP URLDecode$=a$ END FUNCTION VB Replace function ================================== FUNCTION Replace$(a$,b$,c$) LOCAL INTEGER z LOCAL STRING x$,y$ z=1 DO z=INSTR(z,a$,b$) IF z=0 THEN EXIT DO x$=LEFT$(a$,z-1) y$=MID$(a$,z+LEN(b$)) a$=x$+c$+y$ z=LEN(x$)+LEN(c$) LOOP Replace$=a$ END FUNCTION I have RC4 en/decryption functions if anyone wants them my 2p |
||||
nasi Newbie ![]() Joined: 08/07/2015 Location: AustraliaPosts: 16 |
Soooooo this would be harder or impossible for me to do on an older maximite with firmware 4.5E? Richards |
||||
greybeard Senior Member ![]() Joined: 04/01/2010 Location: AustraliaPosts: 168 |
Just do it all in the esp8266. You shouldn't need the maximite at all. Plenty of examples out there. |
||||
nasi Newbie ![]() Joined: 08/07/2015 Location: AustraliaPosts: 16 |
Yes I was starting to think that. But I really love Geoff's MMBasic and all this work I have done already would be for nought. But hey I think the coolest think about all of this is their is more than one way to skin a cat. So to speak. Richard |
||||
lizby Guru ![]() Joined: 17/05/2016 Location: United StatesPosts: 3298 |
In trying this very nice piece of software I was stumped for a while with this error while running on an Explore64 with MMBasic 5.3b: Page$=UCase$(parsehtmldata$(nparams)) Error: Invalid syntax I finally figured out that "Page" is now a command. Changing all instances to "pageX" fixed that. Now I have an error sending the html back to the browser. [code] Opening COM3 Reopening COM3 Getting IP: IP: AT+CIFSR +CIFSR:STAIP,"192.168.1.201" Connected nparams: 0: |INDEX| ERROR : AT+CIPSEND=0,173 ERROR : AT+CIPSEND=0,173 ERROR : AT+CIPSEND=0,173 ERROR : AT+CIPSEND=0,173 ERROR : AT+CIPSEND=0,185 ERROR : AT+CIPSEND=0,238 ERROR : AT+CIPSEND=0,238 ERROR : AT+CIPSEND=0,238 ERROR : AT+CIPSEND=0,238 ERROR : AT+CIPSEND=0,238 ERROR : AT+CIPSEND=0,238 ERROR : AT+CIPSEND=0,238 ERROR : AT+CIPSEND=0,238 ERROR : AT+CIPSEND=0,238 ERROR : AT+CIPSEND=0,238 ERROR : AT+CIPSEND=0,238 ... [/code] The first 6 lines plus the line after "Connected" I added for diagnostic purposes. Other than removing the calls to the BME280 (and setting appropriate values for data from it), I didn't make any changes to the parsing code. I'm connecting with a ESP-001. The program says it has connected. In response to my browser request of http://192.168.1.201:1234/INDEX (which the line after "Connected" indicates id being received), I get "The 192.168.1.201 page isn’t working". Anything to try to get past this sending error? PicoMite, Armmite F4, SensorKits, MMBasic Hardware, Games, etc. on fruitoftheshed |
||||
lizby Guru ![]() Joined: 17/05/2016 Location: United StatesPosts: 3298 |
This is perplexing. It works for me with IE, Opera, Safari, but not with FireFox or Chrome. ![]() PicoMite, Armmite F4, SensorKits, MMBasic Hardware, Games, etc. on fruitoftheshed |
||||
lizby Guru ![]() Joined: 17/05/2016 Location: United StatesPosts: 3298 |
Ok, it was a rendering issue. Firefox considered the page to be text, and showed the raw html. Chrome sent back to the ESP8266 that what had been sent to it was invalid, so I got the messages, "ERROR : AT+CIPSEND=0,239". IE, Opera, and Safari figured it out and rendered the raw html appropriately. Adding this line at the beginning of sub drawpage made it so that it works in all 5 browsers: sendText "HTTP/1.1 200 OK"+crlf+"Content-type: text/html"+crlf+"Connection: close"+crlf+crlf PicoMite, Armmite F4, SensorKits, MMBasic Hardware, Games, etc. on fruitoftheshed |
||||
matherp Guru ![]() Joined: 11/12/2012 Location: United KingdomPosts: 9992 |
Looks like they have changed something on Chrome. I did all my development using it and had no issues. Anyway, very well diagnosed ![]() ![]() |
||||
lizby Guru ![]() Joined: 17/05/2016 Location: United StatesPosts: 3298 |
It may well have been a setting or an extension, but I have no idea where to look. Sorted now, for me. PicoMite, Armmite F4, SensorKits, MMBasic Hardware, Games, etc. on fruitoftheshed |
||||
lizby Guru ![]() Joined: 17/05/2016 Location: United StatesPosts: 3298 |
I note your use of these CSUBs and CFUNCTIONs: CSub SAddStr float, string CFunction SGetStr(float, integer, integer) string CSub SClear float CSub SMid float, float, integer, integer CFunction SInstr(float, string, integer) integer CFunction SLen(float) integer They appear to use arrays of float to hold characters (bytes), and these functions operate in a similar manner to the similarly-named string functions. Where are these CSUBs and CFUNCTIONs described? PicoMite, Armmite F4, SensorKits, MMBasic Hardware, Games, etc. on fruitoftheshed |
||||
matherp Guru ![]() Joined: 11/12/2012 Location: United KingdomPosts: 9992 |
These are provided by Geoff in the MM firmware distribution and there is a PDF in there (Embedded C Modules/longstrings.pdf) explaining their use |
||||
lizby Guru ![]() Joined: 17/05/2016 Location: United StatesPosts: 3298 |
Thank you. A very useful set of CSUBs and CFUNCTIONs. And thanks for the ESP8266 code example. Got my own "drawpage" working now. PicoMite, Armmite F4, SensorKits, MMBasic Hardware, Games, etc. on fruitoftheshed |
||||
![]() |