ESP8266 POST Server

From ProjectPages
Jump to: navigation, search

Overview

I have added this code because I needed my ESP8266 to serve 'Web Hooks' or HTTP POST Requests.

I searched long and hard, but found no solutions.

So, I started from the simple web server example, linked below, and, after some research, came up with the working example posted here.

The key is that the Header is all nicely terminated with carriage return / line feed combinations, but the Body is not.

The body starts after a blank line terminated by cr/lf.

In the header there is a parameter called Content_Length, and that tells us how many more bytes to expect. We then read() those, as opposed to using readline() for the header lines.

The client then expects to be told the result of the POST

We send "HTTP/1.1 200 OK\r\nContent-Length: 7\r\n\r\nSUCCESS", which tells the client the request was successful, and sends its own BODY content back. The Content-Length: 7 describes the seven bytes that make up the word SUCCESS

This works fine so long as the client is well behaved, but, ideally, there would be much error checking in here so that it can recover from badly formed requests.

Also, the first line, for example POST /post HTTP/1.1\r\n, contains the request type and also the path. This could be parsed out to deal with GET requests, and the path, in this case /post, could be used for determining the type of data we might expect in the Body or to provide some security.

The code as it stands will allow you to use IFTTT to control your ESP8266. I have demonstrated using Amazon Alexa and IFTTT to pass POST requests to a server in my tutorial Controlling_Anything_With_Alexa_And_Raspberry_Pi. There is no reason why you shouldn't use the ESP8266 in place of the Pi.

Code

import socket
port = 8080
addr = socket.getaddrinfo('0.0.0.0', port)[0][-1]

s = socket.socket()
s.bind(addr)
s.listen(1)

print('listening on', addr)

header = True
while True:
	cl, addr = s.accept()
	print('client connected from', addr)
	cl_file = cl.makefile('rwb', 0)
	
	while True:
		if header:
			line = cl_file.readline()
			
			# parse out Content-Length - e.g. b'Content-Length: 24\r\n'
			if b"Content-Length: " in line:
				content_length = int(line[16:len(line)-2])
				print ("content length", content_length)
			
			print("Line:",line)
			# first new line with no content means end of header
			if line == b"\r\n":
				header = False
		else:
			# this is where we collect the body
			# line will contain all the body and will need parsing appropriately
			line = cl_file.read(content_length)
			print("Body:",line)
			break
				
	response = "HTTP/1.1 200 OK\r\nContent-Length: 7\r\n\r\nSUCCESS"
	cl.send(response)
	cl.close()
	# start again
	header = True
		

Links