How-To

DEBUG

SERIAL DEVICES

While working with new serial devices be aware of:

  • Use a USB data cable, otherwise the device will be powered on but won’t be accessible.

  • If using a device with no native USB support (e.g. esp32, esp8266) most likely it will have a CP210x USB to UART Bridge Virtual COM Port (VCP) chip (or something similar), so the up to date drivers will be needed. (e.g. Silicon labs VCP Drivers) 1

  • If using Linux, it may be necessary to add user to dialout group to allow access to the USB device. 2

$ sudo usermod -a -G dialout $USER
1

This may require reset the computer so the drivers can be loaded properly.

2

See How do I allow a non-default user to use serial device ttyUSB0?


WEBSOCKET DEVICES (WIFI)

While working with devices connected over WiFi, in order to succeed sending upydev commands make sure that there is a reliable connection between the host and the device and that the wifi signal strength (rssi) in the device is above -80 (below -80 performance could be inconsistent)

A ‘Reliable’ connection means that there is no packet loss (use ping or upydev ping command to check)

See Received signal strength indication and Mobile Signal Strength Recommendations

TRACKING PACKETS

To see if “command packets” are sent and received or lost use Wireshark and filter the ip of the device.

SEE WHAT’S GOING ON UNDER THE HOOD:

ℹ️ Host and the device must be connected.

In a terminal window open a ‘serial repl’ with upydev srepl –port [USBPORT] command

In another window use upydev normally. Now in the terminal window with the serial repl you can see which commands are sent.

Working with dchp_hostname instead of IP

To do this activate station interface and set dhcp_hostname before connecting to the WLAN, e.g. in MicroPython REPL/script

>>> import network
>>> wlan = network.WLAN(network.STA_IF)
>>> wlan.active(True)
>>> wlan.config(dhcp_hostname='esp_dev')

After connecting, the device should be reachable as esp_dev.local, do $ ping esp_dev.local, avahi-resolve --name esp_dev.local or $ sudo resolvectl query esp_dev.local to check that it is working as expected.

$ ping esp_dev.local
PING esp_dev.local (192.168.1.73): 56 data bytes
64 bytes from 192.168.1.73: icmp_seq=0 ttl=255 time=2.403 ms
64 bytes from 192.168.1.73: icmp_seq=1 ttl=255 time=219.618 ms
64 bytes from 192.168.1.73: icmp_seq=2 ttl=255 time=239.348 ms
^C
--- esp_dev.local ping statistics ---
3 packets transmitted, 3 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 2.403/153.790/239.348/107.349 ms

$ avahi-resolve --name esp_dev.local

esp_dev.local     192.168.1.73

$ sudo resolvectl query esp_dev.local

esp_dev.local: 192.168.1.73                     -- link: enp5s0

Now is possible to use this dhcp_hostname for -t argument while configuring a device e.g.

$ upydev config -t esp_dev.local -p mypass -@ esp_dev -gg
WebSocketDevice esp_dev settings saved in global group!

$ upydev check -@ esp_dev
Device: esp_dev
Address: esp_dev.local, Device Type: WebSocketDevice

$ upydev check -@ esp_dev -i
Device: esp_dev
WebSocketDevice @ ws://192.168.1.73:8266, Type: esp32, Class: WebSocketDevice
Firmware: MicroPython v1.12-63-g1c849d63a on 2020-01-14; ESP32 module with ESP32
(MAC: 30:ae:a4:1e:73:f8, RSSI: -38 dBm)

$ upydev ping -@ esp_dev
PING esp_dev.local (192.168.1.73): 56 data bytes
64 bytes from 192.168.1.73: icmp_seq=0 ttl=255 time=56.655 ms
64 bytes from 192.168.1.73: icmp_seq=1 ttl=255 time=75.751 ms
^C
--- esp_dev.local ping statistics ---
2 packets transmitted, 2 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 56.655/66.203/75.751/9.548 ms

Note

Be aware some systems default ping use ipv6 first, and fallback to ipv4 while resolving mDNS names, which may cause some delay. Use ping -4 instead which will use ipv4 directly and resolve the name faster.


BLUETOOTH LOW ENERGY DEVICES

See Bleak Troubleshooting


WEBSOCKET DEVICES (WIFI) THROUGH ZEROTIER GLOBAL AREA NETWORK

See ZeroTier Global Area Network

Although there is no library to directly connect a microcontroller to a zerotier network, a raspberry pi can be used as a bridge to make it possible. So install zerotier in your computer and in the raspberry pi.

Setup a zerotier network, add both your computer and the raspberry pi. (guide) Now add the rules for port fordwarding e.g. for WebREPL port (8266) in the raspberry pi and device with IP 192.168.1.46

First enable port forwarding by editing /etc/sysctl.conf and uncomment

net.ipv4.ip_forward=1

And reload

$ sudo sysctl -p
net.ipv4.ip_forward = 1

Then set the rules with iptables

$ sudo iptables -t nat -A PREROUTING -p tcp --dport 8266 -j DNAT --to-destination 192.168.1.46:8266
$ sudo iptables -t nat -A POSTROUTING -j MASQUERADE

And if using a firewall e.g. ufw

$ sudo ufw allow 8266
$ sudo ufw route allow in on ztrta7qtbo out on wlan0 to 192.168.1.46 port 8266 from any
$ sudo ufw reload

Where ztrta7qtbo is the zerotier interface (check this and its IP with ifconfig) Now connecting to the raspberry pi zerotier IP and port 8266 should redirect the traffic to the microcontroller port 8266 (WebREPL), e.g.

$ upydev config -t 142.64.115.62 -p mypass -gg -@ zerotdevice

Where 142.64.115.62 is the IP of the raspberry pi zerotier interface.

To configure shell-repl with WebSecureREPL through zerotier network do the same as above but with port 8833.

To enable ota firmware updates (e.g your computer has a zerotier IP 142.64.115.75)

$ sudo iptables -t nat -A PREROUTING -p tcp --dport 8014 -j DNAT --to-destination 142.64.115.75:8014
$ sudo iptables -t nat -A POSTROUTING -j MASQUERADE

And if using a firewall e.g. ufw

$ sudo ufw allow 8014
$ sudo ufw route allow in on wlan0 out on ztrta7qtbo to 142.64.115.75 port 8014 from any
$ sudo ufw reload

Note

If $ sudo zerotier-cli info shows this error: Error connecting to the ZeroTier service:

Please check that the service is running and that TCP port 9993 can be contacted via 127.0.0.1.

Add this rule $ sudo iptables -t nat -I POSTROUTING -o lo -j ACCEPT

Now shell-repl mode is available using -zt option: e.g.

$ upydev shl@zerotdevice -zt 142.64.115.75/192.168.1.79

Where 192.168.1.79 is the IP of the raspberry pi in the local area network.

Or configure a device with the -zt option so it is not required anymore, e.g.

$ upydev config -t 142.64.115.62 -p mypass -gg -@ zerowpy -zt 142.64.115.75/192.168.1.79
WebSocketDevice zerotdevice settings saved in global group!
WebSocketDevice zerotdevice settings saved in ZeroTier group!

Now to access the shell-repl mode through zerotier network:

$ upydev shl@zerotdevice

Note

To allow ping and probe work correctly instead of pinging the raspberry pi, add the ssh alias of the raspberry pi and the local ip or mDNS name of the device to -zt option as :[ALIAS]/[DEVICE_IP] e.g. :

$ upydev config -t 142.64.115.62 -p mypass -gg -@ zerowpy -zt 142.64.115.75/192.168.1.79:rpi/192.168.1.46
# OR
upydev config -t 142.64.115.62 -p mypass -gg -@ zerowpy -zt 142.64.115.75/192.168.1.79:rpi/weatpy.local

This expects the raspberry pi to be accesible through ssh [ALIAS], and the keys added to the ssh-agent. See ssh add keys and ssh alias

Now ping and probe should actually reach the device through raspbery pi ping, e.g.:

$ upydev ping -@ zerowpy

TESTING DEVICES WITH PYTEST

upydevice device classes allow to test MicroPython code in devices interactively with pytest, e.g. button press, screen swipes, sensor calibration, actuators, servo/stepper/dc motors , etc. Under tests directory there are example tests to run with devices. e.g.

$ upydev pytest test_esp_serial.py -@ sdev
Running pytest with Device: sdev
============================================================= test session starts =============================================================
platform darwin -- Python 3.7.9, pytest-6.1.0, py-1.9.0, pluggy-0.13.1
rootdir: /Users/carlosgilgonzalez/Desktop/MICROPYTHON/TOOLS/upydevice/test, configfile: pytest.ini
collected 7 items

test_esp_serial.py::test_devname PASSED
test_esp_serial.py::test_platform
---------------------------------------------------------------- live log call ----------------------------------------------------------------
22:34:14 [pytest] [ESP32] : Running SerialDevice test...
22:34:14 [pytest] [ESP32] : DEV PLATFORM: esp32
SerialDevice @ /dev/tty.SLAB_USBtoUART, Type: esp32, Class: SerialDevice
Firmware: MicroPython v1.16 on 2021-06-24; ESP32 module with ESP32
CP2104 USB to UART Bridge Controller, Manufacturer: Silicon Labs
(MAC: 30:ae:a4:23:35:64)
22:34:14 [pytest] [ESP32] : DEV PLATFORM TEST: [✔]
Test Result: PASSED
test_esp_serial.py::test_blink_led LED: ON
LED: OFF
LED: ON
LED: OFF

---------------------------------------------------------------- live log call ----------------------------------------------------------------
22:34:17 [pytest] [ESP32] : BLINK LED TEST: [✔]
Test Result: PASSED
test_esp_serial.py::test_run_script
---------------------------------------------------------------- live log call ----------------------------------------------------------------
22:34:17 [pytest] [ESP32] : RUN SCRIPT TEST: test_code.py
2000-01-01 00:53:30 [log_test] [INFO] Test message2: 100(foobar)
2000-01-01 00:53:30 [log_test] [WARN] Test message3: %d(%s)
2000-01-01 00:53:30 [log_test] [ERROR] Test message4
2000-01-01 00:53:30 [log_test] [CRIT] Test message5
2000-01-01 00:53:30 [None] [INFO] Test message6
2000-01-01 00:53:30 [log_test] [ERROR] Exception Ocurred
Traceback (most recent call last):
File "test_code.py", line 14, in <module>
ZeroDivisionError: divide by zero
2000-01-01 00:53:30 [errorlog_test] [ERROR] Exception Ocurred
Traceback (most recent call last):
File "test_code.py", line 20, in <module>
ZeroDivisionError: divide by zero
22:34:18 [pytest] [ESP32] : RUN SCRIPT TEST: [✔]
Test Result: PASSED
test_esp_serial.py::test_raise_device_exception
---------------------------------------------------------------- live log call ----------------------------------------------------------------
22:34:18 [pytest] [ESP32] : DEVICE EXCEPTION TEST: b = 1/0
[DeviceError]:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: divide by zero

22:34:18 [pytest] [ESP32] : DEVICE EXCEPTION TEST: [✔]
Test Result: PASSED
test_esp_serial.py::test_reset
---------------------------------------------------------------- live log call ----------------------------------------------------------------
22:34:18 [pytest] [ESP32] : DEVICE RESET TEST
Rebooting device...
Done!
22:34:18 [pytest] [ESP32] : DEVICE RESET TEST: [✔]
Test Result: PASSED
test_esp_serial.py::test_disconnect
---------------------------------------------------------------- live log call ----------------------------------------------------------------
22:34:18 [pytest] [ESP32] : DEVICE DISCONNECT TEST
22:34:18 [pytest] [ESP32] : DEVICE DISCONNECT TEST: [✔]
Test Result: PASSED

============================================================== 7 passed in 5.20s ==============================================================

IDE INTEGRATION with PLATFORMIO TERMINAL

Visual Studio Code

Using tasks and adding the shortcut in keybinds.json file for example:

Task:

"version": "2.0.0",
    "tasks": [
        {
            "label": "upydev_upload",
            "type": "shell",
            "command": "upydev",
            "args": ["put", "-f", "${file}"],
            "options": { "cwd": "${workspaceFolder}"},
            "presentation": { "echo": true,
                "reveal": "always",
                "focus": true,
                "panel": "shared",
                "showReuseMessage": true,
                "clear": false
            },
            "problemMatcher": []
        }]

Keybinding.json

{ "key": "ctrl+cmd+u",
  "command": "workbench.action.tasks.runTask",
  "args": "upydev_upload"}