brianpugh / belay Goto Github PK
View Code? Open in Web Editor NEWBelay is a python library that enables the rapid development of projects that interact with hardware via a micropython-compatible board.
License: Apache License 2.0
Belay is a python library that enables the rapid development of projects that interact with hardware via a micropython-compatible board.
License: Apache License 2.0
I'm testing out an ESP32 (yesterday I worked on a RPi Pico), and I am getting errors connecting it with Belay. It seemingly fails in Pyboard
, however if I connect thorugh Pyboard
directly, it works.
pyb = pyboard.Pyboard('/dev/cu.SLAB_USBtoUART', 115200)
# pyb = belay.Device('/dev/cu.SLAB_USBtoUART', 115200)
pyb.close()
# Works without errors
# pyb = pyboard.Pyboard('/dev/cu.SLAB_USBtoUART', 115200)
pyb = belay.Device('/dev/cu.SLAB_USBtoUART', 115200)
pyb.close()
# Produces an error >>>
---------------------------------------------------------------------------
PyboardError Traceback (most recent call last)
Cell In [24], line 2
1 # pyb = pyboard.Pyboard('/dev/cu.SLAB_USBtoUART', 115200)
----> 2 pyb = belay.Device('/dev/cu.SLAB_USBtoUART', 115200)
4 pyb.close()
6 # poll = select.poll()
7 # poll.register(pyb.serial, select.POLLIN)
File /usr/local/anaconda3/envs/buxr/lib/python3.10/site-packages/belay/device.py:464, in Device.__init__(self, startup, attempts, *args, **kwargs)
461 self.attempts = attempts
462 self._cmd_history = []
--> 464 self._connect_to_board(**self._board_kwargs)
466 self.task = _TaskExecuter(self)
467 self.thread = _ThreadExecuter(self)
File /usr/local/anaconda3/envs/buxr/lib/python3.10/site-packages/belay/device.py:523, in Device._connect_to_board(self, **kwargs)
521 else:
522 soft_reset = True
--> 523 self._board.enter_raw_repl(soft_reset=soft_reset)
File /usr/local/anaconda3/envs/buxr/lib/python3.10/site-packages/belay/pyboard.py:382, in Pyboard.enter_raw_repl(self, soft_reset)
380 if not data.endswith(b"raw REPL; CTRL-B to exit\r\n>"):
381 print(data)
--> 382 raise PyboardError("could not enter raw repl")
...
386 # Waiting for "soft reboot" independently to "raw REPL" (done below)
387 # allows boot.py to print, which will show up after "soft reboot"
388 # and before "raw REPL".
PyboardError: could not enter raw repl
Before the error it seems to print:
b'MicroPython v1.19.1 on 2022-06-18; ESP32 module with ESP32\r\nType "help()" for more information.\r\n>>> '
I can print a global scope var in a function. But I can't print it or access it if the variable is reassigned inside the function. It looks like it is changing from the global scoped variable to a local scoped one.
The only variable that is important for this issue is current_tics
. Including everything else for completeness
@Device.setup(
autoinit=True
) # ``autoinit=True`` means method will be called during object creation.
def setup():
import board
import touchio
import usb_hid
from adafruit_hid.keyboard import Keyboard
from adafruit_hid.keyboard_layout_us import KeyboardLayoutUS
from adafruit_hid.keycode import Keycode
import neopixel
keyboard = Keyboard(usb_hid.devices)
keyboard_layout = KeyboardLayoutUS(keyboard)
touch1 = touchio.TouchIn(board.TOUCH1)
touch2 = touchio.TouchIn(board.TOUCH2)
pixels = neopixel.NeoPixel(board.NEOPIXEL, 4)
default_color = (1, 1, 1) # color when button not pressed
current_color = default_color # button color or default_color
cycle_length_tics = 600 # loop cycle time --> blink cycle length
cycle_blank_length_tics = cycle_length_tics // 3 # blanking time
current_tics = 0
print(current_tics)
fails here
@Device.task
def broken_vars():
print("====try_touch()============")
print_vars()
print(keyboard)
print(current_tics)
current_tics = 0
print(current_tics)
succeeds here
@Device.task
def broken_vars():
print("====try_touch()============")
print_vars()
print(keyboard)
print(current_tics)
driver program
from trinkeyfunctions import MyDevice
parser = argparse.ArgumentParser()
parser.add_argument("--port", "-p", default="/dev/ttyUSB0")
args = parser.parse_args()
print("starting setup")
device = MyDevice(args.port, startup="") # perform no convenience imports
device.print_vars()
device.broken_vars()
failure message is
NameError: local variable referenced before assignment
It looks like the global scoped variable is replaced with a local one if I reassign it. This means I can't have any loop counters or state between tasks.
I was wondering how you can import libraries other than the ones imported by default by Belay. I've made a @device.task def setup():
function which works well, but is that the intended way? As a side note I'm awaiting Micropython v1.20 which includes mip
(so you should be able to install using mip install <package>
if I've understood correctly). Maybe it would be a nice addition to have a "installing and importing libraries" section in the documentation?
The title may sound odd but the following script gives really weird output:
from belay import Device
device = Device('/dev/ttyUSB5')
@device.setup
def setup():
l1 = [
'foo', 'bar']
l2 = ['foo', 'bar']
l3 = ['foo',
'bar']
l4 = ['foo',
'bar',
'baz']
l5 = [
'foo', 'foo',
'bar', 'bar',
'baz', 'baz']
print('l1', l1)
print('l2', l2)
print('l3', l3)
print('l4', l4)
print('l5', l5)
setup()
Result:
l1 [0, 'bar']
l2 ['foo', 'bar']
l3 ['foo', 0]
l4 ['foo', 0, 0]
l5 [0, 'foo', 0, 'bar', 0, 'baz']
So, the first string in a line is replaced with the integer 0.
Seen with Belay 0.22.3 and Micropython v1.20.0 on an ESP32.
The error does not occur in a task.
First I just wanted to thank you for this library, it's been super helpful!
I've just run in to a problem, I would like to yield a result instead of returning a result.
This is works fine on my pi pico:
from time import sleep
def count():
i = 0
while True:
i += 1
yield i
for index in count():
sleep(1)
print(index)
However when I try and use belay with:
import belay
from time import sleep
device = belay.Device("COM5")
@device.task
def count():
i = 0
while True:
i += 1
yield i
for index in count():
print(index)
I receive the error:
Type "() -> Generator[Literal[1], None, None]" cannot be assigned to type "((...) -> PythonLiteral) | None"
Type "() -> Generator[Literal[1], None, None]" cannot be assigned to type "(...) -> PythonLiteral"
Function return type "Generator[Literal[1], None, None]" is incompatible with type "PythonLiteral"
Type "Generator[Literal[1], None, None]" cannot be assigned to type "PythonLiteral"
Type cannot be assigned to type "None"
"Generator[Literal[1], None, None]" is incompatible with "bool"
"Generator[Literal[1], None, None]" is incompatible with "bytes"
"Generator[Literal[1], None, None]" is incompatible with "int"
Thanks!
I'm now trying to create a class that takes a port as input, creates the Belay.device
in __init__
and has belay-decorated methods. However, I can't do that currently as the decorator depends on device
. I also tried initiating the device outside the class and using it as an argument, but that doesn't help either. Any ideas on how to go about this?
import belay
class BelayDevice():
def __init__(self, port, **kwargs):
device = belay.Device(port, 115200) #'/dev/cu.usbmodem141201'
@device.task
def led_loop(self, period):
from neopixel import NeoPixel
np = NeoPixel(Pin(17), 8)
x = 0
state = False
light = (0,0,0)
while x < 6:
# np.value(state)
if state is False:
light = (255, 255, 255)
else:
light = (0,0,0)
np.fill(light)
state = not state
sleep(period)
x += 1
bel = BelayDevice('/dev/tty.SLAB_USBtoUART')
bel.led_loop(1)
# Traceback (most recent call last):
# File "/Users/roaldarbol/Filen/git-projects/belay/mikkel-test.py", line 3, in <module>
# class BelayDevice():
# File "/Users/roaldarbol/Filen/git-projects/belay/mikkel-test.py", line 7, in BelayDevice
# @device.task
# NameError: name 'device' is not defined
For background, I'd like to be able to connect to multiple controllers and run code separately. I'll use multiprocessing
for that, but I've already got that side of things down - I just need a way that I can easily instantiate a class multiple times.
sync adds a few seconds each time you run which can be problematic in some environments
(host is a rpi so usb tend to be a bit slow)
would be nice if belay could compare and avoid an erase and rewrite each time.
another option would be a freeze command where a checksum file is generated and stored on both the device and host side
I often use list_ports
from serial.tools
to find available devices. However, you always need a few more steps. As it's such a common need I was thinking it would be useful to add into Belay as a helper function. Could be:
from serial.tools import list_ports
def list_devices():
ports = []
for port in list(list_ports.comports()):
ports.append(port.device)
return(ports)
I'll do a PR, so you can comment on that too. ๐
EDIT: This is my first proper pull request on a collaborative project, so hope I've done it right.
On belay
0.15, I could do:
from belay import Device, list_devices
class Pico(Device):
@Device.setup
def setup1(argument=False):
from machine import Pin
led = Pin(25, Pin.OUT)
@Device.task
def led_toggle():
led.toggle()
if __name__ == "__main__":
port = list_devices()[-1]
with Pico(port) as pico:
pico.setup1(argument=True)
pico.led_toggle()
However, I just updated to 0.19, and it no longer works - for good reason of course, as I can't provide an argument to stuff that autoinit
s. However, even adding autoinit=False
gives the error:
from belay import Device, list_devices
class Pico(Device):
@Device.setup(
autoinit=False
)
def setup1(argument=False):
from machine import Pin
led = Pin(25, Pin.OUT)
@Device.task
def led_toggle():
led.toggle()
if __name__ == "__main__":
port = list_devices()[-1]
with Pico(port) as pico:
pico.setup1(argument=True)
pico.led_toggle()
# ValueError: Method <function Pico.setup1 at 0x10699a320> decorated with "@Device.setup(autoinit=True)" must have no arguments.
EDIT: I use this when I have multiple separate setup
routines, and parameters depending e.g. on the number of identical sensors I want to initialize.
Hi,
I got find belay
in search of way to communicate RPi Pico <=> PC via serial (both ways).
I saw examples (i.e. 07_lcd), and it looks like communication is initiate by PC only.
Beside printing something at LCD or return value from some function executed at Pico, I need as well way to get notified about pushed button at Pico (or event form LCD touch screen).
Shortly when message/request was initiate by Pico itself.
Can belay
support this, or maybe you know already existed solution.
Iยดd like to build on belay lib, but it lacks some mechanisms:
While i can use the exceptions (pyboard or serial exceptions) that a thrown to detect a problem, I cannot cleanly re-establish a connection with both resetting the board and restarting my script.
The idea is to automatically restart my belay task when after a devices is resetted or un-plugged > replugged (failproof connection)
(so far i tested only belay with serial connections)
Just a question here. Can I return a bytearray or bytes from a @device.task decorated function? I tried but I'm getting some errors:
bytearray(b'Hello, world!')
<class 'bytearray'>
Traceback (most recent call last):
File "/home/pdietl/unorg/mpy-belay/./demo.py", line 93, in <module>
chip_read(0, len(msg))
File "/home/pdietl/unorg/mpy-belay/.venv/lib/python3.10/site-packages/belay/executers.py", line 141, in func_executer
return self._belay_device._traceback_execute(src_file, src_lineno, name, cmd, record=record)
File "/home/pdietl/unorg/mpy-belay/.venv/lib/python3.10/site-packages/belay/device.py", line 790, in _traceback_execute
res = self(cmd, record=record)
File "/home/pdietl/unorg/mpy-belay/.venv/lib/python3.10/site-packages/belay/device.py", line 286, in __call__
self._board.exec(cmd, data_consumer=data_consumer)
File "/home/pdietl/unorg/mpy-belay/.venv/lib/python3.10/site-packages/belay/pyboard.py", line 592, in exec
ret, ret_err = self.exec_raw(command, data_consumer=data_consumer)
File "/home/pdietl/unorg/mpy-belay/.venv/lib/python3.10/site-packages/belay/pyboard.py", line 584, in exec_raw
return self.follow(timeout, data_consumer)
File "/home/pdietl/unorg/mpy-belay/.venv/lib/python3.10/site-packages/belay/pyboard.py", line 506, in follow
data = self.read_until(b"\x04", timeout=timeout, data_consumer=data_consumer)
File "/home/pdietl/unorg/mpy-belay/.venv/lib/python3.10/site-packages/belay/pyboard.py", line 440, in read_until
data_consumer(data_for_consumer)
File "/home/pdietl/unorg/mpy-belay/.venv/lib/python3.10/site-packages/belay/device.py", line 280, in data_consumer
out = parse_belay_response(line)
File "/home/pdietl/unorg/mpy-belay/.venv/lib/python3.10/site-packages/belay/device.py", line 61, in parse_belay_response
return ast.literal_eval(line)
File "/usr/lib/python3.10/ast.py", line 110, in literal_eval
return _convert(node_or_string)
File "/usr/lib/python3.10/ast.py", line 109, in _convert
return _convert_signed_num(node)
File "/usr/lib/python3.10/ast.py", line 83, in _convert_signed_num
return _convert_num(node)
File "/usr/lib/python3.10/ast.py", line 74, in _convert_num
_raise_malformed_node(node)
File "/usr/lib/python3.10/ast.py", line 71, in _raise_malformed_node
raise ValueError(msg + f': {node!r}')
ValueError: malformed node or string on line 1: <ast.Call object at 0x7ffb4422ca60>
The first print is a print
of the bytearray I want to return.
Again, just running an idea by you to hear your thoughts. Would it be possible to inject calls to the close
method and the class instantiation somehow?
Is there a way one can check if any functions with a particular decorator is present? Then, upon instantiating the class
, we could check to see whether there are any @Device.setup
methods and automatically run them, and similarly, with a @Device.close
, inject them into the default close
method?
For close, the way I work right now, which is still super easy is just to make a new task of some name, say housekeeping
which turns off LEDs, stops motors, etc. I just thought that it would be more intuitive if you could inject them into close
, so you know that all the housekeeping will always be performed when closing the device.
E.g. in this minimal example, the led
would be automatically set up, and turned off:
class Pico(Device):
@Device.setup
def setup():
led = Pin(25, Pin.OUT)
@Device.task
def led_on():
led.on()
@Device.close
def close():
led.off()
if __name__ == "__main__":
pico = Pico(list_devices()[-1])
pico.led_on()
time.sleep(3)
pico.close()
Just a bug report. I'm trying to use the new @Device.setup
decorator (I am on v0.14.1), but I get the following error:
from belay import Device
import time
class PicoW(Device):
@Device.setup
def setup():
led = Pin('LED', Pin.OUT)
@Device.task
def led_toggle():
led.toggle()
if __name__ == "__main__":
picoW = PicoW('/dev/cu.usbmodem14240')
picoW.setup()
picoW.led_toggle()
time.sleep(2)
picoW.led_toggle()
picoW.close()
# Traceback (most recent call last):
# File "/Users/roaldarbol/MEGA/Documents/sussex/research/experiments/experiments/PicoW.py", line 14, in <module>
# picoW = PicoW('/dev/cu.usbmodem14240')
# File "/usr/local/lib/python3.10/site-packages/belay/device.py", line 251, in __init__
# executer = getattr(Executer, metadata.executer.__registry__.name)
# AttributeError: type object 'Executer' has no attribute 'setup'
I am using desktop with an AMD Athlon(tm) II X4 635 Processor the OS is Linux Mint 20.2.
Thonny is installed and my Raspberry PI Pico (with MicroPytho) is connected. In Thonny I can turn the onboard LED on and off, and can get readings from the onboard temperature sensor but cannot get the belay example to run.
The following is my version for the LED example (pico_led.py) annotated to show my configuration and the output when it is run from the terminal:
import belay
# list_ports_linux.py
# /dev/ttyACM0 - Board in FS mode - Board CDC: usb
# udevadm info -q all -a -n /dev/ttyACM0
# looking at parent device '/devices/pci0000:00/0000:00:13.1/usb6/6-2/6-2:1.0'
# ATTRS{interface}=="Board CDC"
# looking at parent device '/devices/pci0000:00/0000:00:13.1/usb6/6-2':
# ATTRS{manufacturer}=="MicroPython"
# ATTRS{product}=="Board in FS mode"
# lsusb
# Bus 006 Device 002: ID 2e8a:0005 MicroPython Board in FS mode
device = belay.Device("/dev/ttyACM0")
from machine import Pin
@device.task
def set_led(state):
print(f"Printing from device; turning LED to {state}.")
Pin(25, Pin.OUT).value(state)
set_led(True)
the following is copied from the terminal:
python3 pico_led.py
Traceback (most recent call last):
File "pico_led.py", line 14, in <module>
device = belay.Device("/dev/ttyACM0")
File "/home/user1/.local/lib/python3.8/site-packages/belay/device.py", line 120, in __init__
self._exec_snippet("startup")
File "/home/user1/.local/lib/python3.8/site-packages/belay/device.py", line 239, in _exec_snippet
snippets = [read_snippet(name) for name in names]
File "/home/user1/.local/lib/python3.8/site-packages/belay/device.py", line 239, in <listcomp>
snippets = [read_snippet(name) for name in names]
File "/home/user1/.local/lib/python3.8/site-packages/belay/helpers.py", line 23, in read_snippet
return importlib.resources.files(snippets).joinpath(resource).read_text()
AttributeError: module 'importlib.resources' has no attribute 'files'
Are there fixes for the scripts in belay
Thanks
Michael
...this is a tentative issue title, until we figure out what the underlying problem should be. Let's see. ;-)
I'd like to be able to use Pimoroni's sensors, and they have lots of libraries for them (found here). However, I can't find the appropriate .py
files, only .cpp
or .hpp
. Any ideas about how to use those? If there's an easy way of using those, then the issue is just this.
They have made their own micropython firmware which includes all their libraries. I tried to just upload their firmware on my Pico, but when loading the libraries I can't (NameError: name 'pimoroni_i2c' isn't defined
). So I guess we don't have access to the on-board libraries - or am I just doing something wrong?
So, perhaps the issue could also be about how to work with alternative firmware. I thought there might be a case for having a section in pyproject.toml
where one could specify a URL to a custom firmware, and if it isn't already loaded, then load that firmware onto the board? I don't know if it'll be confusing, but it would help with "working out of the box".
However, I think it can become problematic with multiple boards, and we would have to put some thought into it.
Once mip
flourishes, it should possibly become redundant, but for now I think there's a need.
So I am calling a function that writes its argument of bytes to a SPI FRAM chip. I call it with a bytes argument of say, 10k bytes. When the function call is over, even when I call gc.collect()
, the memory in microPython keeps going down. Any suggestions?
My function looks like this:
@device.task
def chip_write_remote(bytes_to_write):
print(gc.mem_free())
gc.collect()
# Write enable
cs(0)
spi.write(b'\x06')
cs(1)
# Perform the write
cs(0)
spi.write(bytes_to_write)
cs(1)
del bytes_to_write
print(gc.mem_free())
gc.collect()
print(gc.mem_free())
On my Linux machine I try to take an arbitrary number of bytes and chunk it over to micropython:
def batched(iterable, n):
"Batch data into tuples of length n. The last batch may be shorter."
# batched('ABCDEFG', 3) --> ABC DEF G
if n < 1:
raise ValueError('n must be at least one')
it = iter(iterable)
while batch := bytes(islice(it, n)):
yield batch
def chip_write(address, data_bytes):
assert address >> 18 == 0, "Address must be 18 bits or less!"
for bites in batched(data_bytes, 50 * 1024):
write_buffer = bytearray()
write_buffer.append(0x02)
write_buffer.extend(address.to_bytes(3, byteorder='big'))
write_buffer.extend(data_bytes)
chip_write_remote(write_buffer)
device("import gc; gc.collect()")
i'm cool if belay is really targeted at the Pico RP 2040. But just in case
Firmware: Circuitpython 7.3
Board: Trinkey Neo
Error: no module named 'analogio'
This module does not exist for this distribution of circuitpython on this board.
Code
import argparse
import time
import belay
parser = argparse.ArgumentParser()
parser.add_argument("--port", "-p", default="/dev/ttyUSB0")
args = parser.parse_args()
# Setup the connection with the micropython board.
# This also executes a few common imports on-device.
device = belay.Device(args.port)
Outout
Traceback (most recent call last):
File "C:\Users\joe\Documents\GitHub\Adafruit-Trinkey-CircuitPython\Neo\capacitive-touch-belay\standalone.py", line 12, in <module>
device = belay.Device(args.port)
File "C:\Users\joe\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.9_qbz5n2kfra8p0\LocalCache\local-packages\Python39\site-packages\belay\device.py", line 275, in __init__
self._exec_snippet("convenience_imports_circuitpython")
File "C:\Users\joe\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.9_qbz5n2kfra8p0\LocalCache\local-packages\Python39\site-packages\belay\device.py", line 348, in _exec_snippet
return self("\n".join(snippets))
File "C:\Users\joe\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.9_qbz5n2kfra8p0\LocalCache\local-packages\Python39\site-packages\belay\device.py", line 410, in __call__
self._board.exec(cmd, data_consumer=data_consumer)
File "C:\Users\joe\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.9_qbz5n2kfra8p0\LocalCache\local-packages\Python39\site-packages\belay\pyboard.py", line 524, in exec
raise PyboardException(ret_err.decode())
belay.pyboard.PyboardException:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: no module named 'analogio'
The import fails in the REPL
>>> import board
>>> import analogio
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: no module named 'analogio'
The module is not in the modules list
>>> help("modules")
__future__ adafruit_hid/mouse microcontroller supervisor
__main__ adafruit_pixelbuf micropython sys
adafruit_hid/__init__ array neopixel time
adafruit_hid/consumer_control board neopixel_write touchio
adafruit_hid/consumer_control_code builtins os usb_cdc
adafruit_hid/keyboard collections rainbowio usb_hid
adafruit_hid/keyboard_layout_base digitalio random usb_midi
adafruit_hid/keyboard_layout_us gc storage
adafruit_hid/keycode math struct
Plus any modules on the filesystem
>>>
I think it is because because convenience_imports_circuitpython.py
contains modules that are not on all boards.
import os, time, analogio, board, digitalio
from time import sleep
from busio import I2C, SPI
I feel like a bad penny
I have this boot.py per the instructions and now the device is mounted read only to my pc. I think it changes the drive from writable via circuitpython and read only to the computer. You may wish to add notes on how to remove that boot.py
from the repl so that people can code with files on the drive again.
import storage
storage.remount("/")
I think there may have been a breaking change somewhere. I'm trying a simple generator function, but I only get None
yields. Tried on both my Mac and a student's Windows with Belay 0.15
.
from belay import Device
import time
class PicoW(Device):
@Device.task
def my_generator():
i = 0
while i < 10:
yield i
i += 1
if __name__ == "__main__":
picoW = PicoW(list_devices()[-1])
values = []
for val in picoW.my_generator():
values.append(val)
picoW.close()
print(values)
# [None, None, None, None, None, None, None, None, None, None]
If I print from within the generator I get the correct values, so I believe they're lost during transit somewhere.
I also tried in the old-fashioned way without a class, getting the same results.
Hey Brian. I'm finding myself using the generators as my go-to currently, and started working on a single function to ease with logging data. So something like (roughly copy-pasted out of context):
try:
for val in device.my_generator(iti):
val_pd = pd.DataFrame([val])
data = pd.concat([data, val_pd], ignore_index=True)
data.loc[data.index[-1],'time'] = time.time() - t_init
print(val_pd)
t0 = time.time()
t1 = time.time()
iti_adjusted = iti - ((t0 - t_init) % iti)
while t1 - t0 < iti_adjusted:
t1 = time.time()
except:
pass
Could instead be something like:
values = timed_generator(task, repeats, interval_type, duration, **args, **kwargs)
So I was wondering if that would be something it could make sense to include Belay or if I should make it somewhere separate? I am currently using pandas
which is a major dependency to add onto the project. On the other hand it might be nice to have a few functions to aid various routine tasks, maybe in a separate sub-library. What do you think? I can create a pull request later this week if you'd like to see a draft.
have you thought about ways to implement something to allow 2-way async communication?
i.e. setting up a callback for a hardware interrupt generated from a "board gpio"/button
could also be useful scenarios when you read a sensor that emits its value when its ADC conversion is done, so instead of having a poll loop on the host, it would be nice if a value or fifo can be dispatched on the next (upstream) usb packet transfer => event to belay
Unable to run examples/02_blink_neopixel against an Adafruit Trinkey Neo board running CircuitPython 8.0 release. Haven't tried on 7.x
Belay: 0.16.2
CircuitPython version: 8.0 https://circuitpython.org/board/neopixel_trinkey_m0/
boot.py has been configured per https://belay.readthedocs.io/en/latest/CircuitPython.html
PS C:\Users\joe\Documents\GitHub\belay\examples\02_blink_neopixel> python3 .\circuitpython.py -p COM12
Traceback (most recent call last):
File "C:\Users\joe\Documents\GitHub\belay\examples\02_blink_neopixel\circuitpython.py", line 12, in <module>
device = belay.Device(args.port)
self._connect_to_board(**self._board_kwargs)
File "C:\Users\joe\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.9_qbz5n2kfra8p0\LocalCache\local-packages\Python39\site-packages\belay\device.py", line 337, in _connect_to_board self._board.enter_raw_repl(soft_reset=soft_reset)
File "C:\Users\joe\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.9_qbz5n2kfra8p0\LocalCache\local-packages\Python39\site-packages\belay\pyboard.py", line 421, in enter_raw_repl self.read_until(1, b"soft reboot\r\n")
File "C:\Users\joe\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.9_qbz5n2kfra8p0\LocalCache\local-packages\Python39\site-packages\belay\pyboard.py", line 398, in read_until
raise PyboardError(
belay.pyboard.PyboardError: Timed out reading until b'soft reboot\r\n'
Received: b'OK\r\nraw REPL; CTRL-B to exit\r\n>'
The package manager is already dawning on me. I really really like it! However, I'm a bit confused by:
Belay assumes your project contains a python-package with the same name as tool.belay.name located in the root of your project.
Does that mean you cannot have just a pyproject.toml file, the python script and nothing else in a repo? (except for the autogenerated .belay-lib).
Is it possible to use this without a physically connected board (e.g. via the WebREPL)?
Was testing examples/06_external_modules_and_files with REPL across a Bluetooth Serial connection. The sync operator appears to be doing using the backslash \
character in the path when copying from the windows filesystem to the device file system.
Supposition: \
is the separator for the file system for Python on the PC picked up from the file/dir scan.
Test machine: Windows PC
Target device: Pico RP2040
Windows shell running "python 3" command: Powershell.
Symptoms:
Board directory after test fails.
C:\ESP8266-MicroPython> ls /pyboard/
\somemodule/ \led.py \somemodule\led.py \hello_world.txt
Expected results
C:\ESP8266-MicroPython> ls /pyboard/
somemodule/ led.py somemodule/led.py hello_world.txt
Board directory after manually restoring boot.py into the same device using rshell
builtin cp cp boot-origi.py /pyboard
command
C:\ESP8266-MicroPython> ls /pyboard/
\somemodule/ \led.py \somemodule\led.py boot-orig.py \hello_world.txt
Hi Brian. Thanks for the amazing project! I'm trying to put it to good use for some scientific experiments (neuroscience in case you'd find it interesting ๐). First of all sorry - this is a dual issue, but I think they make sense together. Do feel free to split them into separate issues if you wish.
I'd like to be able to do two specific things:
device.task()
I can only log the function output at the end of the function. And with list_of_values = device.thread()
I haven't been able to return values (I get NoneType
). I could run a loop locally, but it doesn't offer the same time precision I'm after (I can provide an example if needed). I've written about it previously on the MicroPython Github which is where I was recommended to have a look at Belay.poll
on the board and serial.write()
on the local side. See more on the MicroPython forum.Do you think I can achieve this with Belay, or what would be needed? I'd be happy to beta test features if you'd like me to. I can get away with some sloppier code for now, but I'd love to make the most of the tidiness offered by Belay! ๐
Trying to run the LED example on an RP Pico. It blows up with OSError Could not get source code
in a task print statement.
Got to be something obvious
Pico RP2040
MicroPython 1.9.1
Python 3.9.13
PS C:\Users\joe\Documents\GitHub\belay>
>>> parser = argparse.ArgumentParser()
>>> parser.add_argument("--port", "-p", default="COM21")
_StoreAction(option_strings=['--port', '-p'], dest='port', nargs=None, const=None, default='COM21', type=None, choices=None, required=False, help=None, metavar=None)>>> args = parser.parse_args()>>>>>> # Setup the connection with the micropython board.
>>> # This also executes a few common imports on-device.
>>> device = belay.Device(args.port)
>>>>>>... def setup(): # The function name doesn't matter, but is "setup" by convention.... from machine import Pin
... print('hello world')...Traceback (most recent call last):
File "<stdin>", line 2, in <module>
File "C:\Users\joe\Documents\GitHub\belay\belay\executers.py", line 57, in __call__
src_code, src_lineno, src_file = getsource(f, strip_signature=True)
File "C:\Users\joe\Documents\GitHub\belay\belay\inspect.py", line 91, in getsource
lines, src_lineno = inspect.getsourcelines(f)
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.9_3.9.3568.0_x64__qbz5n2kfra8p0\lib\inspect.py", line 1006, in getsourcelines
lines, lnum = findsource(object)
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.9_3.9.3568.0_x64__qbz5n2kfra8p0\lib\inspect.py", line 835, in findsource
raise OSError('could not get source code')
OSError: could not get source code
>>>
This code blows up on print(Hellow World)
import argparse
import time
import belay
parser = argparse.ArgumentParser()
parser.add_argument("--port", "-p", default="COM21")
args = parser.parse_args()
# Setup the connection with the micropython board.
# This also executes a few common imports on-device.
device = belay.Device(args.port)
@device.setup
def setup(): # The function name doesn't matter, but is "setup" by convention.
from machine import Pin
print('hello world')
I've not used the package manager yet, but just based on the documentation it seems that you have to specify URLs to repos. Poetry indexes PyPi, so maybe it could be an idea to do something similar to automatically index both PyPi and micropython-lib? E.g. having
[tools.belay.dependencies.pip]
toml = "^0.10.2"
[tools.belay.dependencies.mip]
logging = "^0.5.2"
or a more Poetry compatible version:
[tools.belay.group.pip.dependencies]
toml = "^0.10.2"
[tools.belay.group.mip.dependencies]
logging = "^0.5.2"
And since Micropython packages can also be in places like pycopy maybe thinking about how to implement specify indexes more generally (like secondary indexes in Poetry) would be worthwhile. ๐
repro:
venv/lib/python3.11/site-packages/belay/cli/new.py", line 6, in <module>
from packaging.utils import canonicalize_name
ModuleNotFoundError: No module named 'packaging'
then:
File "venv/lib/python3.11/site-packages/belay/cli/select.py", line 119, in select
device_index = select_table(
^^^^^^^^^^^^^
File "venv/lib/python3.11/site-packages/belay/cli/questionary_ext.py", line 104, in select_table
raise ValueError("A list of choices needs to be provided.")
ValueError: A list of choices needs to be provided.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.