diff options
Diffstat (limited to 'esp8266/scripts')
-rw-r--r-- | esp8266/scripts/_boot.py | 9 | ||||
-rw-r--r-- | esp8266/scripts/flashbdev.py | 68 | ||||
-rw-r--r-- | esp8266/scripts/inisetup.py | 46 | ||||
-rw-r--r-- | esp8266/scripts/main.py | 1 | ||||
-rw-r--r-- | esp8266/scripts/neopixel.py | 24 | ||||
-rw-r--r-- | esp8266/scripts/ntptime.py | 34 | ||||
-rw-r--r-- | esp8266/scripts/onewire.py | 127 | ||||
-rw-r--r-- | esp8266/scripts/port_diag.py | 19 | ||||
-rw-r--r-- | esp8266/scripts/webrepl.py | 62 | ||||
-rw-r--r-- | esp8266/scripts/webrepl_setup.py | 80 | ||||
-rw-r--r-- | esp8266/scripts/websocket_helper.py | 75 |
11 files changed, 544 insertions, 1 deletions
diff --git a/esp8266/scripts/_boot.py b/esp8266/scripts/_boot.py new file mode 100644 index 0000000000..c950de6758 --- /dev/null +++ b/esp8266/scripts/_boot.py @@ -0,0 +1,9 @@ +import uos +from flashbdev import bdev + +try: + if bdev: + vfs = uos.VfsFat(bdev, "") +except OSError: + import inisetup + vfs = inisetup.setup() diff --git a/esp8266/scripts/flashbdev.py b/esp8266/scripts/flashbdev.py new file mode 100644 index 0000000000..07ed966020 --- /dev/null +++ b/esp8266/scripts/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/scripts/inisetup.py b/esp8266/scripts/inisetup.py new file mode 100644 index 0000000000..93a05bd8a7 --- /dev/null +++ b/esp8266/scripts/inisetup.py @@ -0,0 +1,46 @@ +import uos +import network +from flashbdev import bdev + +def wifi(): + import ubinascii + ap_if = network.WLAN(network.AP_IF) + essid = b"MicroPython-%s" % ubinascii.hexlify(ap_if.config("mac")[-3:]) + ap_if.config(essid=essid, authmode=network.AUTH_WPA_WPA2_PSK, password=b"micropythoN") + +def check_bootsec(): + buf = bytearray(bdev.SEC_SIZE) + bdev.readblocks(0, buf) + empty = True + for b in buf: + if b != 0xff: + empty = False + break + if empty: + return True + fs_corrupted() + +def fs_corrupted(): + import time + while 1: + print("""\ +FAT filesystem appears to be corrupted. If you had important data there, you +may want to make a flash snapshot to try to recover it. Otherwise, perform +factory reprogramming of MicroPython firmware (completely erase flash, followed +by firmware programming). +""") + time.sleep(3) + +def setup(): + check_bootsec() + print("Performing initial setup") + wifi() + uos.VfsFat.mkfs(bdev) + vfs = uos.VfsFat(bdev, "") + with open("/boot.py", "w") as f: + f.write("""\ +# This file is executed on every boot (including wake-boot from deepsleep) +import webrepl +webrepl.start() +""") + return vfs diff --git a/esp8266/scripts/main.py b/esp8266/scripts/main.py deleted file mode 100644 index 83bc52a321..0000000000 --- a/esp8266/scripts/main.py +++ /dev/null @@ -1 +0,0 @@ -# This script is run on boot diff --git a/esp8266/scripts/neopixel.py b/esp8266/scripts/neopixel.py new file mode 100644 index 0000000000..4818c74a3b --- /dev/null +++ b/esp8266/scripts/neopixel.py @@ -0,0 +1,24 @@ +# NeoPixel driver for MicroPython on ESP8266 +# MIT license; Copyright (c) 2016 Damien P. George + +from esp import neopixel_write + +class NeoPixel: + def __init__(self, pin, n): + self.pin = pin + self.n = n + self.buf = bytearray(n * 3) + self.pin.init(pin.OUT, pin.PULL_NONE) + + def __setitem__(self, index, val): + r, g, b = val + self.buf[index * 3] = g + self.buf[index * 3 + 1] = r + self.buf[index * 3 + 2] = b + + def __getitem__(self, index): + i = index * 3 + return self.buf[i + 1], self.buf[i], self.buf[i + 2] + + def write(self): + neopixel_write(self.pin, self.buf, True) diff --git a/esp8266/scripts/ntptime.py b/esp8266/scripts/ntptime.py new file mode 100644 index 0000000000..650cc2e85b --- /dev/null +++ b/esp8266/scripts/ntptime.py @@ -0,0 +1,34 @@ +try: + import usocket as socket +except: + import socket +try: + import ustruct as struct +except: + import struct + +# (date(2000, 1, 1) - date(1900, 1, 1)).days * 24*60*60 +NTP_DELTA = 3155673600 + +def time(): + NTP_QUERY = bytearray(48) + NTP_QUERY[0] = 0x1b + addr = socket.getaddrinfo('pool.ntp.org', 123)[0][-1] + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + s.settimeout(1) + res = s.sendto(NTP_QUERY, addr) + msg = s.recv(48) + s.close() + val = struct.unpack("!I", msg[40:44])[0] + return val - NTP_DELTA + +# There's currently no timezone support in MicroPython, so +# utime.localtime() will return UTC time (as if it was .gmtime()) +def settime(): + t = time() + import machine + import utime + tm = utime.localtime(t) + tm = tm[0:3] + (0,) + tm[3:6] + (0,) + machine.RTC().datetime(tm) + print(utime.localtime()) diff --git a/esp8266/scripts/onewire.py b/esp8266/scripts/onewire.py new file mode 100644 index 0000000000..4980d0af5c --- /dev/null +++ b/esp8266/scripts/onewire.py @@ -0,0 +1,127 @@ +# 1-Wire driver for MicroPython on ESP8266 +# MIT license; Copyright (c) 2016 Damien P. George + +import _onewire as _ow + +class OneWireError(Exception): + pass + +class OneWire: + SEARCH_ROM = const(0xf0) + MATCH_ROM = const(0x55) + SKIP_ROM = const(0xcc) + + def __init__(self, pin): + self.pin = pin + self.pin.init(pin.OPEN_DRAIN, pin.PULL_NONE) + + def reset(self): + return _ow.reset(self.pin) + + def readbit(self): + return _ow.readbit(self.pin) + + def readbyte(self): + return _ow.readbyte(self.pin) + + def read(self, count): + buf = bytearray(count) + for i in range(count): + buf[i] = _ow.readbyte(self.pin) + return buf + + def writebit(self, value): + return _ow.writebit(self.pin, value) + + def writebyte(self, value): + return _ow.writebyte(self.pin, value) + + def write(self, buf): + for b in buf: + _ow.writebyte(self.pin, b) + + def select_rom(self, rom): + self.reset() + self.writebyte(MATCH_ROM) + self.write(rom) + + def scan(self): + devices = [] + diff = 65 + rom = False + for i in range(0xff): + rom, diff = self._search_rom(rom, diff) + if rom: + devices += [rom] + if diff == 0: + break + return devices + + def _search_rom(self, l_rom, diff): + if not self.reset(): + return None, 0 + self.writebyte(SEARCH_ROM) + if not l_rom: + l_rom = bytearray(8) + rom = bytearray(8) + next_diff = 0 + i = 64 + for byte in range(8): + r_b = 0 + for bit in range(8): + b = self.readbit() + if self.readbit(): + if b: # there are no devices or there is an error on the bus + return None, 0 + else: + if not b: # collision, two devices with different bit meaning + if diff > i or ((l_rom[byte] & (1 << bit)) and diff != i): + b = 1 + next_diff = i + self.writebit(b) + if b: + r_b |= 1 << bit + i -= 1 + rom[byte] = r_b + return rom, next_diff + + def crc8(self, data): + return _ow.crc8(data) + +class DS18B20: + CONVERT = const(0x44) + RD_SCRATCH = const(0xbe) + WR_SCRATCH = const(0x4e) + + def __init__(self, onewire): + self.ow = onewire + + def scan(self): + return [rom for rom in self.ow.scan() if rom[0] == 0x28] + + def convert_temp(self): + if not self.ow.reset(): + raise OneWireError + self.ow.writebyte(SKIP_ROM) + self.ow.writebyte(CONVERT) + + def read_scratch(self, rom): + if not self.ow.reset(): + raise OneWireError + self.ow.select_rom(rom) + self.ow.writebyte(RD_SCRATCH) + buf = self.ow.read(9) + if self.ow.crc8(buf): + raise OneWireError + return buf + + def write_scratch(self, rom, buf): + if not self.ow.reset(): + raise OneWireError + self.ow.select_rom(rom) + self.ow.writebyte(WR_SCRATCH) + self.ow.write(buf) + + def read_temp(self, rom): + buf = self.read_scratch(rom) + return (buf[1] << 8 | buf[0]) / 16 diff --git a/esp8266/scripts/port_diag.py b/esp8266/scripts/port_diag.py new file mode 100644 index 0000000000..fd7ee52d14 --- /dev/null +++ b/esp8266/scripts/port_diag.py @@ -0,0 +1,19 @@ +import esp +import uctypes + + +def main(): + + ROM = uctypes.bytearray_at(0x40200000, 16) + fid = esp.flash_id() + + print("Flash ID: %x (Vendor: %x Device: %x)" % (fid, fid & 0xff, fid & 0xff00 | fid >> 16)) + + print("Flash bootloader data:") + SZ_MAP = {0: "512KB", 1: "256KB", 2: "1MB", 3: "2MB", 4: "4MB"} + FREQ_MAP = {0: "40MHZ", 1: "26MHZ", 2: "20MHz", 0xf: "80MHz"} + print("Byte @2: %02x" % ROM[2]) + print("Byte @3: %02x (Flash size: %s Flash freq: %s)" % (ROM[3], SZ_MAP.get(ROM[3] >> 4, "?"), FREQ_MAP.get(ROM[3] & 0xf))) + + +main() diff --git a/esp8266/scripts/webrepl.py b/esp8266/scripts/webrepl.py new file mode 100644 index 0000000000..1a2c82277e --- /dev/null +++ b/esp8266/scripts/webrepl.py @@ -0,0 +1,62 @@ +# 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, client_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) + 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)) + + +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): + stop() + 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") diff --git a/esp8266/scripts/webrepl_setup.py b/esp8266/scripts/webrepl_setup.py new file mode 100644 index 0000000000..7c4068750c --- /dev/null +++ b/esp8266/scripts/webrepl_setup.py @@ -0,0 +1,80 @@ +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 + 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) diff --git a/esp8266/scripts/websocket_helper.py b/esp8266/scripts/websocket_helper.py new file mode 100644 index 0000000000..22ac28592d --- /dev/null +++ b/esp8266/scripts/websocket_helper.py @@ -0,0 +1,75 @@ +import sys +try: + import ubinascii as binascii +except: + import binascii +try: + import uhashlib as hashlib +except: + import hashlib + +DEBUG = 0 + +def server_handshake(sock): + clr = sock.makefile("rwb", 0) + l = clr.readline() + #sys.stdout.write(repr(l)) + + webkey = None + + while 1: + l = clr.readline() + if not l: + raise OSError("EOF in headers") + if l == b"\r\n": + break + # sys.stdout.write(l) + h, v = [x.strip() for x in l.split(b":", 1)] + if DEBUG: + print((h, v)) + if h == b'Sec-WebSocket-Key': + webkey = v + + if not webkey: + raise OSError("Not a websocket request") + + if DEBUG: + print("Sec-WebSocket-Key:", webkey, len(webkey)) + + respkey = webkey + b"258EAFA5-E914-47DA-95CA-C5AB0DC85B11" + respkey = hashlib.sha1(respkey).digest() + respkey = binascii.b2a_base64(respkey)[:-1] + + resp = b"""\ +HTTP/1.1 101 Switching Protocols\r +Upgrade: websocket\r +Connection: Upgrade\r +Sec-WebSocket-Accept: %s\r +\r +""" % respkey + + if DEBUG: + print(resp) + sock.send(resp) + + +# Very simplified client handshake, works for MicroPython's +# websocket server implementation, but probably not for other +# servers. +def client_handshake(sock): + cl = sock.makefile("rwb", 0) + cl.write(b"""\ +GET / HTTP/1.1\r +Host: echo.websocket.org\r +Connection: Upgrade\r +Upgrade: websocket\r +Sec-WebSocket-Key: foo\r +\r +""") + l = cl.readline() +# print(l) + while 1: + l = cl.readline() + if l == b"\r\n": + break +# sys.stdout.write(l) |