├── .gitignore
├── ESP8266WebServer.py
├── README.md
├── Simple_led.py
├── TestWebServer.py
├── index.p.html
├── main.py
├── www
└── index.html
└── www2
├── index.html
├── index.p.html
└── test.css
/.gitignore:
--------------------------------------------------------------------------------
1 | /.idea/
2 |
--------------------------------------------------------------------------------
/ESP8266WebServer.py:
--------------------------------------------------------------------------------
1 | """A simple HTTP server that only accept GET request
2 | It adopt the programming style of ESP8266WebServer
3 | library in ESP8266 Arduino Core
4 | """
5 |
6 | import network
7 | import machine
8 | import socket
9 | import uselect
10 | import os
11 |
12 | server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
13 | server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
14 |
15 | # Use for checking a new client connection
16 | poller = uselect.poll()
17 | # Dict for registed handlers of all paths
18 | handlers = {}
19 | # Function of handler for request not found
20 | notFoundHandler = None
21 | # The path to the web documents on MicroPython filesystem
22 | docPath = "/"
23 | # Data for template
24 | tplData = {}
25 | # Max. POST/PUT-Data size
26 | maxContentLength = 1024
27 |
28 | # MIME types
29 | mimeTypes = {
30 | ".css":"text/css",
31 | ".jpg":"image/jpg",
32 | ".png":"image/png",
33 | }
34 |
35 | def begin(port=80):
36 | """Function to start http server
37 | """
38 | global server, poller
39 | server.bind(('0.0.0.0', port))
40 | server.listen(1)
41 | # Register for checking new client connection
42 | poller.register(server, uselect.POLLIN)
43 |
44 | def close():
45 | """Function to stop http server
46 | """
47 | poller.unregister(server)
48 | server.close()
49 |
50 | def handleClient():
51 | """Check for new client connection and process the request
52 | """
53 | global server, poller
54 | # Note:don't call poll() with 0, that would randomly cause
55 | # reset with "Fatal exception 28(LoadProhibitedCause)" message
56 | res = poller.poll(1)
57 | if res: # There's a new client connection
58 | (socket, sockaddr) = server.accept()
59 | socket.settimeout(0.02) # set timeout for readline to avoid blocking
60 | handle(socket)
61 | socket.close()
62 |
63 | def __sendPage(socket, filePath):
64 | """Send the file as webpage to client
65 | """
66 | try:
67 | f = open(filePath, "rb")
68 | while True:
69 | data = f.read(64)
70 | if (data == b""):
71 | break
72 | socket.write(data)
73 | f.close()
74 | except Exception as e:
75 | print(e)
76 |
77 |
78 | def err(socket, code, message):
79 | """Respong error meesage to client
80 | """
81 | socket.write("HTTP/1.1 " + code + " " + message + "\r\n")
82 | socket.write("Content-Type: text/html\r\n\r\n")
83 | socket.write("
" + message + "
")
84 |
85 | def ok(socket, code, *args):
86 | """Response successful message or webpage to client
87 | """
88 | if len(args)==1:
89 | content_type = "text/plain"
90 | msg = args[0]
91 | elif len(args)==2:
92 | content_type = args[0]
93 | msg = args[1]
94 | else:
95 | raise TypeError("ok() takes 3 or 4 positional arguments but "+ str(len(args)+2) +" were given")
96 | socket.write("HTTP/1.1 " + code + " OK\r\n")
97 | socket.write("Content-Type: " + content_type + "\r\n\r\n")
98 | # if __fileExist(msg):
99 | # filePath = msg
100 | # __sendPage(socket, filePath)
101 | # else:
102 | socket.write(msg)
103 |
104 | def __fileExist(path):
105 | """Check for file existence
106 | """
107 | print(path)
108 | try:
109 | stat = os.stat(path)
110 | # stat[0] bit 15 / 14 -> file/dir
111 | if stat[0] & 0x8000 == 0x8000: # file
112 | print("Found.")
113 | return True
114 | else: # Dir
115 | return False
116 | except:
117 | print("Not Found.")
118 | return False
119 |
120 | def __serveFile(socket, path):
121 | """Serves a file from the filesystem
122 | """
123 | filePath = path
124 | fileFound = True
125 | # find the file
126 | if not __fileExist(filePath):
127 | if not path.endswith("/"):
128 | fileFound = False
129 | else:
130 | filePath = path + "index.html"
131 | # find index.html in the path
132 | if not __fileExist(filePath):
133 | filePath = path + "index.p.html"
134 | # find index.p.html in the path
135 | if not __fileExist(filePath): # no default html file found
136 | fileFound = False
137 | if not fileFound: # file or default html file specified in path not found
138 | if notFoundHandler:
139 | notFoundHandler(socket)
140 | else:
141 | err(socket, "404", "Not Found")
142 | return
143 | # Responds the header first
144 | socket.write("HTTP/1.1 200 OK\r\n")
145 | contentType = "text/html"
146 | for ext in mimeTypes:
147 | if filePath.endswith(ext):
148 | contentType = mimeTypes[ext]
149 | socket.write("Content-Type: " + contentType + "\r\n\r\n")
150 | # Responds the file content
151 | if filePath.endswith(".p.html"):
152 | f = open(filePath, "r")
153 | for l in f:
154 | socket.write(l.format(**tplData))
155 | f.close()
156 | else:
157 | __sendPage(socket, filePath)
158 |
159 | def handle(socket):
160 | """Processing new GET request
161 | """
162 | global docPath, handlers
163 | try: # capture timeout for wainting a line
164 | currLine = str(socket.readline(), 'utf-8')
165 | except:
166 | currLine = "" # readline timeout (not a complete line)
167 | request = currLine.split(" ")
168 | if len(request) != 3: # Discarded if it's a bad header
169 | return
170 | (method, url, version) = request
171 | if "?" in url: # Check if there's query string?
172 | (path, query) = url.split("?", 2)
173 | else:
174 | (path, query) = (url, "")
175 | args = {}
176 | contentType = ""
177 | content = b""
178 | contentLength = 0
179 |
180 | if query: # Parsing the querying string
181 | argPairs = query.split("&")
182 | for argPair in argPairs:
183 | arg = argPair.split("=")
184 | args[arg[0]] = arg[1]
185 |
186 | while True: # Read until blank line after header
187 | header = socket.readline()
188 | if header.startswith(b"Content-Length"):
189 | (key, contentLengthStr) = str(header).split(" ")
190 | contentLength = int(contentLengthStr[0:-5])
191 | if (contentLength > maxContentLength):
192 | err(socket, "400", "Bad Request")
193 | return
194 | if (header.startswith(b"Content-Type")):
195 | (key, contentType) = str(header).split(" ")
196 | contentType = contentType[0:-5]
197 | if (header == b""):
198 | return
199 | if (header == b"\r\n" and contentLength > 0):
200 | while(len(content) < contentLength):
201 | content = content + socket.recv(contentLength)
202 | if (len(content) > maxContentLength):
203 | err(socket, "400", "Bad Request")
204 | return
205 | break
206 | elif header == b"\r\n":
207 | break
208 |
209 | # Check for supported HTTP version
210 | if version != "HTTP/1.0\r\n" and version != "HTTP/1.1\r\n":
211 | err(socket, "505", "Version Not Supported")
212 | elif (method != "GET" and method != "PUT" and method != "POST"): # Only accept GET,PUT and POST request
213 | err(socket, "501", "Not Implemented")
214 | elif path in handlers: # Check for registered path
215 | handlers[path](socket, args, method, contentType, content)
216 | elif not path.startswith(docPath): # Check for wrong path
217 | err(socket, "400", "Bad Request")
218 | else: # find file in the document path
219 | filePath = path
220 | print("Serve File " + filePath)
221 | __serveFile(socket, filePath)
222 |
223 |
224 | def onPath(path, handler):
225 | """Register handler for processing request of specified path
226 | """
227 | global handlers
228 | handlers[path] = handler
229 |
230 | def onNotFound(handler):
231 | """Register handler for processing request of not found path
232 | """
233 | global notFoundHandler
234 | notFoundHandler = handler
235 |
236 | def setDocPath(path):
237 | """Set the path to documents' directory
238 | """
239 | global docPath
240 | docPath = path
241 |
242 | def setTplData(data):
243 | """Set data for template
244 | """
245 | global tplData
246 | tplData = data
247 |
248 | def setMaxContentLength(max):
249 | """Set the maximum content lenpth for incomming data bodies
250 | """
251 | global maxContentLength
252 | maxContentLength = max
253 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | This Library is no longer supported. Visit https://github.com/codemee/ESPWebServer for a version that supports ESP8266 and ESP32.
2 |
3 | ----
4 | # ESP8266WebServer
5 |
6 | This is a very lightweight web server for MicroPython on ESP8266. It only accept GET, POST and PUT requests. It adopts the programming style of ESP8266WebServer library in ESP8266 Arduino Core.This make it suitable for serving REST API.The original code was inspired from the project [Controlling a GPIO through an ESP8266-based web server](https://lab.whitequark.org/notes/2016-10-20/controlling-a-gpio-through-an-esp8266-based-web-server/).
7 |
8 | ## Installation
9 |
10 | Just upload ESP8266WebServer.py to your ESP8266 board and you're done.
11 |
12 | ## Usage
13 |
14 | To use ESP8266WebServer.py library, you should:
15 |
16 | 1. Write functions as handlers for each path you'd like to serve contents.
17 |
18 | 1. Start the server by calling begin().
19 |
20 | 1. Regsiter the handlers you just prepared by calling onPath().
21 |
22 | 1. You can also uploading HTML files onto somewhere in the filesystem and settnig the document path by calling setDocPath().
23 |
24 | 1. Call handleClient() repeatly to process requests.
25 |
26 | ### Documents and Templates
27 |
28 | With setDocPath(), you can spcicified the path for all html files. For examples, if you call setDocPath('www'), and put index.html into /www, you can browse the file with 'http://server_ip/www/index.html.
29 |
30 | If you put a file with suffix '.p.html', the server would do formatting processing before output the content. You should first call setTplData() with a dictionary before accessing any template file, the server uses the elements in the dictionary to replacing all the formatting string in the template file.
31 |
32 | If you access the document path without filename, the server would try to find out if there's a index.html or index.p.html file existed, and output the file.
33 |
34 | ## Function reference
35 |
36 | ### begin(port)
37 |
38 | Start the server at specified *port*.
39 |
40 | ### onPath(path, handler)
41 |
42 | Register *handler* for processing request with matching *path*
43 |
44 | ### setDocPath(path)
45 |
46 | Specified the directory in the filesystem containing all the HTML files.
47 |
48 | ### setTplData(dic)
49 |
50 | Specified the dictionary for template file. `dic` sould be a dictionary with all keys are string and contains all the names in replacing fields in all the template files.
51 |
52 | ### setMaxContentLength(size)
53 |
54 | Defines the maximum Content Length of incoming request bodies (POST, PUT) in bytes. Default: 1024
55 |
56 | ### handleClient()
57 |
58 | Check for new request and call corresponding handler to process it.
59 |
60 | ## Examples
61 |
62 | You can upload www directory and index.p.html to "/" on ESP8266 board and run TestWebServer.py to see how it works.
63 |
64 | `main.py` contains an example for handling Request Body content.
65 |
66 | TestWebServer.py will show its own IP address through serial monitor.Just open your browser and connect it to http://serverIP:8899 or http://serverIP:8899/index.p.html, you'll get the main page that can turn on/off the buildin led on ESP8266 board. The main page also demonstrate the template file usage.
67 |
68 | You can also open http://serverip:8899/www/index.html or http://serverip:8899/www/ to view alternative version of controlling page that use AJAX to asynchronously turn on/off led.
69 |
70 | You can use http://serverip:8899/switch to switch led on/off led directly. Or you can use http://serverip:8899/cmd?led=on to turn the led on and http://serverip:8899/cmd?led=off to turn the led off.
71 |
--------------------------------------------------------------------------------
/Simple_led.py:
--------------------------------------------------------------------------------
1 | import ESP8266WebServer
2 | import network
3 | import machine
4 |
5 | GPIO_NUM = 2 # Builtin led (D4)
6 |
7 | # Wi-Fi configuration
8 | STA_SSID = "MEE_MI"
9 | STA_PSK = "PinkFloyd1969"
10 |
11 | # Disable AP interface
12 | ap_if = network.WLAN(network.AP_IF)
13 | if ap_if.active():
14 | ap_if.active(False)
15 |
16 | # Connect to Wi-Fi if not connected
17 | sta_if = network.WLAN(network.STA_IF)
18 | if not ap_if.active():
19 | sta_if.active(True)
20 | if not sta_if.isconnected():
21 | sta_if.connect(STA_SSID, STA_PSK)
22 | # Wait for connecting to Wi-Fi
23 | while not sta_if.isconnected():
24 | pass
25 |
26 | # Show IP address
27 | print("Server started @", sta_if.ifconfig()[0])
28 |
29 | # Get pin object for controlling builtin LED
30 | pin = machine.Pin(GPIO_NUM, machine.Pin.OUT)
31 | pin.on() # Turn LED off (it use sinking input)
32 |
33 | # Handler for path "/cmd?led=[on|off]"
34 | def handleCmd(socket, args):
35 | global ledData
36 | if 'led' in args:
37 | if args['led'] == 'on':
38 | ledData["status"]="ON"
39 | pin.off()
40 | elif args['led'] == 'off':
41 | ledData["status"]="OFF"
42 | pin.on()
43 | ESP8266WebServer.ok(socket, "200", args["led"])
44 | else:
45 | ESP8266WebServer.err(socket, "400", "Bad Request")
46 |
47 | # Start the server @ port 8899
48 | ESP8266WebServer.begin(8899)
49 |
50 | # Register handler for each path
51 | ESP8266WebServer.onPath("/cmd", handleCmd)
52 | ESP8266WebServer.setDocPath("/www2")
53 |
54 | ledData = {
55 | "status":"Off",
56 | }
57 | ESP8266WebServer.setTplData(ledData)
58 |
59 | try:
60 | while True:
61 | # Let server process requests
62 | ESP8266WebServer.handleClient()
63 | except:
64 | ESP8266WebServer.close()
65 |
--------------------------------------------------------------------------------
/TestWebServer.py:
--------------------------------------------------------------------------------
1 | import ESP8266WebServer
2 | import network
3 | import machine
4 |
5 | GPIO_NUM = 2 # Builtin led (D4)
6 |
7 | # Wi-Fi configuration
8 | STA_SSID = "MEE_MI"
9 | STA_PSK = "PinkFloyd1969"
10 |
11 | # Disable AP interface
12 | ap_if = network.WLAN(network.AP_IF)
13 | if ap_if.active():
14 | ap_if.active(False)
15 |
16 | # Connect to Wi-Fi if not connected
17 | sta_if = network.WLAN(network.STA_IF)
18 | if not ap_if.active():
19 | sta_if.active(True)
20 | if not sta_if.isconnected():
21 | sta_if.connect(STA_SSID, STA_PSK)
22 | # Wait for connecting to Wi-Fi
23 | while not sta_if.isconnected():
24 | pass
25 |
26 | # Show IP address
27 | print("Server started @", sta_if.ifconfig()[0])
28 |
29 | # Get pin object for controlling builtin LED
30 | pin = machine.Pin(GPIO_NUM, machine.Pin.OUT)
31 | pin.on() # Turn LED off (it use sinking input)
32 |
33 | # Dictionary for template file
34 | ledData = {
35 | "title":"Remote LED",
36 | "color":"red",
37 | "status":"Off",
38 | "switch":"on"
39 | }
40 |
41 | # Update information
42 | def updateInfo(socket):
43 | global ledData, color, status, switch
44 | ledData["color"] = "red" if pin.value() else "green"
45 | ledData["status"] = "Off" if pin.value() else "On"
46 | ledData["switch"] = "on" if pin.value() else "off"
47 | ESP8266WebServer.ok(
48 | socket,
49 | "200",
50 | ledData["status"])
51 |
52 | # Handler for path "/cmd?led=[on|off]"
53 | def handleCmd(socket, args):
54 | if 'led' in args:
55 | if args['led'] == 'on':
56 | pin.off()
57 | elif args['led'] == 'off':
58 | pin.on()
59 | updateInfo(socket)
60 | else:
61 | ESP8266WebServer.err(socket, "400", "Bad Request")
62 |
63 | # handler for path "/switch"
64 | def handleSwitch(socket, args):
65 | pin.value(not pin.value()) # Switch back and forth
66 | updateInfo(socket)
67 |
68 | # Start the server @ port 8899
69 | # ESP8266WebServer.begin(8899)
70 | ESP8266WebServer.begin() # use default 80 port
71 |
72 | # Register handler for each path
73 | # ESP8266WebServer.onPath("/", handleRoot)
74 | ESP8266WebServer.onPath("/cmd", handleCmd)
75 | ESP8266WebServer.onPath("/switch", handleSwitch)
76 |
77 | # Setting the path to documents
78 | ESP8266WebServer.setDocPath("/")
79 |
80 | # Setting data for template
81 | ESP8266WebServer.setTplData(ledData)
82 |
83 | try:
84 | while True:
85 | # Let server process requests
86 | ESP8266WebServer.handleClient()
87 | except:
88 | ESP8266WebServer.close()
89 |
--------------------------------------------------------------------------------
/index.p.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {title}
6 |
7 | {title} Status:{status}
8 | Turn {switch}
9 | HOME
10 |
11 |
12 |
19 |
20 |
21 |