My ultimate goal is to control a number of peripheral chips on a PCB from a GUI interface on a PC. To do so, my plan was to incorporate a RP2040 (and memory) on the PCB in order to hold all the python scripts and to program/monitor all the peripheral chips. Then, using a PC to interface with the RP2040, send commands over the serial port to execute specific python files on the pico.
I realize that is a bit confusing, so the attached block diagram should help.
Block diagram
Starting on the left of the block diagram, I have a PC running a tkinter GUI. I am currently running the tkinter gui in Thonny. (eventually i would like it to be an executable, but that is beyond the scope of this post) The gui has a number of buttons to choose which python scripts to run. The PC is connected to the PCB through the USB cable. The USB data lines are routed to the RP2040's USB inputs (pins 47,48). The memory on the PCB holds a number of python scrips that correspond to the buttons in the GUI. Ideally, pressing a button on the PC would execute the corresponding py file on the pcb.
What I've got working so far:
My real expertise lies in peripheral chips and PCB design, in this case the front end for a 2-18GHz transceiver, so bare with me if some of my python questions seem basic or misinformed. I've written and tested all the .py files on the pico's memory. To test those scripts I used Thonny to connect to my pico and simply ran (f5) the scripts with the peripherals connected to the right GPIOs. I was also able to get tkinter working and create functioning buttons that can execute commands. Using the pyserial module, I am also able to connected to the pico through the USB and write... strings. Not very useful, but a start.
import serial
ser = serial.Serial('COM3', 38400, timeout=1, parity=serial.PARITY_EVEN, rtscts=1)
s = ser.read(100) # read up to one hundred bytes or as much is in the buffer
print(ser.name) # check which port was really used
ser.write(b'ToggleLED.py') # write a string
ser.close() # close port
Remaining task: The final task I have been failing miserably at the past 2 days has been actually trying to execute the .py files located on the pico's memory through the serial port. My unexperienced/naïve notion was to simply send a string with the files name, obviously not correct. Any thoughts on how to execute those py files using that pyserial module?
BTW, if there is a better solution, please feel free to share! Perhaps the files should be located on the PC and i send 1 command at time?
I can't say anything about your serial problems until you clarify just what is running on the pi (see my comment: I'll update this answer when you do), but re 'is there a better way': possibly.
Since the Pi is running a full operating system, there are a few options. You are basically creating a network connection to the Pi. Whilst this can be done over serial (with the Pi, presumably, acting as a fake USB serial device), it can also be done more conventionally over wifi or ethernet. Lastly, you could host your interface on the Pi and interact with it in a webbrowser, cutting the second computer out of the picture. Exactly which option you decide to take is up to you, and is really off topic here (though it might be on topic elsewhere on SE).
Sending commands to the pi and having it run scripts is Remote Procedure Call. You might want to lookup some of the protocols (like JSON-RPC) generally used to do that, but the basic approach will have code running on the pi:
def do_something():
pass
def do_something_else():
pass
functions = {"something": do_something, "something_else": do_something_else}
while True:
cmd = get_input() # blocks until input comes
if cmd in functions:
reply(f"Running {cmd}")
output = functions[cmd]()
reply(f"{cmd} returned with output {output}")
else:
reply(f"Invalid command {cmd}")
This is schematic: exactly what get_input() on the pi is will depend on how you end up connecting, and what protocol (if any) you end up using. Notice that I have built in confirmation: you want to know if things work or fail.
Whilst you could store these commands in separate scripts and invoke them, if they are just python scripts there is no reason not to call the functions directly from the code running on the pi.
I've seen two different solutions to the question:
With the Pi pico running the REPL (python shell), text strings can simply be sent from a python program running on the host (Windows or Raspberry Pi, etc., connected to the USB com port, with Thonny window closed), and sent to the pico as python commands. This approach is detailed in:
https://blog.rareschool.com/2021/01/controlling-raspberry-pi-pico-using.html
On the pico side, one just need to define a number of functions that will execute from the REPL. E.g.,:
from machine import Pin
# use onboard LED which is controlled by Pin 25
led = Pin(25, Pin.OUT)
# Turn the LED on
def on():
led.value(1)
# Turn the LED off
def off():
led.value(0)
Any string sent to the pico, such as
on()
will be executed accordingly.
In the 2nd approach, the pico will read each line from the USB serial port and parse the line into command and additional parameters, to be compared against a list of predefined commands and executed accordingly. I did that using the Arduino Due, and I'm in the process of porting it to the pico (= no code to show yet :-().
I am trying to make two different scripts read from the same serial port using pyserial. The script that runs after the first script will try to open the port again, which causes an error. I have tried to remove the s_inst.open() but that would also cause an error because now it says the port is invalid, because I haven't opened it in the same script.
This is the piece of code I have in both separate scripts (I have also tried this without the if statement, and some other stuff, but I couldn't find anything that would work, I am stuck):
s_inst = serial.Serial()
#The microbit's baudrate
s_inst.baudrate = 115200
s_inst.port ='COM3'
if(s_inst.isOpen() == False):
s_inst.open()
I'm trying to combine two programs in Python 3 that use the serial ports. I'll use different USB cables for each controller. One program reads serial data coming in once a minute. The second program will use the other USB serial port once every 15 minutes. Since both programs spend a lot of time doing nothing, I want to combine them. This will be running on both a Mac (designing) and Windows (final operation).
My question is: If program #2 is using it's serial port when data comes in for program #1, when program #1 looks at it's serial port, will the data be there waiting on it?
I am doing a serial communication in python between a RPi and a camera. I send some data from the RPi using ser.write() and read data from the camera in the RPi using ser.read(). Then I would like to know what would ser.flush(), ser.flushinput() and ser.flushoutput() do if I add these after the read command.
I assume ser.flush() will make the program wait till all data from buffer memory is read. But I don't understand what the other two will do
Can someone tell me, what is the difference between these three when used in serial communication and what will happen when I use each of them separately after the ser.write() or ser.read().
I am currently trying to capture serial data within a python script. I intend to begin capturing a log of all the data captured on a serial port while the rest of the script continues to interact with the system I am testing.
If I use pyserial I believe it will end up blocking the rest of the tests I want to carry out until I finish logging.
My options I have considered are:
Writing another script to capture logs using pyserial, call this script using subprocess.Popen()
Using built in unix tools such as tail or cat and calling these with subprocess.Popen()
I am sure I could find a way to get either of these to work, but if anyone knows of a more direct way of doing it then I would love to know.
Thank you in advance.
Why create another process for reading data from pySerial ?
For non-blocking the read you can configure the timeout in serial class.
e.g.
ser = serial.Serial()
ser.baudrate = 19200
ser.port = 0
ser.timeout = 2 #By default, this is set to None
ser.open()
Also look at the wrapper class for reference.
http://pyserial.sourceforge.net/examples.html#wrapper-class
You can run a thread to keep reading the data from serial and update it to the buffer.
Creating another process invloves the overhead of IPC and not recommended for this task.
you can always check if there is available data with ser.inWaiting() see link
from serial import Serial
ser = Serial(port=0, baudrate=19200) # set the parameters to what you want
while 1:
if ser.inWaiting():
temp = ser.read()
#all the other code in the loop
if there is no data available to read the loop skips the serial reading
or if the data is to time sensitive you can do this