this isn't working right now; refactored and drastically minified microWebSrv, unfortunately on an esp8266 it won't import due to low memory. unsure why, will work out another day.

This commit is contained in:
Maff 2018-05-31 23:43:31 +01:00
parent fd173c0f73
commit 7ed40e46a7
4 changed files with 93 additions and 540 deletions

View File

@ -1,37 +1,3 @@
# Authors: Paul Cunnane 2016, Peter Dahlebrg 2016
#
# This module borrows from the Adafruit BME280 Python library. Original
# Copyright notices are reproduced below.
#
# Those libraries were written for the Raspberry Pi. This modification is
# intended for the MicroPython and esp8266 boards.
#
# Copyright (c) 2014 Adafruit Industries
# Author: Tony DiCola
#
# Based on the BMP280 driver with BME280 changes provided by
# David J Taylor, Edinburgh (www.satsignal.eu)
#
# Based on Adafruit_I2C.py created by Kevin Townsend.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
import time
from ustruct import unpack, unpack_from
from array import array
@ -51,7 +17,6 @@ BME280_REGISTER_CONTROL = 0xF4
class BME280:
def __init__(self,
mode=BME280_OSAMPLE_1,
address=BME280_I2CADDR,

13
boot.py
View File

@ -1,12 +1,7 @@
# This file is executed on every boot (including wake-boot from deepsleep)
#import esp
#esp.osdebug(None)
import network
from network import WLAN as wlan, STA_IF as staif, AP_IF as apif
import time
from time import sleep_ms
time.sleep_ms(100)
sleep_ms(100)
#make sure AP mode is off and station mode is on
if wlan(apif).active() == True:
wlan(apif).active(False)
@ -30,7 +25,7 @@ if wlan(staif).isconnected() != True:
break
if found_net == False:
print("couldn't connect to wifi, nets found: %s" % nets)
print("to fix temporarily, run: from network import STA_IF,WLAN as wlan;wlan(STA_IF).connect('yournet','yourpass'), followed shortly after by wlan(STA_IF).ifconfig()")
print("to fix temporarily, run: wlan(staif).connect('yournet','yourpass'), followed shortly after by wlan(staif).ifconfig()")
#exit
else:
#Loop until wifi is connected and passes DHCP, or until 30 seconds have elapsed.
@ -38,7 +33,7 @@ if wlan(staif).isconnected() != True:
while wlan(staif).ifconfig()[0] == '0.0.0.0' or wlan(staif).isconnected() == False:
if slept > 300:
break
time.sleep_ms(100)
sleep_ms(100)
slept+=1
#import webrepl

38
main.py
View File

@ -6,58 +6,48 @@ hw = uPyConfig.esp8266(variant='d1-r2')
#init_sample.init_sample(hw)
# Main app
import uPySensor
sensors={
'bme280': uPySensor.BME280(hw.i2c.bus),
'lm75a': uPySensor.LM75A(hw.i2c.bus),
'sht21': uPySensor.SHT21(hw.i2c.bus),
}
from microWebSrv import MicroWebSrv
ws = MicroWebSrv()
ws.WebSocketThreaded = False
wshead={
'Server':'horny',
}
wsctype='application/json'
wscharset='UTF-8'
import uPySensor
bme280=uPySensor.BME280(hw.i2c.bus)
lm75a =uPySensor.LM75A(hw.i2c.bus)
sht21 =uPySensor.SHT21(hw.i2c.bus)
from microWebSrv import MicroWebSrv
@MicroWebSrv.route('/')
def get_root(wscl, wsres):
wsres.WriteResponseOk(
headers=wshead,
contentType=wsctype,
contentCharset=wscharset,
content='{"result":"error","message":"use /bme280, /lm75a or /sht21 for sensor readings"}'
)
@MicroWebSrv.route('/bme280')
def get_bme280(wscl, wsres):
sensors['bme280'].update_sensor()
json = '{"temperature":"%0.2f","humidity":"%0.2f","pressure":"%0.2f"}' % (
sensors['bme280'].temperature, sensors['bme280'].humidity, sensors['bme280'].pressure)
bme280.update_sensor()
wsres.WriteResponseOk(
headers=wshead,
contentType=wsctype,
contentCharset=wscharset,
content=None
content='{"temperature":"%0.2f","humidity":"%0.2f","pressure":"%0.2f"}' % (
bme280.temperature, bme280.humidity, bme280.pressure)
)
@MicroWebSrv.route('/lm75a')
def get_lm75a(wscl, wsres):
json = '{"temperature":"%0.1f"}' % sensors['lm75a'].read_tempC()
wsres.WriteResponseOk(
headers=wshead,
contentType=wsctype,
contentCharset=wscharset,
content=json
content='{"temperature":"%0.1f"}' % lm75a.read_tempC()
)
@MicroWebSrv.route('/sht21')
def sht21(wscl, wsres):
json = '{"temperature":"%0.3f","humidity":"%0.3f"}' % (sensors['sht21'].read_tempC(), sensors['sht21'].read_hum())
wsres.WriteResponseOk(
headers=wshead,
contentType=wsctype,
contentCharset=wscharset,
content=json
content='{"temperature":"%0.3f","humidity":"%0.3f"}' % (sht21.read_tempC(), sht21.read_hum())
)
ws.Start(threaded=False, stackSize=8192)
ws = MicroWebSrv()
ws.Start()

View File

@ -1,17 +1,5 @@
"""
The MIT License (MIT)
Copyright © 2018 Jean-Christophe Bos & HC² (www.hc2.fr)
Copyright © 2018 LoBo (https://github.com/loboris/MicroPython_ESP32_psRAM_LoBo)
"""
from json import loads, dumps
from os import stat
import _thread
import network
import time
import socket
import gc
import re
from ure import compile
class MicroWebSrvRoute:
def __init__(self, route, method, func, routeArgNames, routeRegex):
@ -21,12 +9,7 @@ class MicroWebSrvRoute:
self.routeArgNames = routeArgNames
self.routeRegex = routeRegex
class MicroWebSrv:
# ============================================================================
# ===( Constants )============================================================
# ============================================================================
_html_escape_chars = {
"&": "&",
'"': """,
@ -34,62 +17,29 @@ class MicroWebSrv:
">": ">",
"<": "&lt;"
}
# ============================================================================
# ===( Class globals )=======================================================
# ============================================================================
_docoratedRouteHandlers = []
# ============================================================================
# ===( Utils )===============================================================
# ============================================================================
_decoratedRouteHandlers = []
@classmethod
def route(cls, url, method='GET'):
""" Adds a route handler function to the routing list """
def route_decorator(func):
item = (url, method, func)
cls._docoratedRouteHandlers.append(item)
cls._decoratedRouteHandlers.append(item)
return func
return route_decorator
# ----------------------------------------------------------------------------
@staticmethod
def HTMLEscape(s):
return ''.join(MicroWebSrv._html_escape_chars.get(c, c) for c in s)
# ----------------------------------------------------------------------------
@staticmethod
def _tryAllocByteArray(size):
for x in range(10):
try:
gc.collect()
return bytearray(size)
except:
pass
return None
# ----------------------------------------------------------------------------
@staticmethod
def _tryStartThread(func, args=(), stacksize=8192):
_ = _thread.stack_size(stacksize)
for x in range(10):
try:
gc.collect()
th = _thread.start_new_thread("MicroWebServer", func, args)
return th
except:
time.sleep_ms(100)
return False
# ----------------------------------------------------------------------------
@staticmethod
def _unquote(s):
r = s.split('%')
@ -101,33 +51,22 @@ class MicroWebSrv:
r[i] = '%' + s
return ''.join(r)
# ----------------------------------------------------------------------------
@staticmethod
def _unquote_plus(s):
return MicroWebSrv._unquote(s.replace('+', ' '))
# ============================================================================
# ===( Constructor )==========================================================
# ============================================================================
def __init__(self,
routeHandlers=[],
port=80,
bindIP='0.0.0.0'):
self._srvAddr = (bindIP, port)
self._notFoundUrl = None
self._started = False
self.thID = None
self.isThreaded = False
self._state = "Stopped"
self._routeHandlers = []
routeHandlers += self._docoratedRouteHandlers
routeHandlers += self._decoratedRouteHandlers
for route, method, func in routeHandlers:
routeParts = route.split('/')
# -> ['', 'users', '<uID>', 'addresses', '<addrID>', 'test', '<anotherID>']
routeArgNames = []
routeRegex = ''
for s in routeParts:
@ -137,53 +76,23 @@ class MicroWebSrv:
elif s:
routeRegex += '/' + s
routeRegex += '$'
# -> '/users/(\w*)/addresses/(\w*)/test/(\w*)$'
routeRegex = re.compile(routeRegex)
routeRegex = compile(routeRegex)
self._routeHandlers.append(MicroWebSrvRoute(route, method, func, routeArgNames, routeRegex))
# ============================================================================
# ===( Server Process )=======================================================
# ============================================================================
def _serverProcess(self):
self._started = True
self._state = "Running"
while True:
try:
client, cliAddr = self._server.accepted()
if client == None:
if self.isThreaded:
notify = _thread.getnotification()
if notify == _thread.EXIT:
break
elif notify == _thread.SUSPEND:
self._state = "Suspended"
while _thread.wait() != _thread.RESUME:
pass
self._state = "Running"
# gc.collect()
time.sleep_ms(2)
continue
if client == None: continue
except Exception as e:
if not self.isThreaded:
print(e)
break
self._client(self, client, cliAddr)
self._started = False
self._state = "Stopped"
self.thID = None
# ============================================================================
# ===( Functions )============================================================
# ============================================================================
def Start(self, threaded=True, stackSize=8192):
if not self._started:
if not network.WLAN().wifiactive():
print("WLAN not connected!")
return
gc.collect()
def Start(self):
if self._started: return
self._server = socket.socket(socket.AF_INET,
socket.SOCK_STREAM,
socket.IPPROTO_TCP)
@ -192,78 +101,34 @@ class MicroWebSrv:
1)
self._server.bind(self._srvAddr)
self._server.listen(1)
self.isThreaded = threaded
# using non-blocking socket
self._server.settimeout(0.5)
if threaded:
th = MicroWebSrv._tryStartThread(self._serverProcess, stacksize=stackSize)
if th:
self.thID = th
else:
self._serverProcess()
# ----------------------------------------------------------------------------
def Stop(self):
if self._started:
if not self._started: return
self._server.close()
if self.isThreaded:
_ = _thread.notify(self.thID, _thread.EXIT)
# ----------------------------------------------------------------------------
def IsStarted(self):
return self._started
# ----------------------------------------------------------------------------
def threadID(self):
return self.thID
# ----------------------------------------------------------------------------
def State(self):
return self._state
# ----------------------------------------------------------------------------
def SetNotFoundPageUrl(self, url=None):
self._notFoundUrl = url
# ----------------------------------------------------------------------------
def GetRouteHandler(self, resUrl, method):
if self._routeHandlers:
# resUrl = resUrl.upper()
if not self._routeHandlers:
return (None, None)
if resUrl.endswith('/'):
resUrl = resUrl[:-1]
method = method.upper()
for rh in self._routeHandlers:
if rh.method == method:
m = rh.routeRegex.match(resUrl)
if m: # found matching route?
if rh.routeArgNames:
if m and rh.routeArgNames:
routeArgs = {}
for i, name in enumerate(rh.routeArgNames):
value = m.group(i + 1)
try:
value = int(value)
except:
pass
try: value = int(value)
except: pass
routeArgs[name] = value
return (rh.func, routeArgs)
else:
return (rh.func, None)
elif m: return (rh.func, None)
return (None, None)
# ============================================================================
# ===( Class Client )========================================================
# ============================================================================
class _client:
# ------------------------------------------------------------------------
def __init__(self, microWebSrv, socket, addr):
socket.settimeout(2)
self._microWebSrv = microWebSrv
@ -281,49 +146,31 @@ class MicroWebSrv:
self._processRequest()
# ------------------------------------------------------------------------
def _processRequest(self):
try:
response = MicroWebSrv._response(self)
if self._parseFirstLine(response):
if self._parseHeader(response):
upg = self._getConnUpgrade()
if not upg:
if self._parseFirstLine(response) and self._parseHeader(response):
routeHandler, routeArgs = self._microWebSrv.GetRouteHandler(self._resPath, self._method)
if routeHandler:
if routeArgs is not None:
if routeHandler and routeArgs is not None:
routeHandler(self, response, routeArgs)
else:
elif routeHandler:
routeHandler(self, response)
elif self._method.upper() == "GET":
response.WriteResponseNotFound()
else:
response.WriteResponseMethodNotAllowed()
else:
response.WriteResponseNotImplemented()
else:
response.WriteResponseBadRequest()
except:
response.WriteResponseInternalServerError()
try:
self._socket.close()
except:
pass
# ------------------------------------------------------------------------
else: response.WriteResponseNotFound()
except: response.WriteResponseInternalServerError()
try: self._socket.close()
except: pass
def _parseFirstLine(self, response):
try:
elements = self._socket.readline().decode().strip().split()
if len(elements) == 3:
if len(elements) != 3: return False
self._method = elements[0].upper()
self._path = elements[1]
self._httpVer = elements[2].upper()
elements = self._path.split('?', 1)
if len(elements) > 0:
if len(elements) < 1: return True
self._resPath = MicroWebSrv._unquote_plus(elements[0])
if len(elements) > 1:
if len(elements) < 2: return True
self._queryString = elements[1]
elements = self._queryString.split('&')
for s in elements:
@ -336,8 +183,6 @@ class MicroWebSrv:
pass
return False
# ------------------------------------------------------------------------
def _parseHeader(self, response):
while True:
elements = self._socket.readline().decode().strip().split(':', 1)
@ -351,75 +196,6 @@ class MicroWebSrv:
else:
return False
# ------------------------------------------------------------------------
def _getConnUpgrade(self):
if 'upgrade' in self._headers.get('Connection', '').lower():
return self._headers.get('Upgrade', '').lower()
return None
# ------------------------------------------------------------------------
def GetServer(self):
return self._microWebSrv
# ------------------------------------------------------------------------
def GetAddr(self):
return self._addr
# ------------------------------------------------------------------------
def GetIPAddr(self):
return self._addr[0]
# ------------------------------------------------------------------------
def GetPort(self):
return self._addr[1]
# ------------------------------------------------------------------------
def GetRequestMethod(self):
return self._method
# ------------------------------------------------------------------------
def GetRequestTotalPath(self):
return self._path
# ------------------------------------------------------------------------
def GetRequestPath(self):
return self._resPath
# ------------------------------------------------------------------------
def GetRequestQueryString(self):
return self._queryString
# ------------------------------------------------------------------------
def GetRequestQueryParams(self):
return self._queryParams
# ------------------------------------------------------------------------
def GetRequestHeaders(self):
return self._headers
# ------------------------------------------------------------------------
def GetRequestContentType(self):
return self._contentType
# ------------------------------------------------------------------------
def GetRequestContentLength(self):
return self._contentLength
# ------------------------------------------------------------------------
def ReadRequestContent(self, size=None):
self._socket.setblocking(False)
b = None
@ -433,59 +209,34 @@ class MicroWebSrv:
self._socket.setblocking(True)
return b if b else b''
# ------------------------------------------------------------------------
def ReadRequestPostedFormData(self):
res = {}
data = self.ReadRequestContent()
if len(data) > 0:
if len(data) < 1: return res
elements = data.decode().split('&')
for s in elements:
param = s.split('=', 1)
if len(param) > 0:
if len(param) < 1: next
value = MicroWebSrv._unquote(param[1]) if len(param) > 1 else ''
res[MicroWebSrv._unquote(param[0])] = value
return res
# ------------------------------------------------------------------------
def ReadRequestContentAsJSON(self):
try:
return loads(self.ReadRequestContent())
except:
return None
# ============================================================================
# ===( Class Response )======================================================
# ============================================================================
class _response:
# ------------------------------------------------------------------------
def __init__(self, client):
self._client = client
# ------------------------------------------------------------------------
def _write(self, data):
if type(data) == str:
data = data.encode()
return self._client._socket.write(data)
# ------------------------------------------------------------------------
def _writeFirstLine(self, code):
reason = self._responseCodes.get(code, ('Unknown reason',))[0]
self._write("HTTP/1.1 %s %s\r\n" % (code, reason))
# ------------------------------------------------------------------------
def _writeHeader(self, name, value):
self._write("%s: %s\r\n" % (name, value))
# ------------------------------------------------------------------------
def _writeContentTypeHeader(self, contentType, charset=None):
if contentType:
ct = contentType \
@ -494,18 +245,12 @@ class MicroWebSrv:
ct = "application/octet-stream"
self._writeHeader("Content-Type", ct)
# ------------------------------------------------------------------------
def _writeServerHeader(self):
self._writeHeader("Server", "MicroWebSrv by JC`zic")
# ------------------------------------------------------------------------
self._writeHeader("Server", "MicroWebSrv lite")
def _writeEndHeader(self):
self._write("\r\n")
# ------------------------------------------------------------------------
def _writeBeforeContent(self, code, headers, contentType, contentCharset, contentLength):
self._writeFirstLine(code)
if isinstance(headers, dict):
@ -518,20 +263,6 @@ class MicroWebSrv:
self._writeHeader("Connection", "close")
self._writeEndHeader()
# ------------------------------------------------------------------------
def WriteSwitchProto(self, upgrade, headers=None):
self._writeFirstLine(101)
self._writeHeader("Connection", "Upgrade")
self._writeHeader("Upgrade", upgrade)
if isinstance(headers, dict):
for header in headers:
self._writeHeader(header, headers[header])
self._writeServerHeader()
self._writeEndHeader()
# ------------------------------------------------------------------------
def WriteResponse(self, code, headers, contentType, contentCharset, content):
try:
contentLength = len(content) if content else 0
@ -542,175 +273,47 @@ class MicroWebSrv:
except:
return False
# ------------------------------------------------------------------------
def WriteResponseOk(self, headers=None, contentType=None, contentCharset=None, content=None):
return self.WriteResponse(200, headers, contentType, contentCharset, content)
# ------------------------------------------------------------------------
def WriteResponseJSONOk(self, obj=None, headers=None):
return self.WriteResponse(200, headers, "application/json", "UTF-8", dumps(obj))
# ------------------------------------------------------------------------
def WriteResponseRedirect(self, location):
headers = {"Location": location}
return self.WriteResponse(302, headers, None, None, None)
# ------------------------------------------------------------------------
def WriteResponseError(self, code):
responseCode = self._responseCodes.get(code, ('Unknown reason', ''))
return self.WriteResponse(code,
None,
"text/html",
"UTF-8",
self._errCtnTmpl % {
'code': code,
'reason': responseCode[0],
'message': responseCode[1]
})
# ------------------------------------------------------------------------
def WriteResponseJSONError(self, code, obj=None):
return self.WriteResponse(code,
None,
"application/json",
"UTF-8",
dumps(obj if obj else {}))
# ------------------------------------------------------------------------
'{"code":"%s","reason":"%s","message":"%s"}' % (
code, responseCode[0], responseCode[1]))
def WriteResponseBadRequest(self):
return self.WriteResponseError(400)
# ------------------------------------------------------------------------
def WriteResponseForbidden(self):
return self.WriteResponseError(403)
# ------------------------------------------------------------------------
def WriteResponseNotFound(self):
if self._client._microWebSrv._notFoundUrl:
self.WriteResponseRedirect(self._client._microWebSrv._notFoundUrl)
else:
return self.WriteResponseError(404)
# ------------------------------------------------------------------------
def WriteResponseMethodNotAllowed(self):
return self.WriteResponseError(405)
# ------------------------------------------------------------------------
def WriteResponseInternalServerError(self):
return self.WriteResponseError(500)
# ------------------------------------------------------------------------
def WriteResponseNotImplemented(self):
return self.WriteResponseError(501)
# ------------------------------------------------------------------------
_errCtnTmpl = """\
<html>
<head>
<title>Error</title>
</head>
<body>
<h1>%(code)d %(reason)s</h1>
%(message)s
</body>
</html>
"""
# ------------------------------------------------------------------------
_execErrCtnTmpl = """\
<html>
<head>
<title>Page execution error</title>
</head>
<body>
<h1>%(module)s page execution error</h1>
%(message)s
</body>
</html>
"""
# ------------------------------------------------------------------------
_responseCodes = {
100: ('Continue', 'Request received, please continue'),
101: ('Switching Protocols',
'Switching to new protocol; obey Upgrade header'),
200: ('OK', 'Request fulfilled, document follows'),
201: ('Created', 'Document created, URL follows'),
202: ('Accepted',
'Request accepted, processing continues off-line'),
203: ('Non-Authoritative Information', 'Request fulfilled from cache'),
204: ('No Content', 'Request fulfilled, nothing follows'),
205: ('Reset Content', 'Clear input form for further input.'),
206: ('Partial Content', 'Partial content follows.'),
300: ('Multiple Choices',
'Object has several resources -- see URI list'),
301: ('Moved Permanently', 'Object moved permanently -- see URI list'),
302: ('Found', 'Object moved temporarily -- see URI list'),
303: ('See Other', 'Object moved -- see Method and URL list'),
304: ('Not Modified',
'Document has not changed since given time'),
305: ('Use Proxy',
'You must use proxy specified in Location to access this '
'resource.'),
307: ('Temporary Redirect',
'Object moved temporarily -- see URI list'),
400: ('Bad Request',
'Bad request syntax or unsupported method'),
401: ('Unauthorized',
'No permission -- see authorization schemes'),
402: ('Payment Required',
'No payment -- see charging schemes'),
403: ('Forbidden',
'Request forbidden -- authorization will not help'),
404: ('Not Found', 'Nothing matches the given URI'),
405: ('Method Not Allowed',
'Specified method is invalid for this resource.'),
406: ('Not Acceptable', 'URI not available in preferred format.'),
407: ('Proxy Authentication Required', 'You must authenticate with '
'this proxy before proceeding.'),
408: ('Request Timeout', 'Request timed out; try again later.'),
409: ('Conflict', 'Request conflict.'),
410: ('Gone',
'URI no longer exists and has been permanently removed.'),
411: ('Length Required', 'Client must specify Content-Length.'),
412: ('Precondition Failed', 'Precondition in headers is false.'),
413: ('Request Entity Too Large', 'Entity is too large.'),
414: ('Request-URI Too Long', 'URI is too long.'),
415: ('Unsupported Media Type', 'Entity body in unsupported format.'),
416: ('Requested Range Not Satisfiable',
'Cannot satisfy request range.'),
417: ('Expectation Failed',
'Expect condition could not be satisfied.'),
500: ('Internal Server Error', 'Server got itself in trouble'),
501: ('Not Implemented',
'Server does not support this operation'),
502: ('Bad Gateway', 'Invalid responses from another server/proxy.'),
503: ('Service Unavailable',
'The server cannot process the request due to a high load'),
504: ('Gateway Timeout',
'The gateway server did not receive a timely response'),
505: ('HTTP Version Not Supported', 'Cannot fulfill request.'),
}
# ============================================================================
# ============================================================================
# ============================================================================