Identifying a special Pico

RP2040 based microcontroller boards running MicroPython.
Target audience: MicroPython users with an RP2040 boards.
This does not include conventional Linux-based Raspberry Pi boards.
Post Reply
User avatar
jcf
Posts: 12
Joined: Wed Mar 03, 2021 11:14 am

Identifying a special Pico

Post by jcf » Wed Apr 28, 2021 4:44 pm

From my Python software on the computer I want to identify one special Pico that holds the script for my application, under several different controllers. It seems as if I had a working solution. :D

To better understand why I need this: I have a Pico that controls a DDS generator that should work as standalone device or remotely controlled by computer. The problem with USB devices is that they can always change their device names.

1. step: Identify all the Picos under all devices:

Code: Select all

from serial.tools import list_ports
def scan_for_picos():
    '''returns list of connected Raspi Picos'''
    picos = []
    for port in list_ports.comports():
        print("Checking ", port.device)
        if port.manufacturer != None:
            if "MicroPython" in port.manufacturer:
                picos.append(port.device)
    return picos
2. step: from these which one is the right one?
On my Pico I have a file called 'info.py' containing just one function:

Code: Select all

def info():
    print( "PICO_2")			# PICO_2 =  name of my Pico
From the computer I do this:

Code: Select all

for pico in scan_for_picos():
    info = get_info_pico(pico, 0.5)
    print (pico, '  INFO:  ',   info)
where the function get_info_pico is defined like this:

Code: Select all

def get_info_pico(pico, timeout):
    s=serial.Serial(pico, baudrate=115200)
    if s.isOpen()==False:
        s.open()
    
    # send commands
    s.write(b'\x03\x03')        # Interrupt eventually running program
    s.write(b'\x02')            # Normal REPL
    s.write(b'import info\r')
    s.write(b'info.info()\r')
    
    # Receive answer (with timeout)
    text = ''
    t1 = time.time()
    while True:
        nbbytes = s.inWaiting()
        if nbbytes >0:
            c = s.read(nbbytes)
            c = c.decode("utf-8")
            text += c    
        if time.time() - t1 > timeout:
            break
    
    # Analize answer
    if 'Traceback' in text:                 # Something went wrong
        info = ''
    else:                                   # Filter interesting part    
        keyword = 'info.info()\r'
        pos1 = text.find(keyword)
        info = text[pos1 + len(keyword):]
        pos2 = info.find('\r')
        info = info[:pos2]       
        info = info.strip() 
        
    return info 
The result may look like this:

Code: Select all

Checking  /dev/ttyS5
...
Checking  /dev/ttyACM1
/dev/ttyACM0   INFO:   SINGEN
/dev/ttyACM1   INFO:   PICO_2
with the names of the Picos being SINGEN and PICO_2

Sorry for the length of this posting!

hippy
Posts: 35
Joined: Sat Feb 20, 2021 2:46 pm
Location: UK

Re: Identifying a special Pico

Post by hippy » Wed Apr 28, 2021 6:16 pm

Instead of importing a module with your .info function, you could create a text file on the Pico, for example run this once -

Code: Select all

with open("info.txt", "w") as f:
  f.write("PICO_2")
Then you can have the Pico report what's in that text file -

Code: Select all

try:
  with open("info.txt", "r") as f:
    result = f.read().strip()
except:
  result = "Unknown!"
print(result)

scruss
Posts: 79
Joined: Sat Aug 12, 2017 2:27 pm

Re: Identifying a special Pico

Post by scruss » Wed Apr 28, 2021 8:39 pm

If you're needing the Picos to identify themselves under MicroPython:

Code: Select all

import machine
import ubinascii

my_id = ubinascii.hexlify(machine.unique_id()).decode()
print(my_id)
This will be a unique hex string for each device.

If you're trying to identify them as connected to a Linux machine, unfortunately /dev/serial/by-id/ doesn't produce different results for different boards. They'll all likely appear as something with the serial number set to '000000000000'

User avatar
jcf
Posts: 12
Joined: Wed Mar 03, 2021 11:14 am

Re: Identifying a special Pico

Post by jcf » Thu Apr 29, 2021 8:03 am

Thanks for the answers!

@hippy
I have thought about a text file, too.
That could be opened from remote by sending open and read commands
The charm of the import solution pleased me however. In the end it may not be simpler. But a good part of my code is to interrupt a running program, receive and analize the answer, and that would be about the same.

@scruss
That's interesting, though it is not exactly what I need. I rather want to find the Pico with the right software than the Pico with the right hardware ID.

User avatar
jcf
Posts: 12
Joined: Wed Mar 03, 2021 11:14 am

Re: Identifying a special Pico

Post by jcf » Thu Apr 29, 2021 9:42 am

I have tried the other variant with a file "info.txt" on the Pico containing just a keyword in the first line (and maybe more text below).
Maybe it is more straightforward than the import variant.

This function on the computer gives the keyword:

Code: Select all

def get_info_pico_2(pico, timeout):
    '''
    Get info stored on the pico in a file info.txt
        This must contain a keyword in the first line:
        The keyword is returned  
        (pico is a port name like '/dev/ttyACM0')
    '''
    s=serial.Serial(pico, baudrate=115200)
    if s.isOpen()==False:
        s.open()
    
    # send commands
    s.write(b'\x03\x03')        # Interrupt eventually running program
    s.write(b'\x02')            # Normal REPL
    s.write(b'f = open("info.txt", "r")\r')
    s.write(b't = f.read()\r')
    s.write(b'f.close()\r')
    s.write(b'print(t)\r')
        
    # Receive answer (with timeout)
    text = ''
    t1 = time.time()
    while True:
        nbbytes = s.inWaiting()
        if nbbytes >0:
            c = s.read(nbbytes)
            c = c.decode("utf-8")
            text += c    
        if time.time() - t1 > timeout:
            break
    
    # Analize answer
    if 'Traceback' in text:                 # Something went wrong
        info = ''
    else:                                   # Filter interesting part    
        keyword = 'print(t)'
        pos1 = text.find(keyword)
        info = text[pos1 + len(keyword) +1:]  # keyword found behind print()
        info = info.split()[0]                # take first line only    
        info = info.strip() 
        
    return info 
As in the above example, you have first to identify the ports with a pico.

To find a special pico, I can use this:

Code: Select all

def find_pico(keyword):
    for pico in scan_for_picos():
        info = get_info_pico(pico, 0.5)
        if info == keyword:
            break
    return pico

User avatar
jcf
Posts: 12
Joined: Wed Mar 03, 2021 11:14 am

Re: Identifying a special Pico

Post by jcf » Fri Apr 30, 2021 3:52 pm

Though everything seems to work I am thinking about an improvement by using the Raw REPL

User avatar
Roberthh
Posts: 2707
Joined: Sat May 09, 2015 4:13 pm
Location: Rhineland, Europe

Re: Identifying a special Pico

Post by Roberthh » Fri Apr 30, 2021 4:55 pm

There is a PR in the chain for inserting the unique id into the usb id, as being visible in /dev/serial/by-id.

User avatar
Roberthh
Posts: 2707
Joined: Sat May 09, 2015 4:13 pm
Location: Rhineland, Europe

Re: Identifying a special Pico

Post by Roberthh » Mon May 03, 2021 7:46 pm

The PR has been merged and should be accessible latest tomorrow in the daily build. Devices are visible then is /dev/serial/by-id e.g. as

usb-MicroPython_Board_in_FS_mode_e66058388340512e-if00

with e66058388340512e being the number returned by machine.unique_id()

User avatar
jcf
Posts: 12
Joined: Wed Mar 03, 2021 11:14 am

Re: Identifying a special Pico

Post by jcf » Wed May 05, 2021 4:23 pm

That's fine.
But as I have already mentioned, I am more interested in identifying a Pico containing the right software than in identifying the hardware.

Anyway, I have learned a lot working on this.
I have summarized my experience here:
http://staff.ltam.lu/feljc/electronics/ ... cation.pdf

scruss
Posts: 79
Joined: Sat Aug 12, 2017 2:27 pm

Re: Identifying a special Pico

Post by scruss » Thu May 06, 2021 2:04 am

Fair enough. I'd do something like this:

Code: Select all

for f in /dev/serial/by-id/usb-MicroPython_*; do ls -la "$f"; ampy --port "$f" ls; echo '----------------------'; done
lrwxrwxrwx 1 root root 13 May  5 21:53 /dev/serial/by-id/usb-MicroPython_Board_in_FS_mode_e66058388375982e-if00 -> ../../ttyACM3
/hello.py
/id.py
/lib
/main.py
----------------------
lrwxrwxrwx 1 root root 13 Apr 12 15:28 /dev/serial/by-id/usb-MicroPython_Pyboard_Virtual_Comm_Port_in_FS_Mode_336D386B3339-if01 -> ../../ttyACM2
/flash
/sd
----------------------
If /dev/serial/by-id/usb-MicroPython_Board_in_FS_mode_e66058388375982e-if00 turned out to be the Pico I wanted from its contents, I know it would always show up at that port address

Post Reply