1,847
edits
m (draft) |
m (Remove redundant Category:Packages.) |
||
(41 intermediate revisions by 16 users not shown) | |||
Line 1: | Line 1: | ||
Instrument-Control is a package for interfacing the outside world of hardware via Serial, i2c or Parallel interfaces. It is currently under development by Andrius Sutas and Stefan Mahr, you can browse the [https://sourceforge.net/p/octave/instrument-control/ci/default/tree/ mercurial repository here] and download the package [http://octave.sourceforge.net/instrument-control/index.html here]. | |||
= Compatibility = | |||
Below is a table showing package compatibility with various platforms: | |||
{| class="wikitable" style="text-align:center" | |||
|- | |||
! !! Linux !! Windows (Cygwin) !! Windows (native) !! FreeBSD !! Mac OS X | |||
|- | |||
! Serial | |||
| bgcolor="green" | v0.1.0 || bgcolor="green" | v0.2.0 || bgcolor="green" | v0.2.1 || bgcolor="skyblue" | stalled || bgcolor="green" | v0.2.2 | |||
|- | |||
! Parallel | |||
| bgcolor="green" | v0.1.0 || - || - || bgcolor="skyblue" | stalled || - | |||
|- | |||
! i2c | |||
| bgcolor="green" | v0.1.0 || - || - || bgcolor="skyblue" | stalled || - | |||
|- | |||
! TCP | |||
| bgcolor="green" | v0.2.0 || bgcolor="green" | v0.2.0 || bgcolor="green" | v0.2.0 || bgcolor="skyblue" | stalled || bgcolor="green" | v0.2.2 | |||
|- | |||
! UDP | |||
| bgcolor="green" | v0.3.0 || bgcolor="green" | v0.3.0 || bgcolor="green" | v0.3.0 || - || bgcolor="green" | v0.3.0 | |||
|- | |||
! USBTMC | |||
| bgcolor="green" | v0.2.0 || - || - || - || - | |||
|- | |||
! GPIB | |||
| bgcolor="green" | v0.2.0 [[#Linux|(*)]] || bgcolor="green" | v0.2.0 [[#Windows_.28cygwin.29|(*)]] || - ||- || - | |||
|- | |||
! VXI11 | |||
| bgcolor="green" | v0.2.0|| bgcolor="green" | v0.2.0 [[#Windows_.28cygwin.29|(*)]] || - || - || bgcolor="yellow" | WIP [[#MacOS|(*)]] | |||
|} | |||
Where: <span style="background:green">(VER)</span> - Included since VER version; <span style="background:yellow">(WIP)</span> - Work In Progress and will probably be included in next release; <span style="background:skyblue">(stalled)</span> - Some work has been done, but it's not tested and no one is actively contributing; (-) - Not supported / Untested. | |||
You might see errors while doing "pkg install", which mean that package is unable to support related interface on your platform (e.g. due to missing header files), therefore one should <b>always</b> check for the supported interfaces before trying to use them: | |||
{{Code|Check for interface support|<syntaxhighlight lang="octave" style="font-size:13px"> | |||
pkg load instrument-control | |||
if (exist("serial") == 3) | |||
disp("Serial: Supported") | |||
else | |||
disp("Serial: Unsupported") | |||
endif | |||
#similarly with: | |||
#exist("parallel") == 3 | |||
#exist("i2c") == 3 | |||
</syntaxhighlight>}} | |||
= Requirements = | |||
== Linux == | |||
For GPIB support, please install [http://linux-gpib.sourceforge.net linux-gpib] before installing instrument-control. | |||
== Windows (cygwin) == | |||
For VXI11 support, please install 'rpcgen', 'libtirpc-devel', and 'libtirpc1' before installing instrument-control. | |||
To be able to use GPIB on windows, the linux-gpib library needs to be 'faked'. This can be done by copying the following script and run it in a bash-terminal. | |||
<source lang="bash"> | |||
#!/bin/bash | |||
# fake_linux_gpib.sh, Kire Pûdsje, Dec 2017 | |||
# This file generates a fake linux-gpib library on Windows, based on the | |||
# installed NI libraries. | |||
# Prerequisits are that during install of the NI drivers, the C/C++ support | |||
# and the NI Measurement & Automation explorer has been enabled. | |||
# set the base for includes and libraries on your system | |||
# it is usually /usr, but on newer windows installs of octave, it will be | |||
# /mingw64 | |||
INSTALL_BASE=/usr | |||
# this define should find the NI header file, if properly installled | |||
# otherwise just point it to the proper header file. | |||
NI_H_FILE="${NIEXTCCOMPILERSUPP}/include/ni4882.h" | |||
NI_DLL_FILE=ni4882.dll | |||
NI_DEF_FILE=/tmp/${0##*/}-$$.def | |||
# symlink NI header file | |||
mkdir -p ${INSTALL_BASE}/include/gpib/ | |||
ln -sf "${NI_H_FILE}" ${INSTALL_BASE}/include/gpib/ib.h | |||
# generate .def for all functions in the NI header file with a NI488CC prefix | |||
echo LIBRARY ${NI_DLL_FILE} > ${NI_DEF_FILE} | |||
echo EXPORTS >> ${NI_DEF_FILE} | |||
grep "NI488CC *[A-Za-z]" ${INSTALL_BASE}/include/gpib/ib.h \ | |||
| sed "s/^.*NI488CC *//" \ | |||
| sed "s/(.*$//" >> ${NI_DEF_FILE} | |||
# generate the wrapper library simulating gpib-linux | |||
dlltool -d ${NI_DEF_FILE} -l ${INSTALL_BASE}/lib/libgpib.a | |||
#cleanup | |||
rm -f ${NI_DEF_FILE} | |||
</source> | |||
== MacOS == | |||
You need to build '''rpcgen''' from source [http://mirror.ancl.hawaii.edu/pub/FreeBSD/FreeBSD-current/src/usr.bin/rpcgen]. | |||
If rpcgen cannot be build from source, the following error might be obtained: | |||
<syntaxhighlight lang="text" style="font-size:13px"> | |||
pkg: error running `make' for the instrument-control package. | |||
error: called from | |||
configure_make at line 99 column 9 | |||
install at line 184 column 7 | |||
pkg at line 437 column 9 | |||
</syntaxhighlight> | |||
If the '''VXI-11 interface''' is not required, a workarround is proposed in [https://stackoverflow.com/questions/46720092/make-error-when-installing-instrument-control-package-for-octave/52860282#52860282] after the bug reported on [https://savannah.gnu.org/bugs/?54842]. | |||
= Examples = | = Examples = | ||
Line 8: | Line 123: | ||
* If you do not have a Serial adapter then create a virtual port using: | * If you do not have a Serial adapter then create a virtual port using: | ||
<pre> | <pre> | ||
$ socat PTY,link=/dev/ttyS15 PTY,link=/dev/ttyS16 | |||
</pre> | </pre> | ||
Line 18: | Line 133: | ||
do not forget to change interface permissions if you want to use them with Octave/screen without root privileges. | do not forget to change interface permissions if you want to use them with Octave/screen without root privileges. | ||
=== | === Example: basic use === | ||
=== | Here is a simple example to get started with the serial package. It tests the RxD and TxD lines. Make sure you have defined a loopback or connected the pins of your adapter. | ||
== | {{Code|Serial port example|<syntaxhighlight lang="octave" style="font-size:13px"> | ||
# Opens serial port ttyUSB1 with baudrate of 115200 (config defaults to 8-N-1) | |||
s1 = serial("/dev/ttyUSB1", 115200) | |||
# Flush input and output buffers | |||
srl_flush(s1); | |||
# Blocking write call, currently only accepts strings | |||
srl_write(s1, "Hello world!") | |||
# Blocking read call, returns uint8 array of exactly 12 bytes read | |||
data = srl_read(s1, 12) | |||
# Convert uint8 array to string, | |||
char(data) | |||
</syntaxhighlight> | |||
}} | |||
Changing some configurations is simply done by calling helper functions | |||
{{Code|Serial port example: helper functions|<syntaxhighlight lang="octave" style="font-size:13px"> | |||
set(s1, "baudrate", 9600) # Change baudrate | |||
set(s1, "bytesize", 5) # Change byte size (config becomes 5-N-1) | |||
set(s1, "parity", "E") # Changes parity checking (config becomes 5-E-1), | |||
# possible values [E]ven, [O]dd, [N]one. | |||
set(s1, "stopbits", 2) # Changes stop bits (config becomes 5-E-2), possible | |||
# values 1, 2. | |||
set(s1, "dataterminalready", "on") # Enables DTR line | |||
set(s1, "requesttosend", "on") # Enables RTS line | |||
# possible values "on", "off". | |||
</syntaxhighlight> | |||
}} | |||
Some properties can be set at opening time | |||
{{Code|Serial port example: constructor call|<syntaxhighlight lang="octave" style="font-size:13px"> | |||
s2 = serial("/dev/ttyS0", 9600, 10) # Opens serial port ttyS0 in | |||
# 9600 baudrate with 1s read timeout | |||
</syntaxhighlight> | |||
}} | |||
Do not forget to close the ports when you are done! | |||
{{Code||<syntaxhighlight lang="octave" style="font-size:13px"> | |||
fclose(s1) # Closes and releases serial interface object | |||
fclose(s2) # Closes and releases serial interface object | |||
</syntaxhighlight> | |||
}} | |||
=== Example: Windows, serial port >COM9 === | |||
Opening a serial port higher than COM9 requires special syntax. [https://support.microsoft.com/en-us/kb/115831] | |||
{{Code|open Windows higher serial port example|<syntaxhighlight lang="octave" style="font-size:13px"> | |||
# Opens serial port COM10 | |||
s1 = serial("\\\\.\\COM10"); | |||
</syntaxhighlight> | |||
}} | |||
== Parallel == | == Parallel == | ||
parallel | === Configuring interface === | ||
You will need to load following modules: | |||
<pre> | |||
# modprobe parport | |||
# modprobe ppdev | |||
</pre> | |||
Now you should see devices like "/dev/parport0". In case you do not, you will need to create them manually and give sufficient privileges for your user: | |||
<pre> | |||
# mknod /dev/parport0 c 99 0 -m 666 | |||
</pre> | |||
=== Example 1: LED trace === | |||
For this example you will need to connect 8 LEDs to Parallel interface Data port as shown below: | |||
[[File:parport_schematic.png|300px|text-bottom]] | |||
And then the actual script: | |||
{{Code|trace.m|<syntaxhighlight lang="octave" style="font-size:13px"> | |||
delay = 0.1; | |||
pp = parallel("/dev/parport0", 0); | |||
pp_data(pp, 0); | |||
pins = pow2([0, 1, 2, 3, 4, 5, 6, 7]); | |||
while 1 | |||
for p = pins | |||
pp_data(pp, p); | |||
sleep(delay); | |||
endfor | |||
# Reverse the order | |||
pins = pins(end:-1:1); | |||
endwhile | |||
pp_close(pp); | |||
</syntaxhighlight>}} | |||
Run it and you should see LEDs "tracing" forwards and backwards. | |||
=== Example 2: LED dimming a.k.a. PWM control === | |||
Use the same schematic as in Example 1. | |||
{{Code|dim.m|<syntaxhighlight lang="octave" style="font-size:13px"> | |||
delay = 0.00005; | |||
pp = parallel("/dev/parport0", 0); | |||
pp_data(pp, 0); | |||
dRange = 0:100; | |||
while 1 | |||
for duty = dRange | |||
pp_data(pp, 255); | |||
for dOn = 0:duty | |||
sleep(delay); | |||
endfor | |||
pp_data(pp, 0); | |||
for dOff = 0:(100-duty) | |||
sleep(delay); | |||
endfor | |||
endfor | |||
# Reverse order | |||
dRange = dRange(end:-1:1); | |||
endwhile | |||
pp_close(pp); | |||
</syntaxhighlight>}} | |||
Run it and you should see LEDs changing brightness from lowest to maximum. | |||
=== Example 3: Logic analyzer === | |||
We can surely make something more interesting, right? Enter basic logic analyzer. | |||
Assume you are working with [[http://datasheets.maximintegrated.com/en/ds/MAX31855.pdf this]] temperature sensor. Something is not right. You do not have one of those expensive logic analyzers, but you do have a Parallel port! You remember that someone made a package for interfacing it with GNU Octave. So you connect your probes to appropriate Data port terminals and change settings accordingly. In this example: | |||
{| class="wikitable" | |||
|- | |||
! Probe !! Port terminal | |||
|- | |||
| !CS || DATA0 | |||
|- | |||
| SCK || DATA1 | |||
|- | |||
| SO || DATA2 | |||
|} | |||
NB: Parallel ports usually have weak pull-ups to +5V even when in "input" mode, so do not do this if unsure. | |||
One could potentially use different terminals in Control/Status ports to get true high-impedance inputs. | |||
And write a simple script below: | |||
{{Code|logic_analyzer.m|<syntaxhighlight lang="octave" style="font-size:13px"> | |||
##################################################################### | |||
# Settings | |||
##################################################################### | |||
# Channels to capture | |||
#channels = [0, 1, 2, 3, 4, 5, 6, 7]; | |||
channels = [2, 1, 0]; | |||
# Channel labels | |||
#channel = {"CH0"; "CH1"; "CH2"; "CH3"; "CH4"; "CH5"; "CH6"; "CH7"}; | |||
channel = {"SO"; "SCK"; "!CS"}; | |||
# Trigger channel | |||
triggerCh = 0; | |||
# When to trigger | |||
trigger = 0; # Capture on low. For high - 1 | |||
##################################################################### | |||
samplesTime = []; | |||
samplesValue = []; | |||
#pp_close(pp); | |||
pp = parallel("/dev/parport0", 1); | |||
printf("Waiting for trigger...\n"); | |||
fflush(stdout); | |||
data = pp_data(pp); | |||
while (bitget(data, triggerCh + 1) != trigger) | |||
oldData = data; | |||
data = pp_data(pp); | |||
endwhile | |||
printf("Capturing...\n"); | |||
fflush(stdout); | |||
startTime = time(); | |||
samplesTime(end + 1) = 0; | |||
samplesValue(end + 1) = oldData; | |||
while (bitget(data, triggerCh + 1) == trigger) | |||
data = pp_data(pp); | |||
samplesTime(end + 1) = time() - startTime; | |||
samplesValue(end + 1) = data; | |||
endwhile | |||
# Statistics | |||
printf("Average sample rate: %f kHz\n", size(samplesValue)(2) / samplesTime(end) / 1000.0); | |||
pp_close(pp); | |||
# Plotting | |||
figure; | |||
for p = 1:size(channels)(2) | |||
subplot (size(channels)(2), 1, p) | |||
plot(samplesTime, bitget(samplesValue, channels(p) + 1)) | |||
ylabel(channel{p}); | |||
axis([-0.01, samplesTime(end)+ 0.01, -1, 2], "manual"); | |||
set(gca(), 'ytick', -1:2); | |||
set(gca(), 'yticklabel', {''; '0'; '1'; ''}); | |||
endfor | |||
xlabel ("t"); | |||
</syntaxhighlight>}} | |||
If connections and settings are correct you should see something like this: | |||
[[File:logic_analyzer.png|600px|text-bottom]] | |||
Now you can fully debug what is going with your hardware from comfort of GNU Octave. | |||
== i2c == | == i2c == | ||
i2c | i2c | ||
== TCP == | |||
[[Category: | === Example: basic use === | ||
For testing you could start a tcp listener | |||
<pre> | |||
$ socat TCP-LISTEN:8000 - | |||
</pre> | |||
Now you can connect your listener. | |||
{{Code|TCP example|<syntaxhighlight lang="octave" style="font-size:13px"> | |||
# Open TCP connection to 127.0.0.1:8000 | |||
t0 = tcp("127.0.0.1",8000) | |||
# write to listener | |||
tcp_write(t0, "Hello world!") | |||
# Blocking read call, returns uint8 array of exactly 12 bytes read | |||
data = tcp_read(t0, 12) | |||
# Convert uint8 array to string, | |||
char(data) | |||
</syntaxhighlight> | |||
}} | |||
There are several ways to set timeout of read call. | |||
{{Code|set TCP timeout|<syntaxhighlight lang="octave" style="font-size:13px"> | |||
# Open TCP connection to 127.0.0.1:8000 with timeout of 100 ms | |||
t0 = tcp("127.0.0.1",8000,100) | |||
# set timeout to blocking | |||
tcp_timeout(t0, -1) | |||
# the timeout can be overwritten for single read call, in this case 1000ms | |||
data = tcp_read(t0, 12, 1000) | |||
# close tcp session | |||
tcp_close(t0) | |||
</syntaxhighlight> | |||
}} | |||
== USBTMC == | |||
=== Configuring interface === | |||
Recent linux kernels support USBTMC out of the box. Connect your instrument and check if /dev/usbtmc* exists. Set appropriate permissions to /dev/usbtmc* | |||
=== Example: basic use === | |||
{{Code|USBTMC example|<syntaxhighlight lang="octave" style="font-size:13px"> | |||
# Open interface to USB instrument | |||
t0 = usbtmc('/dev/usbtmc0') | |||
# write to listener | |||
usbtmc_write(t0, '*IDN?') | |||
# Blocking read call, returns uint8 array | |||
data = usbtmc_read(t0, 10000) | |||
# Convert uint8 array to string, | |||
char(data) | |||
# close usbtmc session | |||
usbtmc_close(t0) | |||
</syntaxhighlight> | |||
}} | |||
== VXI11 == | |||
=== Example: basic use === | |||
{{Code|VXI11 example|<syntaxhighlight lang="octave" style="font-size:13px"> | |||
# Open VXI11 connection to 192.168.100.100 | |||
t0 = vxi11('192.168.100.100') | |||
# write to listener | |||
vxi11_write(t0, '*IDN?') | |||
# read from instrument, returns uint8 array | |||
data = vxi11_read(t0, 10000) | |||
# Convert uint8 array to string, | |||
char(data) | |||
# close usbtmc session | |||
vxi11_close(t0) | |||
</syntaxhighlight> | |||
}} | |||
=== Limitations === | |||
For now, | |||
* it's not possible to connect more than one instrument per IP address (e.g. VXI11-GPIB gateways) | |||
* only instrument ''inst0'' can be connected | |||
* setting timeout is not implemented (defaults: 10 seconds global timeout, 2 seconds read timeout) | |||
== GPIB == | |||
=== Configuring interface === | |||
For using GPIB you need to install and configure the linux-gpib kernel modules and libraries. | |||
=== Example: basic use === | |||
{{Code|GPIB example|<syntaxhighlight lang="octave" style="font-size:13px"> | |||
# Open GPIB instrument with ID 7 and set timeout to 1 second (see ibtmo / NI-488.2 Function Reference) | |||
t0 = gpib(7,11) | |||
# write to listener | |||
gpib_write(t0, '*IDN?') | |||
# read from instrument, returns uint8 array | |||
data = gpib_read(t0, 10000) | |||
# Convert uint8 array to string, | |||
char(data) | |||
# close usbtmc session | |||
gpib_close(t0) | |||
</syntaxhighlight> | |||
}} | |||
=== Limitations === | |||
* Setting minor, sad, send_eoi and eos_mode is not implemented yet. | |||
* Every read or write command opens and closes a new gpib session, since the linux-gpib session pointer is only valid for single command. | |||
[[Category:Octave Forge]] |