Connections
This section describes the connection with the device, as well as other elements regarding the connection of the device.
Reconnect
In the event that the device is temporarily disconnected, Belay can re-attempt to connect to the device and restore state. Typically, this will only work with projects that are purely sensor/actuator IO and do not have complicated internal states.
To enable this feature, set the keyword attempts
when declaring your Device
.
Belay will attempt up to attempts
times to reconnect to the device with
1 second delay in-between attempts. If Belay cannot restore the connection, it will raise
a ConnectionLost
exception.
Example:
device = Device("/dev/ttyUSB0", attempts=10)
By default, attempts=0
, meaning that Belay will not attempt to reconnect with the device.
If attempts
is set to a negative value, Belay will infinitely attempt to reconnect with the device.
If using a serial connection, a serial device __might__ not be assigned to the name upon reconnecting.
See the UDev Rules section for ways to ensure the same name is assigned upon reconnection.
How State is Restored
This section describes how the state is restored on-device, so the user can understand the limitations of Belay's reconnect feature.
When Belay sends a command to the device, the command is recorded into a command history. Function/generator calls are not recorded. These calls are expected to be frequent and not significantly modify the device's internal state.
On device disconnect, nothing happens.
On the next attempted Belay call, Belay will begin to attempt to reconnect with the device. This inherently resets the device, and consequently resets the device's python interpreter state.
Upon reconnection, Belay will replay the entire call history. For most projects, this should be relatively short and typically includes things like:
File-syncs:
device.sync("board/")
Library imports:
device("import mysensor")
Global object creations:
device("sensor = mysensor.Sensor()")
Task definitions:
@device.task def read_sensor(): return sensor.read()
This history replay can result in a longer-than-expected blocking call.
Interface
Belay currently supports two connection interfaces:
Serial, typically over a USB cable. Recommended connection method.
WebREPL, typically over WiFi. Experimental and relatively slow due to higher command latency.
Serial
This is the typical connection method over a cable and is fairly self-explanatory.
UDev Rules
To ensure your serial device always connects with the same name, we can create a udev rule.
Invoke lsusb
to figure out some device information; the response should look like:
belay:~/belay$ lsusb
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 003: ID 239a:80f4 Adafruit Pico
Bus 001 Device 002: ID 2109:3431 VIA Labs, Inc. Hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Left of the colon is the 4-character idVendor
value, and right of the colon is the 4-character idProduct
value.
Next, edit a file at /etc/udev/rules.d/99-usb-serial.rules
to contain:
SUBSYSTEM=="tty", ATTRS{idVendor}=="xxxx", ATTRS{idProduct}=="yyyy", SYMLINK+="target"
For example, the following will map the "Adafruit Pico" to /dev/ttyACM10
:
SUBSYSTEM=="tty", ATTRS{idVendor}=="239a", ATTRS{idProduct}=="80f4", SYMLINK+="ttyACM10"
Finally, the following command will reload the udev rules without having to do a reboot:
sudo udevadm control --reload-rules && sudo udevadm trigger
WebREPL
WebREPL is a protocol for accessing a MicroPython REPL over websockets.
WebREPL requires the MicroPython-bundled webrepl
server running on-device.
To run the WebREPL server on boot, we need two files on device:
boot.py
that connects to your WiFi and starts the server.webrepl_cfg.py
that contains the password to access the WebREPL interface.
These files may look like (tested on an ESP32):
###########
# boot.py #
###########
def do_connect(ssid, pwd):
import network
sta_if = network.WLAN(network.STA_IF)
if not sta_if.isconnected():
print("connecting to network...")
sta_if.active(True)
sta_if.connect(ssid, pwd)
while not sta_if.isconnected():
pass
print("network config:", sta_if.ifconfig())
# Attempt to connect to WiFi network
do_connect("YOUR WIFI SSID", "YOUR WIFI PASSWORD")
import webrepl
webrepl.start()
##################
# webrepl_cfg.py #
##################
PASS = "python"
Once these files are on-device, connect to the device by providing the
correct IP address and password. The ws://
prefix tells Belay to
use WebREPL.
device = belay.Device("ws://192.168.1.100", password="python")