How to create a callback function to start and stop recording EMG Button in real time for Delsys Trigno.

13 views (last 30 days)
Hello,
I am trying to create a start stop to record a data stream for the Delsys Trigno System in MATLAB. If I run the file as is, then it will automatically run the signal, and stop when the figures are closed, but I would like to control it will through a button. I have pasted the Matlab code that runs the data stream and displays in figures below. But, I am struggles to also seperate the functionality of controlling the data stream and displaying it in figures. When I try to pass the commObject between files, which is needed to communicate, it continues to give me errors. I would be greateful if somebody could help. All the code below is from the delsys Trigno SDK.
function real_time_data_stream_plotting
% CHANGE THIS TO THE IP OF THE COMPUTER RUNNING THE TRIGNO CONTROL UTILITY
HOST_IP = '10.101.29.77';
%%
%This example program communicates with the Delsys SDK to stream 16
%channels of EMG data and 48 channels of ACC data.
%% Create the required objects
%Define number of sensors
NUM_SENSORS = 4;
%handles to all plots
global plotHandlesEMG;
plotHandlesEMG = zeros(NUM_SENSORS,1);
global plotHandlesACC;
plotHandlesACC = zeros(NUM_SENSORS*3, 1);
global rateAdjustedEmgBytesToRead;
%TCPIP Connection to stream EMG Data
interfaceObjectEMG = tcpip(HOST_IP,50041);
interfaceObjectEMG.InputBufferSize = 6400;
%TCPIP Connection to stream ACC Data
interfaceObjectACC = tcpip(HOST_IP,50042);
interfaceObjectACC.InputBufferSize = 6400;
%TCPIP Connection to communicate with SDK, send/receive commands
global commObject;
commObject = tcpip(HOST_IP,50040);
% handles.commObject = commObject;
%Timer object for drawing plots.
t = timer('Period', .1, 'ExecutionMode', 'fixedSpacing', 'TimerFcn', {@updatePlots, plotHandlesEMG});
global data_arrayEMG
data_arrayEMG = [];
global data_arrayACC
data_arrayACC = [];
%% Set up the plots
axesHandlesEMG = zeros(NUM_SENSORS,1);
axesHandlesACC = zeros(NUM_SENSORS,1);
%initiate the EMG figure
figureHandleEMG = figure('Name', 'EMG Data','Numbertitle', 'off', 'CloseRequestFcn', {@localCloseFigure, interfaceObjectEMG, interfaceObjectACC, commObject, t});
set(figureHandleEMG, 'position', [50 200 750 750])
for i = 1:NUM_SENSORS
axesHandlesEMG(i) = subplot(2,2,i);
plotHandlesEMG(i) = plot(axesHandlesEMG(i),0,'-y','LineWidth',1);
set(axesHandlesEMG(i),'YGrid','on');
%set(axesHandlesEMG(i),'YColor',[0.9725 0.9725 0.9725]);
set(axesHandlesEMG(i),'XGrid','on');
%set(axesHandlesEMG(i),'XColor',[0.9725 0.9725 0.9725]);
set(axesHandlesEMG(i),'Color',[.15 .15 .15]);
set(axesHandlesEMG(i),'YLim', [-.025 .025]);
set(axesHandlesEMG(i),'YLimMode', 'auto');
set(axesHandlesEMG(i),'XLim', [0 2000]);
set(axesHandlesEMG(i),'XLimMode', 'manual');
if(mod(i, 4) == 1)
ylabel(axesHandlesEMG(i),'V');
else
set(axesHandlesEMG(i), 'YTickLabel', '')
end
if(i >12)
xlabel(axesHandlesEMG(i),'Samples');
else
set(axesHandlesEMG(i), 'XTickLabel', '')
end
%save('test.mat','axesHandlesEMG')
title(sprintf('EMG %i', i))
end
%initiate the ACC figure
figureHandleACC = figure('Name', 'ACC Data', 'Numbertitle', 'off', 'CloseRequestFcn', {@localCloseFigure, interfaceObjectEMG, interfaceObjectACC, commObject, t});
set(figureHandleACC, 'position', [850 200 750 750]);
for i= 1:NUM_SENSORS
axesHandlesACC(i) = subplot(2, 2, i);
hold on
plotHandlesACC(i*3-2) = plot(axesHandlesACC(i), 0, '-y', 'LineWidth', 1);
plotHandlesACC(i*3-1) = plot(axesHandlesACC(i), 0, '-y', 'LineWidth', 1);
plotHandlesACC(i*3) = plot(axesHandlesACC(i), 0, '-y', 'LineWidth', 1);
hold off
set(plotHandlesACC(i*3-2), 'Color', 'r')
set(plotHandlesACC(i*3-1), 'Color', 'b')
set(plotHandlesACC(i*3), 'Color', 'g')
set(axesHandlesACC(i),'YGrid','on');
%set(axesHandlesACC(i),'YColor',[0.9725 0.9725 0.9725]);
set(axesHandlesACC(i),'XGrid','on');
%set(axesHandlesACC(i),'XColor',[0.9725 0.9725 0.9725]);
set(axesHandlesACC(i),'Color',[.15 .15 .15]);
set(axesHandlesACC(i),'YLim', [-3 1]);
set(axesHandlesACC(i),'YLimMode', 'auto');
set(axesHandlesACC(i),'XLim', [0 2000/13.5]);
set(axesHandlesACC(i),'XLimMode', 'manual');
if(i > 12)
xlabel(axesHandlesACC(i),'Samples');
else
set(axesHandlesACC(i), 'XTickLabel', '');
end
if(mod(i, 4) == 1)
ylabel(axesHandlesACC(i),'g');
else
set(axesHandlesACC(i) ,'YTickLabel', '')
end
title(sprintf('ACC %i', i))
end
%%Open the COM interface, determine RATE
fopen(commObject);
pause(1);
fread(commObject,commObject.BytesAvailable);
fprintf(commObject, sprintf(['RATE 2000\r\n\r']));
pause(1);
% fread(commObject,commObject.BytesAvailable);
% fprintf(commObject, sprintf(['RATE?\r\n\r']));
% pause(1);
data = fread(commObject,commObject.BytesAvailable);
emgRate = strtrim(char(data'));
if(strcmp(emgRate, '1925.926'))
rateAdjustedEmgBytesToRead=1664;
else
rateAdjustedEmgBytesToRead=1728;
end
%% Setup interface object to read chunks of data
% Define a callback function to be executed when desired number of bytes
% are available in the input buffer
bytesToReadEMG = rateAdjustedEmgBytesToRead;
interfaceObjectEMG.BytesAvailableFcn = {@localReadAndPlotMultiplexedEMG,plotHandlesEMG,bytesToReadEMG};
interfaceObjectEMG.BytesAvailableFcnMode = 'byte';
interfaceObjectEMG.BytesAvailableFcnCount = bytesToReadEMG;
bytesToReadACC = 384;
interfaceObjectACC.BytesAvailableFcn = {@localReadAnPlotMultiplexedACC, plotHandlesACC, bytesToReadACC};
interfaceObjectACC.BytesAvailableFcnMode = 'byte';
interfaceObjectACC.BytesAvailableFcnCount = bytesToReadACC;
drawnow
start(t);
%pause(1);
%
%Open the interface object
try
fopen(interfaceObjectEMG);
fopen(interfaceObjectACC);
catch
localCloseFigure(figureHandleACC,1 ,interfaceObjectACC, interfaceObjectEMG, commObject, t);
delete(figureHandleEMG);
error('CONNECTION ERROR: Please start the Delsys Trigno Control Application and try again');
end
disp('now press start');
%%
% Send the commands to start data streaming
disp('it came here');
fprintf(commObject, sprintf(['START\r\n\r']));
%%
% Display the plot
snapnow;
%% Implement the bytes available callback
%The localReadandPlotMultiplexed functions check the input buffers for the
%amount of available data, mod this amount to be a suitable multiple.
%Because of differences in sampling frequency between EMG and ACC data, the
%ratio of EMG samples to ACC samples is 13.5:1
%We use a ratio of 27:2 in order to keep a whole number of samples.
%The EMG buffer is read in numbers of bytes that are divisible by 1728 by the
%formula (27 samples)*(4 bytes/sample)*(16 channels)
%The ACC buffer is read in numbers of bytes that are divisible by 384 by
%the formula (2 samples)*(4 bytes/sample)*(48 channels)
%Reading data in these amounts ensures that full packets are read. The
%size limits on the dataArray buffers is to ensure that there is always one second of
%data for all 16 sensors (EMG and ACC) in the dataArray buffers
function localReadAndPlotMultiplexedEMG(interfaceObjectEMG, ~,~,~, ~)
global rateAdjustedEmgBytesToRead;
bytesReady = interfaceObjectEMG.BytesAvailable;
bytesReady = bytesReady - mod(bytesReady, rateAdjustedEmgBytesToRead);%%1664
if (bytesReady == 0)
return
end
global data_arrayEMG
data = cast(fread(interfaceObjectEMG,bytesReady), 'uint8');
data = typecast(data, 'single');
if(size(data_arrayEMG, 1) < rateAdjustedEmgBytesToRead*19)
data_arrayEMG = [data_arrayEMG; data];
else
data_arrayEMG = [data_arrayEMG(size(data,1) + 1:size(data_arrayEMG, 1));data];
save('data_ArrayEMG.mat', 'data_arrayEMG')
end
function localReadAnPlotMultiplexedACC(interfaceObjectACC, ~, ~, ~, ~)
bytesReady = interfaceObjectACC.BytesAvailable;
bytesReady = bytesReady - mod(bytesReady, 384);
if(bytesReady == 0)
return
end
global data_arrayACC
data = cast(fread(interfaceObjectACC, bytesReady), 'uint8');
data = typecast(data, 'single');
if(size(data_arrayACC, 1) < 7296)
data_arrayACC = [data_arrayACC; data];
else
data_arrayACC = [data_arrayACC(size(data, 1) + 1:size(data_arrayACC, 1)); data];
end
%% Update the plots
%This timer callback function is called on every tick of the timer t. It
%demuxes the dataArray buffers and assigns that channel to its respective
%plot.
function updatePlots(obj, Event, tmp)
global data_arrayEMG
global plotHandlesEMG
for i = 1:size(plotHandlesEMG, 1)
data_ch = data_arrayEMG(i:16:end);
set(plotHandlesEMG(i), 'Ydata', data_ch)
end
global data_arrayACC
global plotHandlesACC
for i = 1:size(plotHandlesACC, 1)
data_ch = data_arrayACC(i:48:end);
set(plotHandlesACC(i), 'Ydata', data_ch)
end
drawnow
%% Implement the close figure callback
%This function is called whenever either figure is closed in order to close
%off all open connections. It will close the EMG interface, ACC interface,
%commands interface, and timer object
function localCloseFigure(figureHandle,~,interfaceObject1, interfaceObject2, commObject, t)
%%
% Clean up the network objects
if isvalid(interfaceObject1)
fclose(interfaceObject1);
delete(interfaceObject1);
clear interfaceObject1;
end
if isvalid(interfaceObject2)
fclose(interfaceObject2);
delete(interfaceObject2);
clear interfaceObject2;
end
if isvalid(t)
stop(t);
delete(t);
end
if isvalid(commObject)
fclose(commObject);
delete(commObject);
clear commObject;
end
%%
% Close the figure window
delete(figureHandle);
  2 Comments
晴晴
晴晴 on 22 Nov 2022
Hello! I also met the same problem recently. Have you solved this problem?I would be greateful if you could help me out.

Sign in to comment.

Answers (1)

Benjamin Kraus
Benjamin Kraus on 22 Nov 2022
I didn't dig into your code specifically, but here is code for a simple app with a timer and a start/stop button that shows how you can control a timer using a button. The key bits you need for your app are most likely the line that creates the button, and the buttonClicked callback function.
function simpleTimerApp
% Create a timer with a callback function.
% Note that I'm using an anonymous function to pass the figure handle (fig)
% into the timer fcn. I will set the TimerFcn later, once I have a figure
% handle to use.
t = timer('Period', 1, 'ExecutionMode', 'fixedSpacing');
% I'm using an onCleanup object to make sure the timer is always deleted
% when the figure closes. Deleting the onCleanup object will delete the
% timer.
c = onCleanup(@() cleanupTimer(t));
% Create a figure for the app.
fig = uifigure('Name', string(0));
% By storing a handle to the onCleanup object in the figure's UserData, the
% onCleanup object will be deleted automatically when the figure closes,
% which will stop and delete the timer.
fig.UserData = c;
% Now that I have a figure handle, create a TimerFcn on the timer that
% calls updateCounter. I'm using an anonymous function to pass the figure
% handle into the updateCounter function.
t.TimerFcn = @(~,~) updateCounter(fig);
% The button that can start or stop the timer. I'm once again using an
% anonymous function, passing both the button and timer handles into
% "buttonClicked". The button handle will be used to update the label on
% the button.
uibutton(fig,'Text','Start','ButtonPushedFcn', @(b,~) buttonClicked(b,t));
end
function updateCounter(fig)
% This function is called by the timer every time it executes, with a
% frequency based on the "Period" of the timer. I'm going to use the "Name" field in the figure as a counter for
% simplicity, but the body of this function should do whatever needs to be
% done every execution of the timer.
fig.Name = string(str2double(fig.Name)+1);
end
function buttonClicked(b, t)
% This function runs whenever the button is clicked. It will check the
% state of the timer and either start or stop the timer.
disp('Button Clicked')
if isvalid(t)
if (t.Running == "on")
% Timer is running, stop it.
stop(t);
% The next time the button is pressed it will start the timer, so
% update the button text to say "Start".
b.Text = "Start";
else
% Timer is stopped, start it.
start(t);
% The next time the button is pressed it will stop the timer, so
% update the button text to say "Stop".
b.Text = "Stop";
end
end
end
function cleanupTimer(t)
% This function will run when the onCleanup object is deleted due to the
% figure being closed. It needs to stop and delete the timer.
if isvalid(t)
stop(t)
delete(t)
end
end

Categories

Find more on Enterprise Deployment with MATLAB Production Server in Help Center and File Exchange

Community Treasure Hunt

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

Start Hunting!