Communicating between Pure Data and MATLAB using OSC - matlab
I'm trying to receive a message sent over OSC from Pure Data (or Max/MSP) to MATLAB.
I'm able to send OSC messages from Pure Data to Pure Data
I can send messages from MATLAB to MATLAB
I can even send messages from MATLAB to Pure Data
...I'm really struggling to get Pure Data to talk back to MATLAB
Here's my code that sends messages from MATLAB (I'm using the oscmex protocol):
host = 'localhost'; % local host UDP address
sendPort = 3333; % UDP port number to send over
receivePort = 3333; % UDP port number to receive from
oscAddress = osc_new_address(host, sendPort); % open send address
oscServer = osc_new_server(receivePort); % open server
dataPacket = struct('path','/foo','tt','f','data',{num2cell([1.0])}); % create packet
osc_send(oscAddress, dataPacket); % write packet to OSC
oscMessage = osc_recv(oscServer, 0.1); % listen for packet on OSC
% check to see if anything is there...
if length(oscMessage) > 0
fprintf('Found something!')
else
fprintf('Failed to find anything')
end
osc_free_address(oscAddress);
osc_free_server(oscServer);
If I send using host 'localhost', everything works fine sending from MATLAB to MATLAB using the code above. If I set it to '127.0.0.1', MATLAB sends to Pure Data, but MATLAB then can't receive its own messages.
Now for the other end of things. Here's my Pure Data patch:
Again, running the above patch alone successfully sends and receives messages through Pure Data.
The problem lies when I try to talk from one program to another. If I set things so that MATLAB is sending on port 3333 and Pure Data is receiving on 3333, and that Pure Data is sending on 2222 and MATLAB is receiving on 2222, I can make Pure Data receive if MATLAB's host is '127.0.0.1'. But, with '127.0.0.1', MATLAB can't send to itself.
In any case, no matter what I try, I'm unable to make Pure Data send to MATLAB, despite being able to get it to send to itself. I suspect it has something to do with the 'host' address.
My actual IPv4 address (found using 'ipconfig' of the MS command prompt) is completely different from 127.0.0.1, and using the value specified here doesn't seem to make things work any better.
I'm aware that I can't have more than one OSC server with the same port open at any one time and so my current attempt at a solution involves sending from MATLAB on one port, and sending from Pure Data on another, with only a single server open at one time on either port.
Note I'm also aware that I use /foo for messages from MATLAB and /test from Pure Data. However, my MATLAB code indiscriminately receives everything sent over OSC, so this makes no difference.
Any help getting PD to talk to MATLAB would be appreciated.
Update: Ive solved the 'localhost' issue and that doesn't seem to fix things (i had to add localhost to my Windows 'hosts' file). So, I may have been barking up the wrong tree by worrying about the localhost thing. But, I still can't get PD to talk to MATLAB.
Update #2: Amro has posted an elegant solution below and I still can't get MATLAB to receive messages from Pure Data. I've installed CloseTheDoor to monitor my UDP connections and notice that when MATLAB sets up a server, it uses the 'Interface' [::0], whereas PD sets uses 'Interface' 0.0.0.0. Since PureData is the one that successfully receives messages, perhaps I need to make MATLAB listen on 0.0.0.0 as well?
Let me start by saying that I've never used PureData or OSC before, and I just duplicated the graph/patch you've shown to create the server/client.
1) server in PureData, client in MATLAB:
First lets create the server in PureData:
Now here is a simple client implemented as a GUI in MATLAB:
function example_osc_client()
handles = createGUI();
osc = [];
function h = createGUI()
h.fig = figure('Menubar','none', 'Resize','off', ...
'CloseRequestFcn',#onClose, ...
'Name','OSC Client', 'Position',[100 100 220 140]);
movegui(h.fig, 'center')
h.conn = uicontrol('Style','pushbutton', 'String','Connect', ...
'Callback',{#onClick,'connect'}, ...
'Parent',h.fig, 'Position',[20 20 80 20]);
h.disconn = uicontrol('Style','pushbutton', 'String','Disconnect', ...
'Callback',{#onClick,'disconnect'}, ...
'Parent',h.fig, 'Position',[120 20 80 20]);
h.slid = uicontrol('Style','slider', 'Callback',#onSlide, ...
'Min',-10, 'Max',10, 'Value',0, ...
'Parent',h.fig, 'Position',[30 60 160 20]);
h.txt = uicontrol('Style','text', 'String','0.0', ...
'Parent',h.fig, 'Position',[80 100 60 20]);
set([h.slid;h.disconn], 'Enable','off');
drawnow
end
function onClick(~,~,action)
switch lower(action)
case 'connect'
osc = osc_new_address('127.0.0.1', 2222);
set(handles.conn, 'Enable','off')
set(handles.disconn, 'Enable','on')
set(handles.slid, 'Enable','on')
case 'disconnect'
osc_free_address(osc); osc = [];
set(handles.conn, 'Enable','on')
set(handles.disconn, 'Enable','off')
set(handles.slid, 'Enable','off')
end
drawnow
end
function onSlide(~,~)
if isempty(osc), return; end
val = single(get(handles.slid,'Value'));
m = struct('path','/test', 'tt','f', 'data',{{val}});
osc_send(osc, m);
set(handles.txt, 'String',num2str(val))
drawnow
end
function onClose(~,~)
if ~isempty(osc)
osc_free_address(osc);
end
delete(handles.fig);
end
end
As you move the slider, messages are sent to the server (using OSC-MEX interface), and the values are displayed in the PureData model.
While testing this, I noticed that double type was not supported, as I saw the following message in the PD log window:
unpackOSC: PrintTypeTaggedArgs: [A 64-bit float] not implemented
So it was necessary to either manually cast values as single or explicitly specify the hint type in the structure passed to osc_send OSC-MEX function:
val = single(1);
m = struct('path','/test', 'tt','f', 'data',{{val}});
osc_send(osc, m);
2) server in MATLAB, client in PureData:
Similarly we create the client in PureData:
Again, here is the server implemented as a MATLAB GUI:
function example_osc_server()
handles = createGUI();
osc = [];
function h = createGUI()
h.fig = figure('Menubar','none', 'Resize','off', ...
'CloseRequestFcn',#onClose, ...
'Name','OSC Server', 'Position',[100 100 220 140]);
movegui(h.fig, 'center')
h.start = uicontrol('Style','pushbutton', 'String','Start', ...
'Callback',{#onClick,'start'}, ...
'Parent',h.fig, 'Position',[20 20 80 20]);
h.stop = uicontrol('Style','pushbutton', 'String','Stop', ...
'Callback',{#onClick,'stop'}, ...
'Parent',h.fig, 'Position',[120 20 80 20]);
h.txt = uicontrol('Style','text', 'String','', ...
'Parent',h.fig, 'Position',[60 80 100 20]);
set(h.stop, 'Enable','off');
drawnow expose
h.timer = timer('TimerFcn',#receive, 'BusyMode','drop', ...
'ExecutionMode','fixedRate', 'Period',0.11);
end
function onClick(~,~,action)
switch lower(action)
case 'start'
set(handles.start, 'Enable','off')
set(handles.stop, 'Enable','on')
osc = osc_new_server(2222);
start(handles.timer);
case 'stop'
set(handles.start, 'Enable','on')
set(handles.stop, 'Enable','off')
osc_free_server(osc); osc = [];
stop(handles.timer);
end
drawnow expose
end
function receive(~,~)
if isempty(osc), return; end
m = osc_recv(osc, 0.1);
if isempty(m), return; end
set(handles.txt, 'String',num2str(m{1}.data{1}))
drawnow expose
end
function onClose(~,~)
if ~isempty(osc)
osc_free_server(osc);
end
stop(handles.timer); delete(handles.timer);
delete(handles.fig);
clear handles osc
end
end
The server part was a bit trickier in MATLAB. The idea is that we don't want MATLAB to block indefinitely waiting for messages. So I created a timer which executes every 0.11 second. Inside the timer function we try to receive message in a blocking manner but with a timeout of 0.1 sec. This way both the GUI and MATLAB IDE stay responsive.
3) other combinations:
Using the above solutions, you could also open both client and server in PureData, or both client and server in MATLAB. It should work either way.
Finally I should say that it made no difference whether I'm using the hostname as localhost or specified the IP address directly 127.0.0.1.
HTH
EDIT:
I managed to compile the OSC-MEX package myself, here are the steps. First download osc-mex sources and its dependencies. This includes: liblo sources, pthreads-win32 binaries, premake4 executable.
1) We start by building the liblo library:
Copy "premake4.exe" into the "build" directory, and run: premake4 --platform=x32 vs2010
open the generated "liblo.sln" solution file in VS2010. Select the "liblo" project and go to "Project > Properties". Add the include folder containing pthreads header files in the "Additional Include Directories" field. Similarly add the lib folder for the linker, and specify pthreadVC2.lib as additional dependency.
Select the "ReleaseLib" Win32 target and build the project. This should create the final target: lib\ReleaseLib\liblo.lib
Note that by default, IPv6 support is disabled in liblo because OSC applications like Pd have problems with IPv6. If you still want to enable it, add the following line to config.h file:
#define ENABLE_IPV6 1
2) Next we compile the MEX-functions in MATLAB:
Go to the folder containing the C-sources of the MEX-functions
copy liblo.lib from the previous step into this directory. Also copy pthreadVC2.lib from the pthreads library.
compile each function using:
mex -largeArrayDims -I../path/to/liblo-0.27 xxxxxx.c pthreadVC2.lib liblo.lib -lwsock32 -lws2_32 -liphlpapi
You should end up with six *.mexw32 files for each of the xxxxxx.c source files
Finally copy the pthreads DLL into this same folder: pthreadVC2.dll
To save you some troubles, here are the compiled MEX-files built on WinXP 32-bit and Win8 64-bit both using VS2010. Here are the sources if you would like to compile it yourself (simply build the solution in VS2010, then run osc_make.m in MATLAB)
localhost is an alias for 127.0.0.1; they really are the same IP-address. so if matlab only receives something if it is sending to localhost but not if sending to 127.0.0.1, they probably have a buggy implementation of OSC.
as long as you have [udpreceive 2222] in your patch, Pd will block the port UDP/2222, and matlab will not be able to receive anything on that port.
so the simple solution is: remove [udpreceive 2222] before creating the matlab server using osc_new_server(2222);
Related
how to query userinput via phone, within matlab
Conceptual question: How can I query userinput via phone (android) within a matlab script running on a server? What would it take to make this work? Could this be achieved using a COM interface like outlook's? Thanks
Here's the code I wrote to make this happen, suggestions welcome... install matlab app then... %matlab instance #1, run on server t = tcpip('0.0.0.0', 1234, 'NetworkRole', 'server'); fprintf('server is set up. \n\nwaiting for client to connect... '); fopen(t); fprintf('connected.\n') fprintf('listening for user commands...\n\n'); while true if t.BytesAvailable>0 data = fread(t,t.BytesAvailable); if data==1 disp('1') elseif size(data,1)>1 %parse several integers as strings switch char(data') case 'asdf' disp('do asdf'); % >> at this point any input can be run using 'eval' << % end end else pause(2); %wait some seconds end end %matlab instance #2, run on same server %enable mobile app connection connector on; (connect then via mobile app, run following code from the mobile console) try t = tcpip('localhost',1234); fclose(t) fopen(t) catch fprintf('connection failed.\n'); return end fprintf('connection established.\n') % send command to server fwrite(t,'asdf')
Matlab GPIB - how to read Message AVailable from Status Byte Register?
as the title says... I want to check the SBR register of device connected over GPIB. I am interested in reading the MAV bit 4, which should be set if instrument has something it would like to send me. The problem is, that in order to check the SBR, I inevitably have to send another query (*STB?), which clears the device output buffer by default. In other words, whenever I check if there is something to read, I remove it just by looking. Here is code to reproduce it, problem is at the last call to fscanf(): >> fid = gpib('agilent', 7, 26); >> fopen(fid) >> fprintf(fid, '*SRE 255; *SRE?') % Enable everything in SBR >> fscanf(fid) % Returns +191 as expected (255 - 64 for MSS) >> fprintf(fid, '*IDN?') % Make any query... >> fprintf(fid, '*STB?') % Read SBR % The line above generates device Query Error (beep) >> fscanf(fid) % << Returns +0 !!! >> fclose(fid) % Just to prevent flames :] I suspect, that there is some way to check the SBR without querying the device, but I could not find anything. MATLAB help for VISA drivers is silent on the topic of communicating directly with the driver or the bus. I also tried to check for BusManagementStatus with no avail. For reference, I am using MATLAB R2011b, with 32-bit Agilent VISA drivers, and the GPIB enabled device is Agilent E4980A LCR Meter. Thanks for any help.
OK, I think that I figured it out. Please correct me if I am wrong... First of all I need to anticipate the need to check for available message, because in my solution I won't be able to check for Error Queue, Master Summary, and other bits, that are set in SBR. Then, before my actual commands (that may produce some output), I need to mask the Service Request Enable Register (SRER) to only allow MAV bit. Thats done like so (following from example in question): >> fprintf(fid, '*SRE 16'); >> fprintf(fid, '...ACTUAL COMMANDS THAT ARE TO BE EXECUTED...'); Now I can check, whether the device sends a service request, using the aforementioned BusManagementStatus command. The following command returns true iff there is MAV bit set in SBR. >> strcmp(fid.BusManagementStatus.ServiceRequest, 'on') The disadvantage is that there is no way to check for errors during my ACTUAL COMMANDS execution. If I do so, it might produce errors... :]
Non blocking UDP receiver in Matlab GUI
I'm creating a MATLAB GUI using the app designer (very similar to, but better than, GUIDE) which I want to use to monitor the data output of my simulink model in real time. In other words, I have a simulink model and a GUI, both running in the same MATLAB instance and I want to send packets over UDP from the simulink model and use that data in my GUI to update plots. However, I don't know how to read the data from the UDP packet without blocking. Is there a way to bind a handler when a packet is received so that I can execute a function to update plots/fields?
Of course, beside BytesAvailableFcn matlab has the datagramreceivedfcn to call your custom function on new dagatrams, which is nonblocking while fread/fscanf are blocking (temporarily). Regarding callbacks in matlab read events and cbs Compilable standalone could look like: %% standalone main %{ see docs/* %} function exitcode = main(port, remotePort) % sanitize input if(~exist('port','var') || ~exist('remotePort','var')) disp(['args: port remotePort']); exit(1); end if ischar(port) port=str2num(port); end if ischar(remotePort) remotePort=str2num(remotePort); end % create udp object u = udp('127.0.0.1',remotePort, 'LocalPort', port); % connect the UDP object to the host fopen(u); exitcode = 0; % since we poll, suppress warning warning('off','instrument:fscanf:unsuccessfulRead'); warning VERBOSE % specify callback on datagram while true msgEnc= fscanf(u); % timed out without datagram if isempty(msgEnc) continue end % do sth with msgEnc (which is a readable string) fprintf(u, 'heard sth'); %answer end end If you would like to use simulink block use udpreceive which has a non-blokcking capability A First In First Out (FIFO) buffer receives the data. At every time step, the Data port outputs the requested values from the buffer. In a nonblocking mode, the Status port indicates if the block has received new data.
TCPIP connection not working in matlab
Can any body help me out with the problem I am facing while making a tcpip connection with an instrument.Its like when I make a connection with the tcpip address using the following it works perfectly fine for any no of times. 1.test and measurement tool(TMTOOL in matlab) 2.Zoc terminal 3.teraterm. but when I try to implement the following script its does run but dont know some how doesn't seem to send the command to the pan and tilt unit which I am tryin to control. I am trying with two scripts: A. obj1=tcpip('169.254.32.28',4000); fopen(obj1); cmd=5000; fprintf(obj1,'pp%d',cmd) fclose(obj1); delete(obj1); I am trying to send a command pp5000 B. %commands lat=51.57668; lon=-1.26765; alti=500; % Find a tcpip object. obj1 = instrfind('Type', 'tcpip', 'RemoteHost', '169.254.32.28', 'RemotePort', 4000, 'Tag', ''); % Create the tcpip object if it does not exist % otherwise use the object that was found. if isempty(obj1) obj1 = tcpip('169.254.32.28', 4000); else fclose(obj1); obj1 = obj1(1) end % Connect to instrument object, obj1. fopen(obj1); i=1; tic for i=1:10 % Communicating with instrument object, obj1. fprintf(obj1, 'gg%f,%f,%f',lat,lon,alti); i=i+1; toc end fclose(obj1); delete(obj1); Desired result: gg51.57668,-1.26765,500 The second script is just a little bit modified version of the automatic script generated from TMTOOL im matlab Basically I want to print some commands on the desired object every half a second. can anyone suggest some alternative or improvement that could be done to achieve the desired results. I have been unable to execute these scripts and I think it should be correct with whatever small knowledge of matlab I have.I am a new user and I would appreciate if you could help. Thanks Salil
You could always use the Java classes (sockets) to pass data back and forth, it how I do it. File 1: import java.net.ServerSocket import java.io.* server_socket = ServerSocket(4000); client_socket = server_socket.accept; out = PrintWriter(client_socket.getOutputStream, true); in = BufferedReader(InputStreamReader(client_socket.getInputStream)); str = in.readLine(); % Read in data out.println(data); % send data File 2: import java.io.*; import java.net.*; server_socket = Socket('localhost', 4000); in = BufferedReader(InputStreamReader(server_socket.getInputStream)); out = PrintWriter(server_socket.getOutputStream,true); Get and send input the same way as above. When you're done don't forget to close everything. out.close(); in.close(); client_socket.close(); server_socket.close();
Controlling a matlab script (Pause, Reset)
I am trying to create a matlab script (m-file) which shall be controlled by an external VBA script. The matlab script shall do the same operation every time (even params change, but this is not the matter in this case) for a certain number of loops. If I see it right, I can use matlab funktions in VBA like this: http://www.mathworks.de/help/techdoc/matlab_external/f135590.html#f133975 My main problem is how to implement the matlab part of this problem...at the moment my control part looks like this: start.m: run = 1; reset = 0; while run ~= 0 % Loop until external reset of 'run' to '0' if reset ~= 0 doReset(); % Reset the parameters for the processing reset = 0; disp('I did a reset'); end disp('I am processing'); doProcess(); pause(1) end disp('I am done'); The reset part works very fine while changing the value by the script, but when I manually try to change the value of 'run' or 'reset' to any other value in my workspace, nothing happens...my script doen't abort, neither does the reset-if do it's work... this seems to me that the script doesn't recognize any changes in the workspace?! later the variables 'run' and 'reset' shall be set or unset by the VBA script. Is there any plausible reason why I can't abort the loop by hand? Thanks for any advice! greets, poeschlorn Edit: It seems that the script loads the variables once before starting and never again during runtime...is there a possibility to have explicit access to a workspace variable? Edit 2: I use Matlab 2010b with no additional Toolboxes at the moment Edit 3: I found out, that there are several 'workspaces' or RAMs in Matlab. If my function is running, the variables are stored in 'base' (?) workspace, which is not the matlab workspace on which you can click and change every value. So I have to get access to this ominous 'base' space and change the flag 'run' to zero.
I assume your problem is simply that your loop is blocking execution of the external interface. While the loop runs you cannot access the other interfaces. I wanted to do a similar thing -- allow control of a matlab loop by an external program (either Ruby or another matlab instance). The most flexible solution by far was using UDP. There is a great toolbox called PNET for matlab, and I assume VB must have a socket library too. I simply open a UDP port on both sides, and use simple text commands to control and give feedback. obj.conn = pnet('udpsocket', 9999); command = ''; while run ~= 0 nBytes = pnet(obj.conn, 'readpacket'); if nBytes > 0 command = pnet(obj.conn, 'read', nBytes, 'string'); end switch command case '--reset--' doReset(); % Reset the parameters for the processing reset = 0; disp('I did a reset'); case '--abort--' run = 0; disp('Going to abort'); case '--echo--' pnet(obj.conn, 'write', '--echo--'); pnet(obj.conn, 'writepacket', remoteAddress, remotePort); end doProcess(); end This way I can build my own extensible control interface without worrying about blocking from the loop, it can work cross-platform and cross-language, can work within a machine or across the network. UPDATE: To talk between two UDP clients, you need to set up two complimentary UDP ports, both are clients (this example is all in matlab, pretend obj here is a structure, in my case it is a class i wrap around the pnet functionality): obj = struct(); obj.success = 0; obj.client1Port = 9999; obj.client2Port = 9998; obj.client1Address = '127.0.0.1'; obj.client2Address = '127.0.0.1'; obj.conn1 = pnet('udpsocket', obj.client1Port); obj.conn2 = pnet('udpsocket', obj.client2Port); pnet(obj.conn1, 'write', '--echo--') pnet(obj.conn1, 'writepacket', obj.client2Address, obj.client2Port); nBytes = pnet(obj.conn2, 'readpacket'); if nBytes > 0 command = pnet(obj.conn2, 'read', nBytes, 'string'); if regexpi(command,'--echo--') obj.success = obj.success+1; fprintf('Client 2 recieved this message: %s\n',command); pnet(obj.conn2, 'write', '--echo--') pnet(obj.conn2, 'writepacket', obj.client1Address, obj.client1Port); end end nBytes = pnet(obj.conn1, 'readpacket'); if nBytes > 0 command = pnet(obj.conn1, 'read', nBytes, 'string'); if regexpi(command,'--echo--') obj.success = obj.success+1; fprintf('Client 1 got this back: %s\n',command); end end if obj.success == 2 fprintf('\nWe both sent and received messages!\n'); end
Is your script a script m-file or a function? If it's a function, you'll be losing the scope of the workspace variables which is why it's not working. I'd turn your code into a function like this: function processRun(run,reset) while run ~= 0 % Loop until external reset of 'run' to '0' if reset ~= 0 doReset; % Reset the parameters for the processing reset = 0; disp('I did a reset'); end disp('I am processing'); [run,reset] = doProcess; pause(1) end You can then set the values of run and reset evertime you call the function from VBA. If you have a script, try removing the run and reset lines from the top, and set their values in the workspace before you run the script. I think you're overwriting your workspace values by running the script file.
Sorry, I don't have enough rep to make a comment so I'll quote it here: #Adam Leadbetter: Thanks, this makes sense. The only thing I habe trouble with is how to pause (after this reset and then resume) the script when it has been started by run=1 as param... – poeschlorn Feb 25 at 7:17 If you want to break out of the loop once reset has been set to one, and then wait for the loop to continue again once run = 1 that is pretty much the same as just starting over again? function processRun() run = 1; while run ~= 1 run = doProcess(); end if doProcess() returns 0 then the function processRun() will end (like the behaviour you want to have when reset), the next time processRun is called it starts over, with "reset"/default values. Or am I missing something?