diff options
Diffstat (limited to 'esp8266/modules')
-rw-r--r-- | esp8266/modules/_boot.py | 12 | ||||
-rw-r--r-- | esp8266/modules/flashbdev.py | 68 | ||||
-rw-r--r-- | esp8266/modules/webrepl.py | 75 | ||||
-rw-r--r-- | esp8266/modules/webrepl_setup.py | 83 |
4 files changed, 238 insertions, 0 deletions
diff --git a/esp8266/modules/_boot.py b/esp8266/modules/_boot.py new file mode 100644 index 0000000000..2cacb56760 --- /dev/null +++ b/esp8266/modules/_boot.py @@ -0,0 +1,12 @@ +import gc +import uos +from flashbdev import bdev + +try: + if bdev: + vfs = uos.VfsFat(bdev, "") +except OSError: + import inisetup + vfs = inisetup.setup() + +gc.collect() diff --git a/esp8266/modules/flashbdev.py b/esp8266/modules/flashbdev.py new file mode 100644 index 0000000000..07ed966020 --- /dev/null +++ b/esp8266/modules/flashbdev.py @@ -0,0 +1,68 @@ +import esp + +class FlashBdev: + + SEC_SIZE = 4096 + START_SEC = 0x89000 // SEC_SIZE + NUM_BLK = 0x73 + + def __init__(self, blocks=NUM_BLK): + self.blocks = blocks + + def readblocks(self, n, buf): + #print("readblocks(%s, %x(%d))" % (n, id(buf), len(buf))) + esp.flash_read((n + self.START_SEC) * self.SEC_SIZE, buf) + + def writeblocks(self, n, buf): + #print("writeblocks(%s, %x(%d))" % (n, id(buf), len(buf))) + #assert len(buf) <= self.SEC_SIZE, len(buf) + esp.flash_erase(n + self.START_SEC) + esp.flash_write((n + self.START_SEC) * self.SEC_SIZE, buf) + + def ioctl(self, op, arg): + #print("ioctl(%d, %r)" % (op, arg)) + if op == 4: # BP_IOCTL_SEC_COUNT + return self.blocks + if op == 5: # BP_IOCTL_SEC_SIZE + return self.SEC_SIZE + +def set_bl_flash_size(real_size): + if real_size == 256*1024: + code = 1 + elif real_size == 512*1024: + code = 0 + elif real_size == 1024*1024: + code = 2 + elif real_size == 2048*1024: + code = 3 + elif real_size == 4096*1024: + code = 4 + else: + code = 2 + buf = bytearray(4096) + esp.flash_read(0, buf) + buf[3] = (buf[3] & 0xf) | (code << 4) + esp.flash_erase(0) + esp.flash_write(0, buf) + +# If bootloader size ID doesn't correspond to real Flash size, +# fix bootloader value and reboot. +size = esp.flash_id() >> 16 +# Check that it looks like realistic power of 2 for flash sizes +# commonly used with esp8266 +if 22 >= size >= 18: + size = 1 << size + if size != esp.flash_size(): + import machine + import time + print("Bootloader Flash size appear to have been set incorrectly, trying to fix") + set_bl_flash_size(size) + machine.reset() + while 1: time.sleep(1) + +size = esp.flash_size() +if size < 1024*1024: + bdev = None +else: + # 16K at the flash end is reserved for SDK params storage + bdev = FlashBdev((size - 16384) // FlashBdev.SEC_SIZE - FlashBdev.START_SEC) diff --git a/esp8266/modules/webrepl.py b/esp8266/modules/webrepl.py new file mode 100644 index 0000000000..da3e70c595 --- /dev/null +++ b/esp8266/modules/webrepl.py @@ -0,0 +1,75 @@ +# This module should be imported from REPL, not run from command line. +import socket +import uos +import network +import websocket +import websocket_helper +import _webrepl + +listen_s = None +client_s = None + +def setup_conn(port, accept_handler): + global listen_s + listen_s = socket.socket() + listen_s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + + ai = socket.getaddrinfo("0.0.0.0", port) + addr = ai[0][4] + + listen_s.bind(addr) + listen_s.listen(1) + if accept_handler: + listen_s.setsockopt(socket.SOL_SOCKET, 20, accept_handler) + for i in (network.AP_IF, network.STA_IF): + iface = network.WLAN(i) + if iface.active(): + print("WebREPL daemon started on ws://%s:%d" % (iface.ifconfig()[0], port)) + return listen_s + + +def accept_conn(listen_sock): + global client_s + cl, remote_addr = listen_sock.accept() + print("\nWebREPL connection from:", remote_addr) + client_s = cl + websocket_helper.server_handshake(cl) + ws = websocket.websocket(cl, True) + ws = _webrepl._webrepl(ws) + cl.setblocking(False) + # notify REPL on socket incoming data + cl.setsockopt(socket.SOL_SOCKET, 20, uos.dupterm_notify) + uos.dupterm(ws) + + +def stop(): + global listen_s, client_s + uos.dupterm(None) + if client_s: + client_s.close() + if listen_s: + listen_s.close() + + +def start(port=8266, password=None): + stop() + if password is None: + try: + import port_config + _webrepl.password(port_config.WEBREPL_PASS) + setup_conn(port, accept_conn) + print("Started webrepl in normal mode") + except: + import webrepl_setup + setup_conn(port, webrepl_setup.handle_conn) + print("Started webrepl in setup mode") + else: + _webrepl.password(password) + setup_conn(port, accept_conn) + print("Started webrepl in normal mode") + + +def start_foreground(port=8266): + stop() + s = setup_conn(port, None) + accept_conn(s) diff --git a/esp8266/modules/webrepl_setup.py b/esp8266/modules/webrepl_setup.py new file mode 100644 index 0000000000..d0bf8465d5 --- /dev/null +++ b/esp8266/modules/webrepl_setup.py @@ -0,0 +1,83 @@ +import sys +import socket +import time + +from websocket import * +import websocket_helper + + +def setup_server(): + s = socket.socket() + s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + + ai = socket.getaddrinfo("0.0.0.0", 8266) + addr = ai[0][4] + + s.bind(addr) + s.listen(1) + return s + +def getpass(stream, prompt): + stream.write(prompt) + passwd = b"" + while 1: + c = stream.read(1) + if c in (b"\r", b"\n"): + stream.write("\r\n") + return passwd + passwd += c + stream.write("*") + +def handle_conn(listen_sock): + cl, remote_addr = listen_sock.accept() + + print(""" + +First-time WebREPL connection has been received. WebREPL initial setup +will now start over this connection. During setup, UART REPL will be +non-responsive. After setup finishes, the board will be rebooted. In +case of error during setup, current session will continue. + +If you receive this message unexpectedly, it may mean that your WebREPL +connection is being hacked (power off board if unsure). +""") + + websocket_helper.server_handshake(cl) + ws = websocket(cl) + + ws.write("""\ +Welcome to MicroPython WebREPL!\r +\r +This is the first time you connect to WebREPL, so please set a password\r +to use for the following WebREPL sessions. Once you enter the password\r +twice, your board will reboot with WebREPL running in active mode. On\r +some boards, you may need to press reset button or reconnect power.\r +\r +""") + + while 1: + passwd1 = getpass(ws, "New password: ") + if len(passwd1) < 4: + ws.write("Password too short\r\n") + continue + elif len(passwd1) > 9: + ws.write("Password too long\r\n") + continue + passwd2 = getpass(ws, "Confirm password: ") + if passwd1 == passwd2: + break + ws.write("Passwords do not match\r\n") + + with open("port_config.py", "w") as f: + f.write("WEBREPL_PASS = %r\n" % passwd1.decode("ascii")) + + ws.write("Password successfully set, restarting...\r\n") + cl.close() + time.sleep(2) + import machine + machine.reset() + + +def test(): + s = setup_server() + handle_conn(s) |