From 3a17e7f2183f37557bdc4038bc2167e05dc78843 Mon Sep 17 00:00:00 2001 From: Maff Date: Sat, 2 Jun 2018 00:10:33 +0100 Subject: [PATCH] okay so now rather than having microWebSrv i just wrote my own dang webserver. --- main.py | 127 ++++++++----------- microWebSrv.py | 319 ----------------------------------------------- tinyWebServer.py | 42 +++++-- uPyConfig.py | 11 +- uPySensor.py | 13 ++ 5 files changed, 104 insertions(+), 408 deletions(-) delete mode 100644 microWebSrv.py diff --git a/main.py b/main.py index aa5a69d..f37b5af 100644 --- a/main.py +++ b/main.py @@ -1,92 +1,63 @@ import uPyConfig -hw = uPyConfig.esp8266(variant='d1-r2') +hw = uPyConfig.esp8266(variant='heltec') #print family, variant and IP address (using oled, if available on-board) import init_sample init_sample.init_sample(hw) # Main app -from uPySensor import BME280, LM75A, SHT21 -bme280=BME280(hw.i2c.bus) -lm75a =LM75A(hw.i2c.bus) -sht21 =SHT21(hw.i2c.bus) +#from uPySensor import BME280, LM75A, SHT21 +#bme280=BME280(hw.i2c.bus) +#lm75a =LM75A(hw.i2c.bus) +#sht21 =SHT21(hw.i2c.bus) +from uPySensor import DS18B20 +hw.owc.__init__(hw.owc, 12) +ds18b20=DS18B20(hw.owc.bus) -#MicroWebSrv disabled until i work out how to make it fit into an esp8266's memory space -#wshead={ -# 'Server':'horny', -#} -#wsctype='application/json' -#wscharset='UTF-8' -#from microWebSrv import MicroWebSrv -#@MicroWebSrv.route('/') -#def get_root(wscl, wsres): -# wsres.WriteResponseOk( -# headers=wshead, -# contentType=wsctype, -# content='{"result":"error","message":"use /bme280, /lm75a or /sht21 for sensor readings"}' -# ) -#@MicroWebSrv.route('/bme280') -#def get_bme280(wscl, wsres): -# bme280.update_sensor() -# wsres.WriteResponseOk( -# headers=wshead, -# contentType=wsctype, -# content='{"temperature":"%0.2f","humidity":"%0.2f","pressure":"%0.2f"}' % ( -# bme280.temperature, bme280.humidity, bme280.pressure) -# ) -#@MicroWebSrv.route('/lm75a') -#def get_lm75a(wscl, wsres): -# wsres.WriteResponseOk( -# headers=wshead, -# contentType=wsctype, -# content='{"temperature":"%0.1f"}' % lm75a.read_tempC() -# ) -#@MicroWebSrv.route('/sht21') -#def sht21(wscl, wsres): -# wsres.WriteResponseOk( -# headers=wshead, -# contentType=wsctype, -# content='{"temperature":"%0.3f","humidity":"%0.3f"}' % (sht21.read_tempC(), sht21.read_hum()) -# ) -# -#ws = MicroWebSrv() -#ws.Start() #fallback microserver -def _bme280(): - bme280.update_sensor() - return '{"temperature":"%0.2f","humidity":"%0.2f","pressure":"%0.2f"}' % ( - bme280.temperature, bme280.humidity, bme280.pressure) -def _lm75a(): return '{"temperature":"%0.1f"}' % lm75a.read_tempC() -def _sht21(): return '{"temperature":"%0.3f","humidity":"%0.3f"}' % (sht21.read_tempC(), sht21.read_hum()) -def header(): return "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nServer: horny\r\n\r\n" -import socket -wssock=socket.getaddrinfo("0.0.0.0",80)[0][-1] -wss=socket.socket() -wss.bind(wssock) -wss.listen(1) +#commented out to use tinyWebServer instead +#def header(): return "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nServer: horny\r\n\r\n" +#import socket +#wssock=socket.getaddrinfo("0.0.0.0",80)[0][-1] +#wss=socket.socket() +#wss.bind(wssock) +#wss.listen(1) +# +#print("Server started on 0.0.0.0:80") +# +##main loop +#while True: +# wcl, wcsock = wss.accept() +# print("Client connected") +# wclfh = wcl.makefile('rwb', 0) +# jsn='' +# while True: +# wcline = wclfh.readline() +# if not wcline or wcline == b'\r\n': break +# wcline=wcline.split(b' ') +# if len(wcline) == 3 and wcline[2].startswith('HTTP'): +# if wcline[0] != b'GET': wcl.close() +# elif wcline[1] == b'/': jsn='{"result":"error","message":"use /bme280, /lm75a or /sht21 for sensor readings"}' +# elif wcline[1] == b'/sht21': jsn=_sht21 () +# elif wcline[1] == b'/lm75a': jsn=_lm75a () +# elif wcline[1] == b'/bme280': jsn=_bme280() +# wcl.send(header()+jsn) +# wcl.close() -print("Server started on 0.0.0.0:80") +import tinyWebServer +ws = tinyWebServer.tinyWebServer(verbose=True) -#main loop -while True: - wcl, wcsock = wss.accept() - print("Client connected") - wclfh = wcl.makefile('rwb', 0) - jsn='' - while True: - wcline = wclfh.readline() - if not wcline or wcline == b'\r\n': break - wcline=wcline.split(b' ') - if len(wcline) == 3 and wcline[2].startswith('HTTP'): - if wcline[0] != b'GET': wcl.close() - elif wcline[1] == b'/': jsn='{"result":"error","message":"use /bme280, /lm75a or /sht21 for sensor readings"}' - elif wcline[1] == b'/sht21': jsn=_sht21 () - elif wcline[1] == b'/lm75a': jsn=_lm75a () - elif wcline[1] == b'/bme280': jsn=_bme280() - wcl.send(header()+jsn) - wcl.close() - -ws = tinyWebServer() +def _get(request): + return ws.respond( + '{"error":"sensors available on /ds18b20"}', + status=404) +def _get_ds18b20(request): + return ws.respond( + '{"temperature":"%0.3f"}' % ds18b20.read_tempC() + ) +ws.add_route('/', _get) +ws.add_route('/ds18b20', _get_ds18b20) +ws.start() \ No newline at end of file diff --git a/microWebSrv.py b/microWebSrv.py deleted file mode 100644 index 18db587..0000000 --- a/microWebSrv.py +++ /dev/null @@ -1,319 +0,0 @@ -import socket -from ure import compile - -class MicroWebSrvRoute: - def __init__(self, route, method, func, routeArgNames, routeRegex): - self.route = route - self.method = method - self.func = func - self.routeArgNames = routeArgNames - self.routeRegex = routeRegex - -class MicroWebSrv: - _html_escape_chars = { - "&": "&", - '"': """, - "'": "'", - ">": ">", - "<": "<" - } - _decoratedRouteHandlers = [] - - @classmethod - def route(cls, url, method='GET'): - def route_decorator(func): - item = (url, method, func) - 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: - return bytearray(size) - except: - pass - return None - - @staticmethod - def _unquote(s): - r = s.split('%') - for i in range(1, len(r)): - s = r[i] - try: - r[i] = chr(int(s[:2], 16)) + s[2:] - except: - r[i] = '%' + s - return ''.join(r) - - @staticmethod - def _unquote_plus(s): - return MicroWebSrv._unquote(s.replace('+', ' ')) - - def __init__(self, - routeHandlers=[], - port=80, - bindIP='0.0.0.0'): - - self._srvAddr = (bindIP, port) - self._started = False - - self._routeHandlers = [] - routeHandlers += self._decoratedRouteHandlers - for route, method, func in routeHandlers: - routeParts = route.split('/') - routeArgNames = [] - routeRegex = '' - for s in routeParts: - if s.startswith('<') and s.endswith('>'): - routeArgNames.append(s[1:-1]) - routeRegex += '/(\\w*)' - elif s: - routeRegex += '/' + s - routeRegex += '$' - routeRegex = compile(routeRegex) - self._routeHandlers.append(MicroWebSrvRoute(route, method, func, routeArgNames, routeRegex)) - - def _serverProcess(self): - self._started = True - while True: - try: - client, cliAddr = self._server.accepted() - if client == None: continue - except Exception as e: - print(e) - break - self._client(self, client, cliAddr) - self._started = False - - def Start(self): - if self._started: return - self._server = socket.socket(socket.AF_INET, - socket.SOCK_STREAM, - socket.IPPROTO_TCP) - self._server.setsockopt(socket.SOL_SOCKET, - socket.SO_REUSEADDR, - 1) - self._server.bind(self._srvAddr) - self._server.listen(1) - self._server.settimeout(0.5) - self._serverProcess() - - def Stop(self): - if not self._started: return - self._server.close() - - def GetRouteHandler(self, resUrl, method): - 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 and rh.routeArgNames: - routeArgs = {} - for i, name in enumerate(rh.routeArgNames): - value = m.group(i + 1) - try: value = int(value) - except: pass - routeArgs[name] = value - return (rh.func, routeArgs) - elif m: return (rh.func, None) - return (None, None) - - class _client: - def __init__(self, microWebSrv, socket, addr): - socket.settimeout(2) - self._microWebSrv = microWebSrv - self._socket = socket - self._addr = addr - self._method = None - self._path = None - self._httpVer = None - self._resPath = "/" - self._queryString = "" - self._queryParams = {} - self._headers = {} - self._contentType = None - self._contentLength = 0 - - self._processRequest() - - def _processRequest(self): - try: - response = MicroWebSrv._response(self) - if self._parseFirstLine(response) and self._parseHeader(response): - routeHandler, routeArgs = self._microWebSrv.GetRouteHandler(self._resPath, self._method) - if routeHandler and routeArgs is not None: - routeHandler(self, response, routeArgs) - elif routeHandler: - routeHandler(self, response) - 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: return False - self._method = elements[0].upper() - self._path = elements[1] - self._httpVer = elements[2].upper() - elements = self._path.split('?', 1) - if len(elements) < 1: return True - self._resPath = MicroWebSrv._unquote_plus(elements[0]) - if len(elements) < 2: return True - self._queryString = elements[1] - elements = self._queryString.split('&') - for s in elements: - param = s.split('=', 1) - if len(param) > 0: - value = MicroWebSrv._unquote(param[1]) if len(param) > 1 else '' - self._queryParams[MicroWebSrv._unquote(param[0])] = value - return True - except: - pass - return False - - def _parseHeader(self, response): - while True: - elements = self._socket.readline().decode().strip().split(':', 1) - if len(elements) == 2: - self._headers[elements[0].strip()] = elements[1].strip() - elif len(elements) == 1 and len(elements[0]) == 0: - if self._method == 'POST': - self._contentType = self._headers.get("Content-Type", None) - self._contentLength = int(self._headers.get("Content-Length", 0)) - return True - else: - return False - - def ReadRequestContent(self, size=None): - self._socket.setblocking(False) - b = None - try: - if not size: - b = self._socket.read(self._contentLength) - elif size > 0: - b = self._socket.read(size) - except: - pass - self._socket.setblocking(True) - return b if b else b'' - - def ReadRequestPostedFormData(self): - res = {} - data = self.ReadRequestContent() - if len(data) < 1: return res - elements = data.decode().split('&') - for s in elements: - param = s.split('=', 1) - if len(param) < 1: next - value = MicroWebSrv._unquote(param[1]) if len(param) > 1 else '' - res[MicroWebSrv._unquote(param[0])] = value - return res - - 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 \ - + (("; charset=%s" % charset) if charset else "") - else: - ct = "application/octet-stream" - self._writeHeader("Content-Type", ct) - - def _writeServerHeader(self): - 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): - for header in headers: - self._writeHeader(header, headers[header]) - if contentLength > 0: - self._writeContentTypeHeader(contentType, contentCharset) - self._writeHeader("Content-Length", contentLength) - self._writeServerHeader() - self._writeHeader("Connection", "close") - self._writeEndHeader() - - def WriteResponse(self, code, headers, contentType, contentCharset, content): - try: - contentLength = len(content) if content else 0 - self._writeBeforeContent(code, headers, contentType, contentCharset, contentLength) - if contentLength > 0: - self._write(content) - return True - except: - return False - - def WriteResponseOk(self, headers=None, contentType=None, contentCharset=None, content=None): - return self.WriteResponse(200, headers, contentType, contentCharset, content) - - 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", - '{"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): - return self.WriteResponseError(404) - - def WriteResponseInternalServerError(self): - return self.WriteResponseError(500) - - _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'), - 204: ('No Content', 'Request fulfilled, nothing follows'), - 301: ('Moved Permanently', 'Object moved permanently -- see URI list'), - 302: ('Found', 'Object moved temporarily -- see URI list'), - 404: ('Not Found', 'Nothing matches the given URI'), - 500: ('Internal Server Error', 'Server got itself in trouble'), - 501: ('Not Implemented', - 'Server does not support this operation'), - } diff --git a/tinyWebServer.py b/tinyWebServer.py index 02872cb..342ce15 100644 --- a/tinyWebServer.py +++ b/tinyWebServer.py @@ -3,10 +3,23 @@ class tinyWebServer: import socket _routes={} - def __init__(self, bind_ip='0.0.0.0', bind_port=80): + _HTTPStatusCodes={ + 100: 'Continue', + 101: 'Switching Protocols', + 200: 'OK', + 201: 'Created', + 202: 'Accepted', + 204: 'No Content', + 301: 'Moved Permanently', + 302: 'Found', + 404: 'Not Found', + 500: 'Not Implemented', + } + def __init__(self, bind_ip='0.0.0.0', bind_port=80, verbose=False): + self.verbose=verbose self.bind_ip=bind_ip self.bind_port=bind_port - self.wssock=socket.getaddrinfo(bind_ip, bind_port)[0][-1] + self.wssock=self.socket.getaddrinfo(bind_ip, bind_port)[0][-1] def add_route(self, path, handler): self._routes.update({b'%s' % path: handler}) @@ -15,20 +28,31 @@ class tinyWebServer: self.wss=self.socket.socket() self.wss.bind(self.wssock) self.wss.listen(1) + print("tinyWebServer started on %s:%d" % (self.bind_ip, self.bind_port)) while True: self.handle_request(*self.wss.accept()) def handle_request(self, reqclient, reqsocket): + if reqclient == None or reqsocket == None: return + if self.verbose: print(reqclient) reqmethod = None reqpath = None reqclf = reqclient.makefile('rwb', 0) while True: reqline = reqclf.readline() if not reqline or reqline == b'\r\n': break - if reqmethod == None or reqpath == None and len(reqline.split(' ')) == 3 and reqline.split(' ')[2].startswith('HTTP'): - reqmethod, reqpath = reqline.split(' ')[:-1] - if reqmethod == None or reqpath == None: reqclient.close() - _routes.get(reqpath, self.no_route)(reqclient) - + if reqmethod == None or reqpath == None and reqline.find(b' ') >= 0 and len(reqline.split(b' ')) == 3 and reqline.split(b' ')[2].startswith(b'HTTP'): + reqmethod, reqpath = reqline.split(b' ')[:-1] + if reqmethod == None or reqpath == None: + if self.verbose: print("closing attempted keepalive") + reqclient.close() + return + if self.verbose: print("client connected from %s: %s %s" % (reqsocket[0], reqmethod, reqpath)) + reqclient.send(self._routes.get(reqpath, self.no_route)(reqclient)) + reqclient.close() + + def respond(self, body='', status=200, ctype="application/json"): + return "HTTP/1.1 %d %s\r\nContent-Type: %s\r\nConnection: close\r\nServer: tinyWebServer (horny)\r\n\r\n%s" % ( + status, self._HTTPStatusCodes[status], ctype, body) + def no_route(self, request): - request.close() - \ No newline at end of file + return self.respond(status=404) diff --git a/uPyConfig.py b/uPyConfig.py index d4aabe4..2e7b313 100644 --- a/uPyConfig.py +++ b/uPyConfig.py @@ -15,6 +15,13 @@ class uPyConfig: temperature = False hall_effect = False + class owc: + from onewire import OneWire + from machine import Pin + def __init__(self, pin): + if pin.__class__ != self.Pin: pin=self.Pin(pin) + self.bus = self.OneWire(pin) + class _i2c: from machine import I2C, Pin @@ -29,7 +36,6 @@ class uPyConfig: class _oled: from machine import Pin - from time import sleep_ms addr = 0x0 rst = 0 rst_hold = False @@ -37,10 +43,11 @@ class uPyConfig: width = 0 def load_handle(self): + from time import sleep_ms if self.rst_hold: self.rst = self.Pin(self.rst, self.Pin.OUT) self.rst.value(0) - self.sleep_ms(2) + sleep_ms(2) self.rst.value(1) from ssd1306 import SSD1306_I2C self.handle = SSD1306_I2C(self.width, self.height, self.i2c.bus) diff --git a/uPySensor.py b/uPySensor.py index 89f78d7..bb4bb99 100644 --- a/uPySensor.py +++ b/uPySensor.py @@ -33,6 +33,19 @@ class BME280(uPySensor): self.update_sensor() return self.pressure +class DS18B20(uPySensor): + def __init__(self, onewire): + from ds18x20 import DS18X20 + self.bus=onewire + self.bus_filter=DS18X20(self.bus) + if len(self.bus_filter.scan()) == 0: raise IOError("No DS18B20 sensors found on OneWire bus") + self.sensor=self.bus_filter.scan()[0] + + def read_tempC(self): + self.bus_filter.convert_temp() + self.sleep_ms(300) + return self.bus_filter.read_temp(self.sensor) + class LM75A(uPySensor): LM75A_ADDRESS = 0x48