esp8266 webImport
UPDATED and BUGFIXED!
MicroPython Repl command line is very nice, because you can telnet to the chip and read-and-evaluate python code. It is a great way of learning embedded IoT. The bad thing you cannot copy file while it is running, so experimenting get bad.
Also an automatic module update would be a very nice thing to have.
So I have created a small module called webImport which is able to
- download a module.py from a known http server
- write it in the esp8266 and import it
- enable your esp8266 to be hacked by a worm :) (see below for caveats)
from webimport import web_import web_import('modulename')After a while you edit your modulename.py and you make a newMethod. So you want to reload it...with...
web_import('modulename') modulename.method()
The CODE
The code is below and was tested on esp8266. It should work on Pyboard and more powerful board too.# Implement a web import api # Rev 1.0.2 # Rev 1.0.1 was bugged #import ujson as json import uos as os import usocket as socketdebug=True
def say(m): if debug: print("wi: "+str(m))
def extractHeaders(headers_pair2split):
hp=headers_pair2split.split(b'\r\n') headers={} for elem in hp: k,v=elem.split(b':',1) headers[k]=v say(str(k)+" => "+str(v))def http_get_async(url): say("webImport:"+url) _, _, host, path = url.split('/', 3) if ':' in host: host, port = host.split(':') else: port = 80 addr = socket.getaddrinfo(host, int(port))[0][-1] s = socket.socket() s.connect(addr) s.send(bytes('GET /%s HTTP/1.0\r\nHost: %s\r\n\r\n' % (path, host), 'utf8')) headerStr = b'' body_bytes= b'' buffer=b'' ## Skip headerStr: they are tiny, so we get a small chunk while True:
buffer= buffer + s.recv(250) if b'\r\n\r\n' in buffer: break # Evict headerStr headerStr, body_bytes = buffer.split(b'\r\n\r\n',1) # GG Consider splitting via b'\r\n' to get headers, get Date: # to implement a caching algorithm #print("wi: debug: headers:"+str(headerStr)+" SIZE:"+str(len(str(headerStr)))) response, headers_pair2split=headerStr.split(b'\r\n',1) extractHeaders(headers_pair2split) if '200 OK' not in response: raise ImportError("Response not ok:"+str(response)) yield body_bytes while True: data= s.recv(1600) if data: yield data else: breakdef http_get(url): response = b'' try: get = http_get_async(url) while True: file_bytes = get.send(None) response += file_bytes except StopIteration: pass
response_str = str(response, 'utf-8') return response_str
def ensure_dirs(path): split_path = path.split('/') if len(split_path) > 1: for i, fragment in enumerate(split_path): parent = '/'.join(split_path[:-i]) try: os.mkdir(parent) except OSError: pass
def http_get_to_file(url, path): ensure_dirs(path) totSize=0 with open(path, 'w') as outfile: try: get = http_get_async(url) while True: file_bytes = get.send(None) outfile.write(file_bytes) totSize+=len(file_bytes) except StopIteration:
outfile.close() say("Loaded "+path+"#"+str(totSize))def web_import(moduleName, host="192.168.1.7:8000", on_error_use_cache=True): try: fname="./"+moduleName+".py" url="http://"+host+"/"+fname http_get_to_file(url,fname) return import(moduleName) except OSError as e: print(str(e)) if on_error_use_cache:
m= import(moduleName) print("wi: "+moduleName+" ECONN cache ok") return m
Optimization
As a bonus you can cross-compile the module, using the MicroPython cross compiler, which runs under any Unix-like system and compiles .py scripts into .mpy files.https://github.com/micropython/micropython/tree/master/mpy-cross
This step is optional but offer to you less memory usage and better performance