additions to listing usb-connected development boards

I’ve written in the past about developing tools to list development boards connected to my Linux Mint system via USB. I’d used that opportunity to create the same tool functionality using Python, C++, and Rust (see links below). This time I decided to add the ability to annotate all the found development boards to better track what I had connected. As before, I started with the Python tool because I find Python far easier to solve problems via software solutions.

#!/bin/env python3import jsonimport osimport reDEVICE_PATH = '/dev/serial/by-id/'devices = {}descriptions = {}try:ANNOTATIONS = os.path.dirname(os.path.abspath(__file__)) + '/devices.json'with open(ANNOTATIONS, encoding='utf-8') as annotations_file:data = json.load(annotations_file)for device in data['devices']:descriptions[device['hexid']] = device['description']except FileNotFoundError:print(f"Device annotations file '{ANNOTATIONS}' not found.\n")for device in os.listdir(DEVICE_PATH):link_name = os.path.basename(os.readlink(DEVICE_PATH + device))if "usb-Espressif_USB_JTAG" in device:re_result = re.search(r'_([A-Za-z0-9]+(:[A-Za-z0-9]+)+)-', device)else:re_result = re.search(r'_[0-9A-Fa-f]+-', device)if re_result is None:re_result = re.search(r'_[0-9A-Fa-f]+I', device)interface_name = device.split(re_result[0])[0].replace('usb-','').replace('_',' ')hex_str = re_result[0].lstrip('_').rstrip('-')if hex_str in descriptions:item = [descriptions[hex_str], interface_name]else:item = [hex_str, interface_name]devices[link_name] = itemfor device in sorted(devices.keys()):values = devices[device]print(f"{device}, {values[0]}, {values[1]}")

The original Python script was 19 lines, nearly 1/3 the length of the current Python program. That’s because there’s a lot more going on that before. Let’s see what the output is like when the new Python script’s run.

Device annotations file '/home/mint/Develop/PythonWork/devices.json' not found.ttyACM0, E6614C775B4A4535, Raspberry Pi Pico WttyACM1, 60:55:F9:F7:6C:5B, Espressif USB JTAG serial debug unitttyUSB0, e08d862b0867ec118f12a17089640db2, Silicon Labs CP2102N USB to UART Bridge ControllerttyUSB1, 80e3350df417ec11baa043103803ea95, Silicon Labs CP2102N USB to UART Bridge ControllerttyUSB2, 964756d6d823ed119c228ee8f9a97352, Silicon Labs CP2102N USB to UART Bridge ControllerttyUSB3, 448454f7dffaeb11aa2038a4c6d924ec, Silicon Labs CP2102N USB to UART Bridge ControllerttyUSB4, 7eab1642dbfaeb1198773ca4c6d924ec, Silicon Labs CP2102N USB to UART Bridge Controller

Right now I have seven individual development boards plugged in (lines 3 through 9). Just like in the original Python script, the devices are sorted based on the devices they’ve been assigned by the USB Linux kernel driver. Each line is now organized differently; the assigned port, the unique ID, and the identification of the USB adapter on each board. Line one gives a clue as to what functionality has been added to the Python script. So let’s put the annotations file in the same location where the Python script is located and re-run it.

ttyACM0, Raspberry Pi CircuitPython 8.1 WiFi control LED, Raspberry Pi Pico WttyACM1, ESP32-H2-DevKitM-1-N4 ESP-IDF 5.1-dev blink LED, Espressif USB JTAG serial debug unitttyUSB0, ESP32-S3-DevKitC-1.1-N32R8 ESP-IDF 5.2-dev WiFi, Silicon Labs CP2102N USB to UART Bridge ControllerttyUSB1, ESP32-S3-DevKitC-1.1-N8R8 MicroPython 1.20.0 WiFi, Silicon Labs CP2102N USB to UART Bridge ControllerttyUSB2, ESP32-C6-DevKitC-1-N8 ESP-IDF 5.2-dev NeoPixel, Silicon Labs CP2102N USB to UART Bridge ControllerttyUSB3, ESP32-C3-DevKitC-1-N4 ESP-IDF 4.4.4 ESP Rainmaker, Silicon Labs CP2102N USB to UART Bridge ControllerttyUSB4, ESP32-C3-DevKitC-1-N4 ESP-IDF 5.2-dev Displays, Silicon Labs CP2102N USB to UART Bridge Controller

With the annotations file (devices.json) in the same location the script is executing from, all the unique IDs have been replaced with human-readable notations for each device. That’s because of how the JSON file is organized. Let’s look at the JSON file.

{ "devices" : [{"hexid" : "60:55:F9:F7:6C:5B","description" : "ESP32-H2-DevKitM-1-N4 ESP-IDF 5.1-dev blink LED"},{"hexid" : "E6614C775B4A4535","description" : "Raspberry Pi CircuitPython 8.1 WiFi control LED"},{"hexid" : "e08d862b0867ec118f12a17089640db2","description" : "ESP32-S3-DevKitC-1.1-N32R8 ESP-IDF 5.2-dev WiFi"},{"hexid" : "80e3350df417ec11baa043103803ea95","description" : "ESP32-S3-DevKitC-1.1-N8R8 MicroPython 1.20.0 WiFi"},{"hexid" : "964756d6d823ed119c228ee8f9a97352","description" : "ESP32-C6-DevKitC-1-N8 ESP-IDF 5.2-dev NeoPixel"},{"hexid" : "7eab1642dbfaeb1198773ca4c6d924ec","description" : "ESP32-C3-DevKitC-1-N4 ESP-IDF 5.2-dev Displays"},{"hexid" : "448454f7dffaeb11aa2038a4c6d924ec","description" : "ESP32-C3-DevKitC-1-N4 ESP-IDF 4.4.4 ESP Rainmaker"}] }

Note that in the JSON file how each of the unique IDs are paired with a unique description. Now when the Python script is run, it first attempts to read in the JSON file. If the JSON file is found, the Python script translates the hexid/description pairs into a single descriptions dictionary, in which the hexid is the key. Later in the Python script the unique IDs (hexids) that are parsed from each device are used to look up an annotation in the descriptions dictionary, and if it exists, then replace the unique ID with the description when it’s printed. Otherwise just print out the unique ID. You can thus expand the file devices.json with new entries if needed, and add them to the JSON file later.

The file devices.json must be in the same location as the Python script. That’s what line 11 in the Python script is all about; determining where the Python script is executed from and then looking for the JSON file. If you move the Python file somewhere else, then put the JSON file in the same location.

I’ve also gotten into the habit of running pylint on my Python scripts to make sure all the code smells are deodorized. Thus pylint returns a 9.67 out of 10 score, complaining about a missing module docstring. If all pylint’s complaining about is a missing docstring, then I’ll live with that and move on.

I hope you find this useful.

Link

python vs c++ vs rust — a personal adventure

scanning for wifi networks

CircuitPython 8.1 final was released this past Tuesday on 23 May. I’ve been experimenting with it since downloading images for my Adafruit ESP32-S3 Feather and Raspberry Pi Pico W. What I’ve been looking at is how WiFi is handled on both chips.

The first thing I always do when I get either a new device, or a new version of CircuitPython, or both, is to put a few lines of code onto the code.py file and then have the device execute it and print out the results via the REPL. I use Thonny as the serial interface, because I can also use the tool for developing MicroPython. The critical difference between MicroPython and CircuitPython is that MicroPython does not expose the device’s flash memory as a read/write drive on a hosting computer. CircuitPython does, which makes editing code a lot easier. Anyway…

This is the code.

import binascii as baimport wifinetworks = {}for network in wifi.radio.start_scanning_networks():if len(network.ssid) > 0 and network.ssid[0] != '\x00':networks[network.ssid] = networkwifi.radio.stop_scanning_networks()for ssid in sorted(networks):print("ssid:",ssid, "rssi:",networks[ssid].rssi)

And here is a typical output via Thonny and the REPL.

ssid: ESP32S3-4EF0 rssi: -49ssid: GuestNetwork rssi: -52ssid: Hershey rssi: -98ssid: Miller guest rssi: -94ssid: NETGEAR04 rssi: -92ssid: NETGEAR80 rssi: -90ssid: SmartLife-EEFB rssi: -77

You may find older versions of this code that use an array instead of a dictionary as I did on line 3 of the source. And you many wonder about the conditional test on line 5. Using the older code available all over the web, I was getting duplicate access points as well as what appeared to be junk access points without a valid ssid. The dictionary eliminates duplicates. The conditional test looks to see if the ssid is filled full of binary zeroes instead ascii characters. Since a null-filled ssid is completely filled with binary zeroes, all I have to do is test the first character for a binary zero. If the ssid string is greater than zero and if the first character isn’t binary zero, then add it to the dictionary. When I then sort and print out the dictionary, I get a nice clean alphabetized listing.

I don’t know if the zeroed ssid string is a feature or a bug, but I don’t remember running into this with earlier releases.