tino / pyFirmata

Python interface for the Firmata (http://firmata.org/) protocol. It is compliant with Firmata 2.1. Any help with updating to 2.2 is welcome. The Capability Query is implemented, but the Pin State Query feature not yet.
MIT License
578 stars 191 forks source link

How to manage multiple pyfirmata arduinos? #61

Closed shawnkirsch closed 6 years ago

shawnkirsch commented 7 years ago

How would I do this?

if I have 3 arduinos hooked up to different things how do I reliably pick a specific arduino.

shawnkirsch commented 7 years ago

i.e.: one for light sensor streaming, one for a robotic controller, one for a different arduino with a servo or something.

in my case need a lot of streaming, so would need specific control.

David-Lor commented 7 years ago

Never tried with more than a single board, but Have you tried declaring the 3 boards? Something like

board1=Arduino("/dev/ttyUSB0") board2=Arduino("/dev/ttyUSB1") board3=Arduino("/dev/ttyUSB2")

coelhudo commented 6 years ago

I tried to use two boards at the same time but didn't work. ~The limitation is the pySerial library~. I still don't know why, but the trick that worked for me is to start two instances of Process from multiprocessing.


from multiprocessing import Process, Queue

def one(queue):
    board_one = Arduino('/dev/ttyUSB0')
    #your code for board one here

def two(queue):
    board_two = Arduino('/dev/ttyUSB1')
    #your code for board two here

q1 = Queue()
mp_one = Process(target=one, args=(q1,))
q2 = Queue()
mp_two = Process(target=two, args=(q2,))

mp_one.start()
mp_two.start()

#interaction between this context and mp_one and mp_two through q1 and q2

If communication is necessary, you can use multiprocessing.Queue as a way to exchange objects between the main process and the other processes as you can see in the example. multiprocessing.SimpleQueue and multiprocessing.Pipe can also be employed to this end.

tino commented 6 years ago

Hmm, strange. If I remember correctly I've used 2 Arduino Mega's at the same time in a project several years ago without spawning multiple processes. But I'd have to dig into my code archive to make sure.

Also pyserial seems to open a file descriptor per serial.Serial class (https://github.com/pyserial/pyserial/blob/master/serial/serialposix.py#L265), so that should work.

Do you run into any specific error?

coelhudo commented 6 years ago

Yes. For example, when I tried the code below only one of the boards worked.

board_one = Arduino('/dev/ttyUSB0')
board_two = Arduino('/dev/ttyUSB1')

#boards setup here, enable_reporting and Iterator stuff

print(board_one.analog[0].read()) #prints None
print(board_two.analog[1].read()) #prints current state of pin A1

If I changed the instantiation order, the opposite occurred: board_one Ok but board_two not Ok.

Just for context, I'm working with Python 3.5.3 on an RPi running Raspbian, communicating with two Arduino Uno.

When I each board is isolated in a multiprocessing.Process instance, everything works fine.

Since I didn't find anything that could cause this problem in pyFirmata code, I searched for similar issues involving pySerial. I found some threads on StackOverflow related to open multiple ports using subprocess, threads, or multprocessing. They weren't stating that the problem was in pySerial per se, but I modified the code with their suggestion, and this did the trick.

You are correct regarding the file descriptor and pySerial, and I made a mistake on blaming pySerial. It was just an assumption. I fixed my previous comment.

Because this solved my problem, I decided not to continue the investigation to find the cause of the problem.

tino commented 6 years ago

Thanks for the clarification and the solution!