Is it possible to control two Arduino boards in parallel using parfor?

15 vues (au cours des 30 derniers jours)
junchul kim
junchul kim le 10 Mai 2021
Modifié(e) : Edric Ellis le 12 Mai 2021
Hi,
I am trying to control two Arduino boards in parallel (not serially) with one matlab script. I was hoping to achieve this by creating two Arduino objects and running the parallel for-loop, but when I try the script below, I get the following error:
"Error using writeDigitalPin(a, "D13", 1). Dot indexing is not supported for variables of this type."
I suspect that the parfor automatically creates and uses dot indexing, which may not be compatible with Arduino object.
Any help will be much appreciated.
Thanks.
a = arduino('/dev/tty.usbmodem141301','UNO');
b = arduino('/dev/tty.usbmodem141401','UNO');
ProtocolList = {@protocol1, @protocol2};
BoardList = {a,b};
Datalist = cell(1,2);
% use of parfor
parfor k = 1:2
Datalist{k}=ProtocolList{k}(BoardList{k});
end
function out = protocol1(a)
data = {};
t0 = clock;
tic
while toc < 3
writeDigitalPin(a, "D13", 1); % turn on LED
pause(1)
writeDigitalPin(a, "D13", 0);
pause(1)
data = vertcat(data,{round(etime(clock,t0) * 1000)})
end
out = data;
end
function out = protocol2(a)
data = {};
tic
t0 = clock;
while toc < 3
writeDigitalPin(a, "D13", 1); % turn on LED
pause(1)
writeDigitalPin(a, "D13", 0);
pause(1)
data = vertcat(data,{round(etime(clock,t0) * 1000)})
end
out = data;
end

Réponses (2)

Walter Roberson
Walter Roberson le 11 Mai 2021
The arduino object data is not being copied to the workers.
Use parfevalOnAll to run an initialization on each worker that creates an arduino object and stores a reference to it .
However, when devices are being run in parallel, it is most common that one needs data from both of them, or needs data from one to influence what is done with the other. In such a case you should be considering using spmd instead of parfor, so that you can communicate between workers using labSend() and labReceive()
  2 commentaires
junchul kim
junchul kim le 11 Mai 2021
Hi Walter,
Thanks for the help.
The two arduino boards do not need to interact with each other, so I suppose that I can stick to the parfor.
I am still not sure how I use parfevalOnAll for initialization.
Can you direct me to any example of using parfevalOnAll to achieve what you mentioned?
Walter Roberson
Walter Roberson le 11 Mai 2021
https://www.mathworks.com/matlabcentral/answers/30294-identifying-lab-id-within-a-parfor#answer_38822
shows how to identify a worker. You can use that to index the filenamesof the ports to figure out which one to open. Then parfevalOnAll passing in the handle of a function that will do the open and store the object.

Connectez-vous pour commenter.


Edric Ellis
Edric Ellis le 11 Mai 2021
Modifié(e) : Edric Ellis le 12 Mai 2021
As @Walter Robersonpoints out, you need to create the arduino objects directly on the workers. Rather than using parfevalOnAll though, I'd recommend using parallel.pool.Constant which is designed exactly for this sort of thing. In this case, things are slightly awkward because each worker needs a slightly different argument when building the arduino object, so we defer to a simple function that switches on the task ID from getCurrentTask. You can use a Constant to both initialize and access the data, like this:
parpool('local',2);
arduinoPorts = {'/dev/tty.usbmodem141301', '/dev/tty.usbmodem141401'};
% The following line runs the function separately on each worker
% and stores the result so that it can be accessed using 'board.Value' on a
% worker. Note that we pass in all the arduinoPorts, and the function
% buildArduinoForWorker selects the right one to use.
board = parallel.pool.Constant(@() buildArduinoForWorker(arduinoPorts));
% Next, use parfor (or you could use spmd, or even parfeval)
parfor k = 1:2
% Get the underlying 'arduino' object on this worker
thisBoard = board.Value;
% Do whatever with that
Datalist{k}=ProtocolList{k}(thisBoard);
end
%%%
function board = buildArduinoForWorker(arduinoPorts)
t = getCurrentTask();
id = t.ID;
board = arduino(arduinoPorts{id}, 'UNO');
end
  2 commentaires
Walter Roberson
Walter Roberson le 11 Mai 2021
Notice that
a = arduino('/dev/tty.usbmodem141301','UNO');
b = arduino('/dev/tty.usbmodem141401','UNO');
Different ports. parallel.port.Constant would require using the same port for both. That's why I posted about identifying the parallel task, so that the user could calculate the file name. parallel.pool.Constant() and parfeval() do not permit designating the worker indexes to evaluate on . parfevalOnAll() does not give any simple way of figuring out which worker you are on, but at least it is possible to work it out... even if it takes doing a parfevalOnAll to retrieve all the task IDs so that you can sort them to impose an ordering for indexing purposes.
Edric Ellis
Edric Ellis le 12 Mai 2021
Argh doh, quite right, I didn't spot that. Thanks for pointing that out. There are a couple of ways around that. I still prefer to use parallel.pool.Constant for this sort of thing though - you just need to write a tiny function to build the right arduino based on the task index (unfortunately this approach is not compatible with parpool("threads")). I'll update my answer...

Connectez-vous pour commenter.

Catégories

En savoir plus sur Parallel for-Loops (parfor) dans Help Center et File Exchange

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by