' 'ESP8266 Module. Andrew Henderson. October 2016 ' 'Preamble OPTION BASE 0 CONST LAN=2 'S/W Init DIM STRING A$,B$,D$ DIM STRING LNBuf$ DIM STRING LANRxLN$,LANTxLN$,TChr$ LENGTH 1 DIM INTEGER C,Z,TOFg,Chan,Conn(4),PingTMR,PingCtr,RL1TMR LANf DIM FLOAT x 'Starting WLAN Interface Open "COM2:9600,1024" As #LAN ESP8266Reset Main: ' don't do this every time through the loop, set a timer for no more than 60second intervals { 'Test Internet connectivity - you'll have to handle what to do if this is broken, there's nothing here for it. A$=ESP8266Exec$("AT+PING="+PA,4000,0) ' PA=Ping address. try to be nice here and set this to a DNS server from your ISP e.g. 100,200,111,222 or www.bbc.co.uk or something that won't mind you pinging them IF A$="OK" THEN 'good '... ELSE 'internet down '... END IF } 'Web API parsing LANRxLN$=GetIO$(LAN,1) IF LANRxLN$="" OR LANRxLN$="T/O" THEN GOTO Main ' nothing waiting on the input IF MID$(LANRxLN$,2,8)=",CONNECT" THEN ' ESP8266 provides the connect as an indication of HTTP input Chan=VAL(LANRxLN$) IF Chan>=0 AND Chan<=4 THEN Conn(Chan)=1 PRINT "Inbound connect channel "+STR$(Chan) 'Inbound connect channel on channel 0 - 5 GOTO IPEXIT END IF END IF IF MID$(LANRxLN$,2,7)=",CLOSED" THEN ' ESP8266 provides the closed as an indication of HTTP channel closing ' either coz it was explicit or it timed out Chan=VAL(LANRxLN$) IF Chan>=0 AND Chan<=4 THEN IF Conn(Chan)=0 THEN A$=" Abnormally " ELSE A$="" PRINT "Closed channel "+STR$(Chan) + A$ Conn(Chan)=0 GOTO IPEXIT END IF END IF IF LEFT$(LANRxLN$,5)="+IPD," THEN Chan=VAL(MID$(LANRxLN$,6,1)) IF Chan>4 THEN GOTO IPDerrEXIT IF Conn(Chan)<>1 THEN GOTO IPDerrEXIT 'channel not open z=INSTR(LANRxLN$,":GET /<my page, e.g. index.htm>") ' +IPD,0,nnn:GET /index.htm HTTP/1.1 do:a$=GetIO$(LAN,2):loop until a$="Connection: Keep-Alive" ' empty the rest of the envelope, this string is the last part of an HTTP connection header IF z=>8 AND Z<=11 THEN ' checking for the position of the arguments in the string - your mileage will vary... simple sanity check b$=MID$(LANRxLN$,z+13) z=INSTR(b$," HTTP/") IF z<21 THEN GOTO IPDerrEXIT '... and again b$=LEFT$(b$,z-1) ' remove the HTTP bit b$=Replace$(b$,"+"," ")' + to spaces args=Split(b$,"?") ' split the request arguments out IF args <3 THEN GOTO IPDerrEXIT ' your arguments might vary - I have two, the header + two arguments = 3 strings from the spilt FOR n=2 TO args 'args=1 is a dummy SELECT CASE LEFT$(ucase$(SP$(n)),4) ' check for the name of the argument here and assign the value to a variable CASE "VAR1=" var1$=LTrim$(RTrim$(MID$(sp$(n),5))) CASE "VAR2=" var2$=LTrim$(RTrim$(MID$(sp$(n),5))) END SELECT NEXT IF var1$="" OR var2$="" THEN GOTO IPDerrEXIT ' don't allow empty values - your mileage may vary ERASE SP$ Msg$=URLDecode$(Msg$) ' other api calls go here, I only have one in this application END IF GOTO IPDerrEXIT 'default action IF we don't find an API call END IF GOTO IPEXIT IPDerrEXIT: B$="ERROR: Malformed request {"+LANRxLN$+"}" IPOKEXIT: PRINT #LAN,"AT+CIPSENDEX="+STR$(Chan)+",255" ' send our response back to the requestor PAUSE 100 PRINT#LAN,B$+"\0" PAUSE 100 PRINT#LAN,"AT+CIPCLOSE="+STR$(Chan) ' then close the channel nicely Conn(Chan)=0 IPEXIT: GOTO Main END '------------------ ESP8266 WiFi Modem SUBsys ------------- SUB ESP8266Reset LOCAL STRING A$ LOCAL INTEGER N PULSE LANRST,10 ' whatever pin you have connected to the reset of the ESP8266 PAUSE 400 LANExec "ATE0",2,0 LANExec "AT+CWAUTOCONN=0",2,0 ' turn off autoconnect while we initialise LANExec "AT+CWMODE=1",2,0 LANExec "AT+CWQAP",4,0 ' disconnect any existing WiFi connections LANExec "AT+RFVDD",2,0 LANExec "AT+CWJAP="+AP+","+PW,10,1 ' AP= name of the network you want to join e.g. your WIFI SSID - and PW= it's password LANExec "AT+CIPSTA="+IP+","+GW+","+MS,2,0 ' the IP and Gateway address for static IP addresses, rem this line for a static IP LANExec "AT+CIFSR",2,0 LANExec "AT+CWAUTOCONN=1",2,0 ' turn autoconnect back on so if the WiFi drops, the ESP8266 will self negotiate to get it back LANExec "AT+CIPMUX=1",2,0 LANExec "AT+CIPSERVER=1,19090",2,0 ' turn on the server with the port you want your API to listen on, normally HTTP is 80 or sometimes 8080, here I listen on 19090... you can use anything from 1 - 65536 but as a guideline; avoid anything below 1000 if you are not using 80 LANTxLN$="":LANRxLN$="" ' buffers for receive and transmit FOR N=0 TO 4:Conn(N)=0:NEXT ' clear the channel connection flags END SUB SUB LANExec(CM$,T,Suppress) 'temp - will go when we have better handling of the startup LOCAL STRING A$ A$=ESP8266Exec$(CM$,T,Suppress) IF Suppress<2 THEN CLog A$,0 END SUB FUNCTION ESP8266Exec$(Com$,TimeOut,Obf) LOCAL STRING A$ LENGTH 1,LN$ LOCAL INTEGER C PRINT #LAN,Com$ DO LN$=GetIO$(LAN,TimeOut) IF LN$="T/O" THEN ESP8266Exec$="T/O":EXIT DO IF LN$<>"" THEN IF Obf<2 THEN CLog "["+LN$+"]",0 IF LN$="OK" OR LN$=">" OR LN$="SEND OK" THEN ESP8266Exec$=LN$:EXIT DO IF INSTR(LN$,"ERROR") THEN ESP8266Exec$="ERROR ["+LNBuf$+"]":EXIT DO END IF LOOP END FUNCTION '---------------------------------------------------- FUNCTION GetIO$(io,TimeOut) LOCAL STRING a$,b$ LOCAL INTEGER c StartTOTimer TimeOut DO IF TOFg=1 THEN GetIO$="T/O":EXIT DO IF LOC(#io)<>0 THEN A$=INPUT$(1,#io):C=ASC(A$) SELECT CASE C CASE 13 GetIO$=b$:EXIT DO CASE 0 TO 31 CASE ELSE IF LEN(b$)+LEN(a$)<255 THEN b$=b$+a$ ELSE b$=a$ END SELECT END IF LOOP ClearTOTimer END FUNCTION SUB FlushIO(stream) LOCAL STRING A$ DO WHILE LOC(#stream)<>0 PAUSE 50:A$=INPUT$(LOC(#stream),#stream):PAUSE 50 LOOP END SUB 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 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$) LOOP URLDecode$=a$ END FUNCTION FUNCTION LTrim$(a$) LOCAL INTEGER m FOR m=1 TO LEN(a$) IF not(MID$(a$,m,1)=" " OR MID$(a$,m,1)=CHR$(9)) THEN LTrim$=MID$(a$,m):EXIT FUNCTION NEXT LTrim$="" END FUNCTION FUNCTION RTrim$(a$) LOCAL INTEGER n FOR n=LEN(a$) TO 1 STEP -1 IF not(MID$(a$,n,1)=" " OR MID$(a$,n,1)=CHR$(9)) THEN RTrim$=LEFT$(a$,n):EXIT FUNCTION NEXT RTrim$="" END FUNCTION FUNCTION Split(a$,b$) LOCAL INTEGER z,n,m ON ERROR SKIP ERASE SP$ z=1:n=0 DO z=INSTR(z,a$,b$) IF z=0 THEN IF n=0 THEN DIM SP$(1):SP$(1)=a$:Split=1:EXIT FUNCTION ELSE EXIT DO END IF ELSE n=n+1:z=z+LEN(b$) END IF LOOP m=n+1:n=1 DIM SP$(m) DO z=INSTR(1,a$,b$) IF z=0 THEN SP$(m)=a$:EXIT DO ELSE SP$(n)=LEFT$(a$,z-1):a$=MID$(a$,z+LEN(b$)):n=n+1 END IF LOOP Split=m END FUNCTION