User Tools

Site Tools


mmbasic:esp8266_module_with_http_api

ESP8266 WiFi Module with multi-session HTTP API (pure MMBasic)

This is a complete ESP8266 module that will connect to the WiFi of your choice and then accept connection inbound. No CFunctions or anything, purely handling a serial port and chopping up whatever comes through. It works really well and is fast. It handles multiple inbound connections - up to five at a time. It looks like a big deal but a lot of it is simply functions to make string handling easier (at the bottom).

This might be for a MicroMite based web server or to allow you to set the MicroMite doing things. Here I don't write back HTML (API doesn't require it) but you could just as easily, you could ask for status on bits, analogue inputs. You could send Instructions for quite complex activities, your imagination is the limit.

This is an extract (with changes) from my SMS Gateway. I connect in with a web page and issue commands via this collection to send text message from my website. This bit works perfect, all the time… (the M590 GSM module is more temperamental and I have some work to do before I can publish that bit).

This is enough to get you going, you have to pick the bones apart of the main loop and understand what is happening but it is mainly just detecting the inbound connection and then using string slicing to pick apart the HTTP request to see what is being asked for.

Here's a load of links I used when I was getting this working. Research loads!

https://alselectro.wordpress.com/2015/05/13/wifi_module_esp8266_2_tcp_client_server_mode/
https://www.sparkfun.com/products/13678
http://www.instructables.com/id/Using_the_ESP8266_module/
http://www.instructables.com/id/Using_the_ESP8266_module/step3/Configuring_the_8266_Module/
http://www.instructables.com/id/How_to_Build_a_ESP8266_Web_Server/step7/Results/
https://github.com/esp8266/esp8266_wiki/wiki
http://www.esp8266.com/
http://dominicm.com/esp8266_send_receive_data/
https://www.youtube.com/watch?v=aqjx_85_hwg
https://cdn.sparkfun.com/datasheets/Wireless/WiFi/Command%20Doc.pdf
http://41j.com/blog/2015/01/esp8266_client_mode_connect_remote_host_simple_example/

Enjoy

'
'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

mmbasic/esp8266_module_with_http_api.txt · Last modified: 2024/02/24 17:33 by gerry