Hello,
I'm looking to use my U3 to record pedal stroke rate on a stationary cycle. I would like to generate a pulse every time the right pedal crank passes a fixed point on the bike frame, which I will log to my Python based acquisition software along with data from other sensors placed on the cyclist (e.g. EKG). Does anyone have any suggestions re the best way to do this, and what kind of sensor to use? I'm essentially looking to create something like this ( http://www.wahoofitness.com/devices/wahoo-rpm-cadence-sensor# ) but connected directly to the U3.
Any advice would be much appreciated!
Thank you
Tom
If you can find any sort of wired sensor you can likely make it work. For example I remember my old speedometer used to have a magnet on a spoke that passed by a reed switch on the fork. When the magnet passed by it closed the switch which we can detect.
Something solid state would be better, but a mechanical reed switch should be fine.
Here are a couple of suggestions using some readily available sensors from Honeywell. There are a number of manufacturers out there that make similar sensors:
http://sensing.honeywell.com/SM451R-omnipolar
http://sensing.honeywell.com/sensors/value-added-magnetic-sensors/SR16-S...
Great - thank you for the suggestions! I'll try the MR sensor option.
Looks like it has an open-collector NPN signal so should connect right to a digital input on the U3:
https://labjack.com/support/app-notes/open-collector-signals
One of my colleagues brought in a reed switch and a magnet from his old bike speedo, so I'll try this first. I wired one end to FIO0 and the other to GND. Not sure what to do next, so any advice would be appreciated. Here's what I'm trying to do...
I currently use the U3 to send event codes (1 thru 255) from MATLAB running on one machine to acquisition software running on another machine. These event codes relate to stimuli that I'm presenting on a monitor, and I send them via the DB15 port. I'm using the labJack.m file to control the U3. My goal here is to send an additional event code to the acquisition machine every time the magnet attached to the pedal crank passes the switch. It's really important that the events don't interfere with each other (timing is critical). Do you have any suggestions re how to set this up in MATLAB?
Thanks!
Move the sensor to FIO4 as you are going to want to use a counter or timer.
Start by looking at the test panel in LJControlPanel. FIO4 should be configured as a digital input. When the reed switch is open FIO4 should read high. When the reed switch is closed FIO4 should read low.
Next step is to use a counter rather than a simple digital input. Go into the timer and counter config and enable Counter0. Leave PinOffset=4 so it appears on FIO4. Click OK and go back to the main test panel where you can watch the counter value as the magnet passes the sensor. If it increments once each time you are in business. If it increments a bunch each time, due to the bounce of a mechanical switch, you will want to instead use a timer in debounce mode 6:
https://labjack.com/support/datasheets/u3/hardware-description/timers-co...
Thanks. I moved the sensor to FIO4 (one wire into FIO4 and one into GND). I'm running Linux so I don't have access to the LJControlPanel. I'm using the labJack.m to control the labjack. I initialize the labjack by calling...
lj = labJack('verbose',true);
Presumably I then need to use lj.setDIO to configue FIO4 as a digital input and lj.timedTTL (?) to set up the timer? However, it's not clear from the labJack.m documentation how to do this, and I'm struggling to find any relevant examples online. Can you point me towards any relevant documentation/examples?
Thank you.
Looking through labJack.m, it doesn't provide U3 hardware timer/counter functionality, and setDIO and timedTTL just do simple input and output operations on the digital I/O. Keep in mind that labJack.m is a third party class not made by us, and we do not provide MATLAB libraries and examples for Linux (we do for Windows). We do provide C/C++ and Python examples:
https://github.com/labjack/exodriver
https://labjack.com/support/software/examples/ud/labjackpython
For Python check the U3 Datasheet and its low-level function documentation which has counter specific examples, but this is for if you were to use the Python language instead of MATLAB.
If you need to use MATLAB, you will need to implement the timer/counter functionality in your MATLAB code. Using labJack.m and adding to it is a good place to start as it provides helpful code on loading the Exodriver (our USB library for our devices), opening a device, helpful functions for sending/receiving packets and calculating packet checksums. The C/C++ examples will help to demonstrate building and parsing low-level command-response packets and how to use the Exodriver for USB communications with the U3. The low-level packet command-responses are documented here in the U3 datasheet:
https://labjack.com/support/datasheets/u3/low-level-function-reference
In particular you will need a ConfigIO command to enable the counter or timer. Use the Feedback command to read from the counter, or configure and read from the timer (if firmware counter with debounce is needed):
https://labjack.com/support/datasheets/u3/low-level-function-reference/c...
https://labjack.com/support/datasheets/u3/low-level-function-reference/f...
OK, thank you. I didn't realize labJack.m is a third party class. I do need to use MATLAB, as my stimulus presentation is all controlled via MATLAB and switching to another language isn't really viable. Sorry, but I have zero experience with low-level programming, so I'm struggling with how to correctly format the new functions that I'll need to create. To use setDIO as an example, the vector of values that is created and outputted by MATLAB is 14 items. I understand that each value in the vector has a specific function (e.g. 27=portStateWrite), but I've been through the documentation and it's unclear how this all fits together. I understand that you don't support MATLAB/linux, but if you have any advice here I'd appreciate it.
Thanks!
For most operations on the U3, you will be using command-response mode. You send a command to the U3 to tell it what to do and read back the response from the U3.
Using setDIO as an example, that vector is a 14-byte Feedback command. With Feedback you can perform multiple operations in one command to the U3. The vector starts at index 1 and the documentation's command bytes starts at index 0. Starting at vector index 8 (byte index 7), you specify the IOTypes and its command bytes. Here's the MATLAB code including what zeros fills in:
%---
cmd=zeros(14,1);
cmd(1) = 0; %Checksum8
cmd(2) = 248; %Command byte. Feedback command is 0xF8 in hex.
cmd(3) = (length(cmd)-6)/2; %0.5 + Number of Data Words (IOTypes and Data)
cmd(4) = 0; %Extended command number. For Feedback it is 0x00 in hex.
cmd(5) = 0; %Checksum16 (LSB)
cmd(6) = 0; %Checksum16 (MSB)
cmd(7) = 0; %Echo
%IOTypes and Data
cmd(8) = 29; %IOType: PortDirWrite = 29 (This sets all digital I/O directions)
cmd(9:11) = maskdir; %State
cmd(12:14) = valuedir; %Direction
cmd(8) = 27; %IOType: PortStateWrite = 27 (This sets all digital I/O states)
cmd(9:11) = mask; %WriteMask
cmd(12:14) = value; %State
obj.command = obj.checksum(cmd,'extended'); %Calculate and set Checksum8 and Checksum16
obj.outp = obj.rawWrite(obj.command); %Send the 14-byte/vector command
if obj.readResponse; obj.inp = obj.rawRead(zeros(1,10),10); %Read the 10-byte/vector response.
%The response is 9-bytes, but one byte is appended for an even numbered packet.
%---
For more details on the Feedback command-response, and the PortStateWrite and PortStateRead IOTypes, refer to there section in the datasheet:
Feedback: https://labjack.com/support/datasheets/u3/low-level-function-reference/f...
PortStateWrite: https://labjack.com/support/datasheets/u3/low-level-function-reference/f...
PortStateRead: https://labjack.com/support/datasheets/u3/low-level-function-reference/f...
Now regarding a Counter you will probably want 2 functions. One to enable/disable the counter and one to read the current value of the counter.
To enable/diable the counter, use the ConfigIO command-response. Something like (not fully implemented or tested):
%---
cmd=zeros(12,1);
cmd(2) = 248; %Command byte. Feedback command is 0xF8 in hex.
cmd(3) = 3; %Number of Data Words. For ConfigIO it is 0x03
cmd(4) = 11; %Extended command number. For ConfigIO it is 0x0B in hex.
cmd(7) = 1; %WriteMask. TimerCounterConfig is bit 0.
cmd(9) = 68; %TimerCounterConfig. TimerCounterPinOffset is Bits 4-7. Enable Counter0 is Bit 2. Refer to doc. for other config.
%TimerCounterPinOffset = 4 (0x40) which is FIO4 and Counter0 = 1 (0x04), which is 68 combined.
%Set to 64 (0x40) to disable Counter 0.
cmd(11) = 15; %FIOAnalog.
%Setting FIOAnalog to 15 (0x0F) which sets FIO0-3 to analog and FIO4-7 to digital I/O.
%Timer/Counter lines need to be configured for digital, and Counter 0 will be on FIO4.
obj.command = obj.checksum(cmd,'extended'); %Calculate and set Checksum8 and Checksum16
obj.outp = obj.rawWrite(obj.command); %Send the 12-byte/vector command
obj.inp = obj.rawRead(zeros(1,12),12); %Read the 12-byte/vector response. Check response vector index 7 for an ErrorCode (nonzero).
%---
The ConfigIO low-level command-response is documented here:
ConfigIO: https://labjack.com/support/datasheets/u3/low-level-function-reference/c...
To read the counter you will use the Feedback command and its IOType 54 which is Counter0 (55 is Counter 1). That IOType is documented here:
Counter#: https://labjack.com/support/datasheets/u3/low-level-function-reference/f...
Something like (not fully implemented or tested):
%---
%Command size is 9-bytes + 1 padding byte for an even numbered command.
cmd=zeros(10,1);
cmd(2) = 248; %Command byte. Feedback command is 0xF8 in hex.
cmd(3) = (length(cmd)-6)/2; %0.5 + Number of Data Words (IOTypes and Data)
%IOTypes and Data
cmd(8) = 54; %IOType: Counter0 = 54
cmd(9) = 0; %Reset. Set to 1 and the count will reset after the reading is performed.
obj.command = obj.checksum(cmd,'extended'); %Calculate and set Checksum8 and Checksum16
obj.outp = obj.rawWrite(obj.command); %Send the 10-byte/vector command
obj.inp = obj.rawRead(zeros(1, 14),14); %Read the 14-byte/vector response (13-bytes + 1 padding byte).
%Check the ErrorCode at vector index 7 and the Counter 0 value is at indexes 10-13.
%Note that the Counter is a 32-bit value, and the Counter 0 response is the 4-bytes that make up the value
%from least significant (10) to most significant (13) byte.
%---
OK thank you for this.
Here's where I'm at. I installed the LJ software on a windows PC and used the LJ control panel application to test the status of the reed switch (as suggested in one of the earlier posts). This is definitely working (the check next to FIO4 disappears when I move the magnet close to the switch). When I set up Counter 0 the value increments a bunch each time, which suggests I need to use the timer in debounce mode.
Moving back to MATLAB, I created a setCounter function and a readCounter function as suggested (see below). I understand the cmd structure and everything looks correct to me. When I run the functions I don't get any error codes. However, I don't get any response or see any evidence of the counter incrementing. When you say "Check the ErrorCode at vector index 7 and the Counter 0 value is at indexes 10-13", what item in the structure are you referring to? I also need to send the counter value via the 15 pin output to my acquisition software, rather than read it into MATLAB, and I'm not sure how to go about doing this.
Thanks again for your help.
Tom
%==================================================================
% Initialize a counter
%==================================================================
function setCounter(obj)
if obj.silentMode == false && obj.vHandle == 1
cmd=zeros(12,1);
cmd(2) = 248; %Command byte. Feedback command is 0xF8 in hex.
cmd(3) = 3; %Number of Data Words. For ConfigIO it is 0x03
cmd(4) = 11; %Extended command number. For ConfigIO it is 0x0B in hex.
cmd(7) = 1; %WriteMask. TimerCounterConfig is bit 0.
cmd(9) = 68; %TimerCounterConfig. TimerCounterPinOffset is Bits 4-7. Enable Counter0 is Bit 2. Refer to doc. for other config.
%TimerCounterPinOffset = 4 (0x40) which is FIO4 and Counter0 = 1 (0x04), which is 68 combined.
%Set to 64 (0x40) to disable Counter 0.
cmd(11) = 15; %FIOAnalog.
%Setting FIOAnalog to 15 (0x0F) which sets FIO0-3 to analog and FIO4-7 to digital I/O.
%Timer/Counter lines need to be configured for digital, and Counter 0 will be on FIO4.
cmd;
cmd = obj.checksum(cmd,'extended'); %Calculate and set Checksum8 and Checksum16
obj.outp = obj.rawWrite(cmd); %Send the 12-byte/vector command
obj.inp = obj.rawRead(zeros(1,12),12); %Read the 12-byte/vector response. Check response vector index 7 for an ErrorCode (nonzero).
%obj.inp = obj.rawRead(zeros(1,10),10)
%if obj.readResponse; obj.inp = obj.rawRead(zeros(1,10),10);end %Read the 10-byte/vector response.
%The response is 9-bytes, but one byte is appended for an even numbered packet.
end
end
%==================================================================
% Check counter value
%==================================================================
function checkCounter(obj)
if obj.silentMode == false && obj.vHandle == 1
%Command size is 9-bytes + 1 padding byte for an even numbered command.
cmd=zeros(10,1);
cmd(2) = 248; %Command byte. Feedback command is 0xF8 in hex.
cmd(3) = (length(cmd)-6)/2; %0.5 + Number of Data Words (IOTypes and Data)
%IOTypes and Data
cmd(8) = 54; %IOType: Counter0 = 54
cmd(9) = 0; %Reset. Set to 1 and the count will reset after the reading is performed.
cmd;
cmd = obj.checksum(cmd,'extended'); %Calculate and set Checksum8 and Checksum16
obj.outp = obj.rawWrite(cmd); %Send the 10-byte/vector command
obj.inp = obj.rawRead(zeros(1, 14),14) %Read the 14-byte/vector response (13-bytes + 1 padding byte).
%Check the ErrorCode at vector index 7 and the Counter 0 value is at indexes 10-13.
%Note that the Counter is a 32-bit value, and the Counter 0 response is the 4-bytes that make up the value
%from least significant (10) to most significant (13) byte.
end
end
rawRead should should be returning a response vector/packet. At the time I wrote the code I didn't check the functionality of rawRead and assumed you would figure that part out. I think rawRead works out like below if you need to get the response back.
For setCounter try something like this for the read:
res = zeros(1,12); %response vector to pass
obj.inp = obj.rawRead(res,12); %This will read the response from the U3 and update the res vector
%Check errorcode in res(7)
For checkCounter try:
res = zeros(1,14); %response vector to pass
obj.inp = obj.rawRead(res,14); %This will read the response from the U3 and update the res vector
%Check errorcode in res(7)
%32-bit value is the 4-bytes from res(10) to res(13).
counterValue = res(10) + res(11)*256 + res(12)*65536 + res(13)*16777216;
If you just need to reset the counter after each reading, Set "cmd(9) = 1" in your Feedback command.
If you need to use a timer and mode 6 (Firmware Counter with Debounce), first test it in LJControlPanel and confirm that is what you need. The Timer value for configuration is documented here:
https://labjack.com/support/datasheets/u3/hardware-description/timers-co...
In that case for configuration you will need to modify your ConfigIO command to enable a timer instead of a counter, and then send a Feedback command to set the timer value value (use IOType 43 for Timer0Config). Then for reading the timer's counter value use a Feedback command (use IOType 42 for Timer0)
Regarding sending out the counter value, it sounds like you were using the U3 digital lines to send a values to your other data acquisition software. Each digital line represents a bit of the value. The DB15 provides 12 digital lines, so it can send a 12-bit value (0 to 4095) if each digital I/O represents a bit. The other 8 (U3-LV) or 4 digital I/0 (U3-HV) are provided on the U3's screw terminals, but note you are using one digital line for the counter input. For higher numbers you will need more digital I/O, but keep in mind that the counter value is 32-bits. The setDIO function in labJack.m can set digital outputs.
I think the issue is that rawRead is just returning a single value (not a vector) which is being stored in the obj structure. For example, when I run check counter, I get obj.outp=10 and obj.inp=14. I understand how this is supposed to work, but either the function isn't working as anticipated or I'm trying to access the data incorrectly.
I tested the timer in debounce mode using the LJ Control Panel in windows and this works.
Looking at its rawRead's implementation, it looks like it just returns the amount of bytes read but will update the bytein (first) parameter. In the updated code I suggested, is the "res" vector you pass not being updated with new values (some may still be zero) from rawData.
The res value isn't being updated with new values. It's always just zeros.
OK. I was thinking there might have been pass by reference functionality there, but I guess not. So the rawRead function needs to be modified as it is not returning the read response. rawRead is using this liblabjackusb function in the library:
unsigned long LJUSB_ReadTO(void *hDevice, unsigned char *pBuff, unsigned long count, unsigned int timeout);
The read bytes which fill out the vector is pBuff. Here a similar, simpler version of rawRead (its better to have a second function of it to prevent other functions from breaking):
function [ret_count, ret_bytes] = rawRead2(obj, count)
bytes = zeros(count,1);
[ret_count, ret_handle, ret_bytes] = calllib('liblabjackusb', 'LJUSB_ReadTO', obj.handle, bytes, count, obj.timeOut);
if ret_count == 0; obj.salutation('rawRead','ERROR READING!',true); end
end
Usage would look like this in setCounter:
[obj.inp, res] = obj.rawRead2(12); %This will read a 12-byte response (vector "res" with 12 elements).
Creating this second rawRead function worked well. I'm now able to send a code to my acquisition machine every time the reed switch is activated. Thank you for your help with figuring this out - I really appreciate it.