Skip to content

Instantly share code, notes, and snippets.

@markusand
Created November 8, 2024 23:12
Show Gist options
  • Save markusand/d5a7b9806dba059a565dfdd947d1747b to your computer and use it in GitHub Desktop.
Save markusand/d5a7b9806dba059a565dfdd947d1747b to your computer and use it in GitHub Desktop.
Lightweight version of pyboard. Includes replacement of module constants declaration for execfile()
"""
Minimal pyboard.py implementation for communicating with MicroPython boards
"""
import os
import time
import re
import serial
import serial.tools.list_ports
class PyboardError(Exception):
"""Exception raised for errors in the pyboard."""
def parse_error(error: PyboardError):
"""Parse a PyboardError into a human-readable message"""
message = str(error).split(r"\r\n")[-2:][0] # Get the second-to-last or last line
return message.split(":")[-1].strip()
class Pyboard:
"""Class for communicating with MicroPython boards"""
def __init__(self, device, baudrate=115200, wait=0):
"""Initialize connection to the board"""
try:
self._serial = serial.Serial(device, baudrate, timeout=1)
except serial.SerialException as e:
raise PyboardError(f"Failed to open serial port {device}: {str(e)}") from e
if wait:
time.sleep(wait)
self.enter_repl()
def close(self):
"""Close the serial connection"""
if self._serial:
self.exit_repl()
self._serial.close()
def read_until(self, min_bytes, ending, timeout=10):
"""Read from serial until ending sequence is found"""
if not self._serial:
raise PyboardError("Board not connected")
data = self._serial.read(min_bytes)
start_time = time.time()
while True:
if time.time() - start_time > timeout:
raise PyboardError("Timeout waiting for board response")
if data.endswith(ending):
break
if self._serial.in_waiting > 0:
data += self._serial.read(1)
start_time = time.time()
return data
def enter_repl(self):
"""Enter raw REPL mode"""
if not self._serial:
raise PyboardError("Board not connected")
self._serial.write(b"\x03\x03") # CTRL-C twice
time.sleep(0.1)
self._serial.write(b"\x01") # CTRL-A
self.read_until(1, b"raw REPL; CTRL-B to exit\r\n>")
def exit_repl(self):
"""Exit raw REPL mode"""
if not self._serial:
raise PyboardError("Board not connected")
self._serial.write(b"\x02") # CTRL-B
time.sleep(0.1)
def exec(self, command, timeout=10):
"""Execute command in raw REPL mode"""
if not self._serial:
raise PyboardError("Board not connected")
self._serial.reset_input_buffer() # Flush any data that might be in the buffer
self._serial.write(command.encode("utf8") + b"\x04")
if self._serial.read(2) != b"OK":
raise PyboardError(f"Could not execute the command {command}")
data = self.read_until(1, b"\04")
if not data.endswith(b"\x04"):
raise PyboardError("Timeout waiting for first EOF")
data = data[:-1]
error = self.read_until(1, b"\x04", timeout=timeout)
if not error.endswith(b"\x04"):
raise PyboardError("Timeout waiting for second EOF")
if error[:-1]:
raise PyboardError(
f"Error with command {command} because {error.decode('utf-8')[:-1]}"
)
return data.decode("utf8").strip()
def execfile(self, filepath, **kwargs):
"""Execute a local MicroPython script file on the board with optional arguments"""
if not os.path.exists(filepath):
raise PyboardError(f"File not found: {filepath}")
try:
with open(filepath, "r", encoding="utf8") as file:
data = file.read()
# Remove comments and docstrings
data = re.sub(r'""".*?"""|#.*$', "", data, flags=re.MULTILINE)
# Replace module constant assignments
if kwargs:
for k, v in kwargs.items():
pattern = rf"^{k}\s*=\s*.*"
data = re.sub(pattern, f"{k} = {v}", data, flags=re.MULTILINE)
return self.exec(data)
except IOError as e:
raise PyboardError(f"Failed to read file {filepath}: {e}") from e
except PyboardError as e:
raise PyboardError(f"Error executing {filepath}: {e}") from e
except Exception as e:
raise PyboardError(f"Unexpected error executing {filepath}: {e}") from e
def reset(self):
"""Reset the board"""
if not self._serial:
raise PyboardError("Board not connected")
self._serial.write(b"\x04") # CTRL-D
time.sleep(0.5)
self.enter_repl()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment