Daqarta
Data AcQuisition And Real-Time Analysis
Scope - Spectrum - Spectrogram - Signal Generator
Software for Windows
Science with your Sound Card!
The following is from the Daqarta Help system:

Features:

Oscilloscope

Spectrum Analyzer

8-Channel
Signal Generator

(Absolutely FREE!)

Spectrogram

Pitch Tracker

Pitch-to-MIDI

DaqMusiq Generator
(Free Music... Forever!)

Engine Simulator

LCR Meter

Remote Operation

DC Measurements

True RMS Voltmeter

Sound Level Meter

Frequency Counter
    Period
    Event
    Spectral Event

    Temperature
    Pressure
    MHz Frequencies

Data Logger

Waveform Averager

Histogram

Post-Stimulus Time
Histogram (PSTH)

THD Meter

IMD Meter

Precision Phase Meter

Pulse Meter

Macro System

Multi-Trace Arrays

Trigger Controls

Auto-Calibration

Spectral Peak Track

Spectrum Limit Testing

Direct-to-Disk Recording

Accessibility

Applications:

Frequency response

Distortion measurement

Speech and music

Microphone calibration

Loudspeaker test

Auditory phenomena

Musical instrument tuning

Animal sound

Evoked potentials

Rotating machinery

Automotive

Product test

Contact us about
your application!

USB / Serial Communications Port Access


Macro: Port

Introduction:

The Port command allows Daqarta macros to access RS-232 or similar communications (COM) ports, including USB virtual COM ports. A typical use is creation of macro scripts that interact with external hardware, for simply turning things off and on, or sensing when some switch changes. But Port is also fundamental to much more sophisticated applications.

This command is included in Daqarta specifically to control inexpensive devices such as the open-hardware Arduino (<https://www.arduino.cc>), available from many sources:

It can also control a family of USB GPIO (General Purpose Input/Output) devices from Numato Labs (<https://numato.com>):

These devices are used with Daqarta's DC Chart Recorder macro mini-app to allow recording of positive DC voltages or I/O pin states, something that sound cards can't do without DC input modifications. These devices can also be used for custom measurement and control operations.

Arduino devices are user-programmable, and Daqarta includes such a program ("sketch" in Arduino-speak) called DaqPort.ino that uses the USB/serial port to communicate with Daqarta on your computer for data collection and control, including the high-speed DaquinOscope. (DaqPort is not tied to Daqarta; it can be used with any software that observes its protocol. You can also add your own custom modifications or extensions to DaqPort.)

The Numato devices are pre-programmed to use terminal-type serial communications, and as such can be controlled by any terminal software such as TeraTerm or HyperTerminal. Conventional terminal operation involves typing in each command as needed, such as "adc read 0" to read the voltage on ADC 0. That's fine if you just want to test various functions on the device, but not for serious data collection or control.

Daqarta's Port command allows you to use macros to automate operation and achieve the fastest speeds the devices are capable of. However, please note that these devices are not capable of truly high-speed operation; effective chart recorder sample rates are limited to less than 100 samples per second, compared to sound card rates of 48000 or more.

(Note, however, that the DaquinOscope mini-app can run the Arduino at rates well above 48000 samples per second as an oscilloscope, which collects a "burst" of 1024 samples, then displays them, and repeats.)

The basic approach is that you first open the port using Port#O=5 or whatever port number your devices uses, or the _ComDev_Scan macro that finds it automatically (more on this later). Then you give one or more Port commands as needed for your task, and when you are finally done you close the port again with Port#O=0.

Now we need to understand writing to the port and reading from the port. Writing is easiest to understand: It sends a command consisting of one or more 8-bit bytes to the device, which the device reads and interprets, then acts upon. If the device action is "write only", such as setting a certain digital output line high or low, then that's the end of that command.

But there are also commands that tell the device to read an ADC or digital line, and report the value. In such cases the device sends one or more bytes of data back via the port, which must then be read by the host computer (the Daqarta macro in this case). That means that the macro consists of a write command telling the device to acquire the value, followed by a read command to receive the value from the device.

For example, a typical command to read an ADC channel might be Port="adc read 0" for the Numato, or Port#D1=hA0 to do the same on Arduino. (See Command Summary, below, for more details.) These commands tell their respective devices to read ADC channel 0 on the device and send the result back.

The command must be followed immediately by a port read command that actually collects the value from the USB/serial port and assigns it to a specific variable such as A. For the Numato the command is A=Port?V, while the Arduino uses A=Port?2 to read 2 data bytes.

Numato Example:

    Port="adc read 0"      ;Send 10 bytes plus terminator
    A=Port?V               ;Read value, 13-16 bytes

Arduino example:

    Port#D1=hA0            ;Send 1 byte read command
    A=Port?2               ;Read value, 2 bytes

The Numato and Arduino use different write and read commands because the Numato is strictly text-based, while the Arduino (using DaqPort) uses a much more compact binary code. Note that the Numato ADC command sends "adc read 0" as a string of 10 bytes, plus a terminator byte. The Arduino DaqPort command does the same thing with a single byte.

Likewise, the Numato returns a string that includes the original 11 bytes plus 1 to 4 digits of data and a command prompt, for a total of 13 to 16 bytes from which the value must be extracted by Daqarta via the A=Port?V statement. The Arduino returns only 2 bytes which are much simpler to process via the A=Port?2 statement.

Although this makes the Arduino faster at port transfers than it would be if it used a text-based scheme like the Numato, it is not in general faster (or even as fast as) the Numato. That's because the Numato runs at full USB transfer (baud) rates, which may be hundreds of times faster than the serial port rates of the Arduino.


Command Summary:

Port setup commands:
Port#O=n          Open COMn port for read/write
Port#Q=n          Query if valid COM port number n
Port#M="mode string"  Set port Mode (must precede #O Open)
Port#Mf=UA             Set mode flags in DCB (must precede #O Open)
Port#N="a"        New terminal write end character "a", default h0D
Port#S="a"        New terminal read Stop character "a", default = ">"
Port#W=UW              Set Wait msec UW that precedes write (0-20)


Timeout commands:
Port#Ti=U1             Set ReadIntervalTimeout
Port#Tm=U2             Set ReadTotalTimeoutMultiplier
Port#Tc=U3             Set ReadTotalTimeoutConstant
Port#T=n          Set read timeout mode: 0 (default) = use stop char
                            1 = timeout-only reads
                            2 = timeout with stop char reads, no wait


Terminal commands:
Port="adc read 0"      Sends command to port.  For read, see following:
X=Port?V               Reads returned data value after above command
X=Port?H               Reads returned data as hex, sets X to binary equivalent
Msg=Port?R             Reads Raw return string, incl. command and final prompt
Msg=Port?$             Reads only the return data as a string


Simple data write commands:
Port#D1=UA             Send low byte of UA to port
Port#D2=UA             Send low 2 bytes, low first
Port#D3=UA             Send low 3 bytes, low first
Port#D4=UA             Send all 4 bytes, low first

Port=$b(UA)            Low byte to port = Port#D1=UA
Port=$w(UA)            Low 2 bytes      = Port#D2=UA
Port=$t(UA)            Low 3 bytes      = Port#D3=UA
Port=$d(UA)            All 4 bytes      = Port#D4=UA

Port=$1(UA)            Low byte to port (same as $b() or $())
Port=$2(UA)            Low 2 bytes, high first
Port=$3(UA)            Low 3 bytes, high first
Port=$4(UA)            All 4 bytes, high first


Special data write command:
Port=$(hF0) + "F" + $d(UA)   Arbitrary-length string to port (6 bytes here)


Bulk data write commands:
Port#An=string    Sends 1K Arbn to port after string prefix
Port#an=string    As above, but creates and sends 256-byte version.
Port#Bn=string    Sends Bufn (0-7 or V) as 1024 bytes after string
Port#bn=string    As above but sends first 256 bytes from buffer.


Simple data read commands:
UA=Port?1              Read port byte into low byte of UA
UA=Port?b              Read byte, same as above
UA=Port?2              Read word
UA=Port?w              Read signed word (MSB extended to fill dword)
UA=Port?3              Read 3 bytes
UA=Port?4              Read dword
UA=Port?d              Read dword, same as above


Special data read commands:
UA=Port?5              Read low nybble of 1st byte as count, then dword
UC=Port?c              Return count from most-recent Port?5 (above)
UN=Port?n              Return total byte count from most-recent Port read


Special bulk data read commands:
Buf0#A=Port?A          Read 1280 analog bytes sent by 0xF2 after prior
                            0xF1, convert to 1024 10-bit samples in Buf0.
Buf0#A2=Port?A         As above but convert to 512 samples in Buf0 and Buf1
Buf0#A4=Port?A         As above but convert to 256 samples in Buf0-3

Buf0#b=Port?a          Read 1024 analog bytes sent by 0xF3 after prior
                            0xF1, convert to 1024 8-bit samples in Buf0
Buf0#b2=Port?a         As above but convert to 512 samples in Buf0 and Buf1
Buf0#b4=Port?a         As above but convert to 256 samples in Buf0-3

Buf0#Dn=Port?D    Read 1024 digital bytes sent by 0xF3 after 0xF1,
                            with h40 bit set in next byte.  Reads digital bit 'n'
BufN#Dn=Port?C    Continued read, digital bit n to new BufN.


Status read commands:
UB=Port?B              Read UB baud rate of open port
UM=Port?M              Read port mode status (0 = err, non-zero = OK)
UF=Port?f              Read initial DCB flags found during Port#O open
UA=Port?F              Read target DCB Flags (default=1) set via Port#Mf=UA
UN=Port?N              Read UN port number set via Port#O, 0 if failed

Port Setup Commands:

Before you can send or receive data over a COM port, it must first be opened.

Port#O=n opens COMn port for read and write. If the port opened properly, UN=Port?N will return the requested port number. If it failed to open, UN will be 0.

Port#Q=n Queries COMn to see if it is a valid port. It is similar to the above Open command, but it doesn't actually try to open the port. However, it still allows UN=Port?N to report just as above.

These commands require you to know the COM number, which will be different for different devices or even for the same device plugged into a different USB connector. Manufacturers typically provide instructions for determining the COM number, but those usually assume you always use the same device on the same USB.

Daqarta provides the _ComDev_Scan macro subroutine to simplify this for the types of devices that Daqarta supports. You tell it which device you want to use, and it finds the proper port and opens it. It takes care of the above commands so you don't have to. (See the _ComDev_Scan subtopic for a complete listing.)

However, _ComDev_Scan assumes you are using one of the supported Numato or Arduino devices. If you are writing a macro that uses something else, you may still be able to use _ComDev_Scan if you modify it to support your device.

Port#M="mode string" sets most port parameters. You normally won't need this command, since the default settings work for the Arduino and Numato devices that Daqarta currently supports. If you do need it for some other device, or some special application of a supported device, it must be given before the Port#O open command, or before invoking _ComDev_Scan. The quoted mode string may take different forms. Daqarta's default string is equivalent to:

Port#M="baud=115200 parity=N data=8 stop=1"

The string is passed as-is to the Windows BuildCommDCB function, so you can use any form that Windows supports. There are other settable parameters besides those used here, but the online Windows documentation is exceedingly poor. Apparently the assumption is that anyone using COM ports in this day and age has a collection of old DOS manuals with the original cryptic abbreviations.

You can determine whether BuildCommDCB failed by reading its return value with UM=Port?M; 0 indicates failure, while anything else is OK.

You can copy the complete DCB (Device Control Block) to a buffer via Buf0="<Port" for subsequent examination. The DCB will always be copied to elements [0] to [14], regardless of whether an index is used. Element [15] will hold the return value otherwise read via UM=Port?M as mentioned above.

    [0]     DCBlength
    [1]     BaudRate
    [2]     fbits (see below)
    [3]     wReserved
    [4]     XonLim
    [5]     XoffLim
    [6]     ByteSize
    [7]     Parity
    [8]     StopBits
    [9]     XonChar
    [10]    XoffChar
    [11]    ErrorChar
    [12]    EofChar
    [13]    EvtChar
    [14]    wReserved1
    [15]    Return value (see above)

Port#Mf=UA sets the desired mode flags (fbits) in the COM port DCB structure. This is a 32-bit value treated by Windows as 15 flag bits (the least-significant bits of the value) that supercedes any flags that may be set by the above Mode command, unless the UA value is negative. The default value is 1 (fBinary flag bit only).

     Use:                  Bits:
    fBinary                  0
    fParity                  1
    fOutxCtsFlow             2
    fOutxDsrFlow             3
    fDtrControl              4,5
    fDsrSensitivity          6
    fTXContinueOnXoff        7
    fOutX                    8
    fInX                     9
    fErrorChar               10
    fNull                    11
    fRtsControl              12,13
    fAbortOnError            14
    fDummy2                  15-31

As with the Mode command, if you use it you must do so before the Open command or _ComDev_Scan invocation. You can read the value (or default, if you didn't set it) via UA=Port?F. You can read the (superceded) value that the Mode command returned via UF=Port?f. These values are used by _ComDev_Scan to determine if it is the first Open of the port in a Daqarta session, since after that the UF flags will always match the UA flags. This is needed for certain versions of the Arduino, which require a 2 second initialization delay (undocumented) the first time the port is opened in a session.

Port#N="a" replaces the default terminal end character h0D (carriage return) with any desired character "a". This character is appended after any string given by a terminal write command such as Port="adc read 0". A carriage return is standard for terminal communications, and is used by the supported Numato devices, but you may want to change this if you are writing a macro for some other device. This command does not need to appear before the Open command or _ComDev_Scan invocation, but must be given at least before the first terminal write command.

Port#S="a" replaces the default read Stop character ">" with any desired character "a". This is the character that a terminal-type device sends back to signify the end of its response. This character is sent by the supported Numato devices and is fairly standard in general, but you may need to change it for some other devices. This command does not need to appear before the Open command or _ComDev_Scan invocation, but must be given at least before the first terminal read.

Port#W=UW sets an optional Wait period (Windows Sleep) that precedes each write to the port. The value must be in the range of 0-20, which is interpreted as milliseconds. The default is 0. This command is supplied for dealing with slow devices, and is not needed on the supported Numato and Arduino units.


Timeout Commands:

Daqarta supports COM port Timeouts for read operations. These allow a read to be automatically aborted if there is nothing detected from the device within the alloted time. This prevents a macro waiting endlessly for a non-responding device, whether due to a bug in your code, or a defective or unplugged device.

Three separate values set the timeout parameters used by Windows:

    Port#Ti=U1     ReadIntervalTimeout
    Port#Tm=U2     ReadTotalTimeoutMultiplier
    Port#Tc=U3     ReadTotalTimeoutConstant

The ReadIntervalTimeout sets the maximum time in milliseconds between the arrival of any two characters (bytes) of data.

More importantly for Daqarta use, there is also a total timeout that starts from the beginning of the read attempt. It consists of two parts: ReadTotalTimeoutMultiplier and ReadTotalTimeoutConstant. The total timeout in milliseconds is the product of the Multiplier times the number of bytes requested to be read, plus the Constant.

A good starting point is to set all three values to 1.


Daqarta allows two modes of timeout operation, or none, as set by Port#T=n, where n may be 0, 1, or 2:

    0   No timeouts, use stop character
    1   Use timeouts only, no stop character
    2   Use timeouts with stop character

The DC_Chart_Recorder and DaquinOscope macro mini-apps included with Daqarta use Port#T=0, meaning no timeouts are used. However, they both call the _ComDev_Scan subroutine when they start up, and that uses Port#T=2 (timeouts with stop character) as part of its device identification process. It sends a special command and attempts to read a response every 10 msec for 100 msec total. If it gets no response (times out), or one it doesn't recognize, it knows the device is not supported by these mini-apps.

A similar timeout application is needed for the Arduino Event/Race Timer. This DaqPort-based macro mini-app allows you to time multiple simultaneous processes, such as multiple lanes of a slot car track or Soap Box Derby. Since each contestant can pass the finish line at an arbitrary time, and you don't want to stall the system waiting for the last one, you can check every 10 msec or so and see who's come in.


Terminal Commands:

These commands are for use with terminal-type devices such as those from Numato. They could also be used with the Arduino if you load it with a terminal-based sketch instead of the much faster DaqPort.

Port="gpio set 1" commands a Numato device to set the General Purpose I/O (GPIO) digital pin 1 to high. This is a write only command that does not produce a response from the device. Similar commands clear a pin to low, or write all pins at once.

Port="adc read 0" commands a Numato device to read ADC 0. This type of command causes the device to send a response back, in this case the current value from the ADC, which must be read separately. Similar write/read commands are Port="gpio read 4" to read digital pin 4, or Port="gpio readall" to read all available digital pins.

Numato devices always respond with the original read command, including the command prompt and carriage return (newline), followed by a string representation of the result value in most cases, or a hexadecimal value for "gpio readall".

X=Port?V reads the response from the terminal device, isolates the requested value string, and converts it to a binary value in X.

X=Port?H does the same thing where a hexadecimal value is returned by the device, converting it to a binary value in X.

Msg=Port?$ reads only the returned data as a string, assuming the Numato format. For example, if you have just sent an ADC read command and the current value of the specified ADC is 987, the Msg will show "987" (without the quotes). You can read the string value into Notes, String Arrays Str0-7, Fields, Custom Meters Mtr0-3, Log Files or other string locations.

If you have a device that uses a different scheme, you may have to write your own macro code to process the returned string. Msg=Port?R displays the complete Raw string, including (in the case of Numato) the read command and final prompt. Once you see what's going on, you can (for example) use Str0=Port?R to send it to a string array and operate on it to extract what you need.


Simple Data Write Commands:

Port#D1=UA     Send low byte of UA to port
Port#D2=UA     Send low 2 bytes
Port#D3=UA     Send low 3 bytes
Port#D4=UA     Send all 4 bytes, low to high

These commands are for sending one to four bytes of data to the port device. They are used with non-terminal devices like the Arduino (running DaqPort) to send commands. For example, the DC_Chart_Recorder subroutine _DC_Chart_Arduino uses Port#D1=hA0 sends one byte (hex value A0) to initiate an analogRead on channel 0. (_DC_Chart_Numato08 for the terminal-type Numato uses Port="adc read 0" instead.)

Similarly, Port#D2=h40D0 + UI is used to send a 2-byte value consisting of h40D0 plus UI, where UI runs from 2 to 9 in a WHILE loop to set digital pins 2-9 as inputs (the h40 byte).

Note that for the multi-byte versions, the bytes are sent in low to high order. So in the above example the hD0 byte is sent first, then the h40 byte.

Note also that the exact same results could be obtained using Binary-to-String Format, as discussed under String Variables and Expressions:

Port=$b(UA) = Port#D1=UA      Low byte to port
Port=$w(UA) = Port#D2=UA      Low 2 bytes
Port=$t(UA) = Port#D3=UA      Low 3 bytes
Port=$d(UA) = Port#D4=UA      All 4 bytes

For a single byte, you can omit the b such that Port=$b(UA) becomes simply Port=$(UA).

In the above forms, the low byte is always sent first. If the need arises, you can alternatively send the bytes in high-to-low order using numerals:

Port=$1(UA)    Low byte to port (same as $b() or $())
Port=$2(UA)    Low 2 bytes, high first
Port=$3(UA)    Low 3 bytes, high first
Port=$4(UA)    All 4 bytes, high first

For example, suppose UA is h31323334. This is the ASCII equivalent of the string "1234" which could be sent in left-to-right order via Port=$4(UA). If you use Port=$d(UA) or Port#D4=UA it would be the same as Port="4321".


Special Data Write Command:

An alternative to the previous simple commands is to send them as a string, which can be of whatever length is needed. The usual way to send a string (which is used by terminal-type devices such as the Numato, as discussed previously) is of the form Port="String".

Here, however, we present a way to send a string that may include binary values as well as text. To get Daqarta to treat a value as a string, you put it in parentheses and precede it with '$', and a possible size character, as noted above and in the Binary-to-String Format subtopic of String Variables and Expressions.

Port=$(hF0) + "F" + $d(UA) sends byte hF0, followed by the ASCII character for F (as indicated by the quotes), followed by all 4 bytes of variable UA sent low byte first. The d in $d(UA) stands for dword, meaning double word, or 4 bytes. There are thus 6 bytes total sent to the port.

Note that there is no requirement for any quoted text at all; since the $ operator means the associated value is treated as a string, you could send just a list of values such as Port=$(hFC) + $(b00011010) + $w(1000).


Bulk Data Write Commands:

Port#An=string sends string to the port, followed by Arbn, where n may be a digit 0-7 or V to use the Arb (arbitrary waveform) indicated by Channel Select variable Ch. The Arb values are decimated as needed to 1024 samples (if the Arb was larger) and automatically scaled to the 0-255 range, then sent as 1024 bytes. The normal use for this command is to load an Arduino DaqPort wave oscillator with the desired waveform. A typical command would be Port#AV=$(hF0) + "O" after setting Ch to the deired Arb number. The right side tells DaqPort that this is a 1024-byte wave oscillator load via the "O".

Port#an=string is the same as above, but the lowercase #a means it will decimate the Arb to 256 samples instead of 1024. The corresponding DaqPort wave oscillator command would be Port#aV=$(hF0) + "o", where the lowercase "o" indicates 256 bytes.

Port#Bn=string sends Bufn (where n is 0-7 or V) as 1024 bytes after sending string. It is used to load a DaqPort wave oscillator with 1024 values, here from a Macro Array Buf0-Buf7 instead of from an Arb. Values are limited to 0-255 after rounding (no automatic scaling).

Port#bn=string is the same as the above, but uses only the first 256 values from the buffer. There is no automatic decimation from the total 1024 buffer size.


Simple Data Read Commands:

UA=Port?1      Read poort byte into low byte of UA
UA=Port?b      Read byte, same as above
UA=Port?2      Read 2-byte word
UA=Port?w      Read signed word (MSB extended to fill dword)
UA=Port?3      Read 3 bytes
UA=Port?4      Read 4-byte dword
UA=Port?d      Read dword, same as above

These commands read one to 4 bytes of data from the port into a Daqarta variable (integer UA in these examples). They are used with non-terminal devices like the Arduino (running DaqPort) to get data requested by a prior write, such as Port#D1=hA0 to read ADC channel 0. Since the returned value is a 16-bit (2 byte) word, the proper read command is the Port?2 form. The DC_Chart_Recorder subroutine _DC_Chart_Arduino uses the following code:

    Port#D1=hA0        ;Request read of ADC 0
    A=Port?2           ;Read 0-1023 value into float A

Please note that the Port?w form is not identical to the Port?2 form, since it sign-extends the most-significant bit of the high byte to fill a larger variable such as 32-bit UA or 80-bit float A. In the case of the Arduino ADC, there would be no difference because the range is only 0-1023; since the high byte never has its most-significant bit set, the result is always positive. But other possible uses may need to read signed words.


Special Data Read Commands:

UA=Port?5 reads two values from the port, an 8-bit value that is held internally, followed by a 32-bit value (low byte first) that goes into UA.

UC=Port?c returns the 8-bit value from the most-recent Port?5, limited to the 0-15 range.

The current use for this pair of commands is to support DaqPort multi-event options such as RCtime and the Event/Race Timer. The count of events or contestants is returned in the 8-bit value and the elapsed time of the first event (race winner) in the 32-bit value. (The pin/contestant numbers and times of the other events are obtained via separate reads afterward.)

UN=Port?n is a general-purpose command that returns the total byte count from the most-recent port read.


Special Bulk Data Read Commands:

Buf0#A=Port?A reads 1280 bytes as 1024 10-bit values, and stores them in Buf0. This command expects 1024 low data bytes followed by 256 packed bytes, each consisting of the high 2 bits of 4 consecutive 10-bit values. You can use Buf0 to Buf7 to receive the data, or BufV to send it to the buffer indicated by the current Ch Channel Select setting.

This command is used by the _Dscope_Task subroutine of the DaquinOscope mini-app after DaqPort function Port#D1=hF2 requests 1024 samples of data acquired via Port=$(hF1) + $(UM). The #A indicates that the 1024 samples all come from a single 10-bit analog (ADC) channel.

Buf0#A2=Port?A is similar to the above, but the #A2 indicates that the 1024 samples are from alternating dual 10-bit analog channels. It converts the data stream so the first channel is stored in the first 512 samples of Buf0 and the second channel in the first 512 of Buf1. The second channel is always stored in the Bufn after the one given in the command, so you can use Buf0 to Buf6, or BufV with Ch set to 0 to 6, but you can't use Buf7.

Buf0#A4=Port?A is again similar, but the A4 indicates that the 1024 samples are from 4 sequential 10-bit analog channels. The data stream is converted to 256 samples of the first channel in Buf0, the next channel to Buf1, then Buf2 and Buf3 in sequence. Thus, you can't use a channel higher than 4 in the command itself.

Buf0#b=Port?a is similar to Buf0#A=Port?A above, but reads 1024 bytes directly as 1024 8-bit analog samples from a single ADC channel. It is used by the _Dscope_Task subroutine of the DaquinOscope mini-app after DaqPort function Port#D1=hF3 requests 1024 bytes of data acquired via Port=$(hF1) + $(UM). (Note the use of hF3 instead of hF2 to select the number of bytes read.)

Buf0#b2=Port?a is similar but the #b2 indicates that the 1024 bytes are from alternating dual 8-bit analog channels, with the first channel sent to the first 512 samples of Buf0 and the second channel to the first 512 in Buf1.

Buf0#b4=Port?a is the 4-channel version for 8-bit data, storing 256 samples of the first channel in Buf0, 256 of the second in Buf1, the third to Buf2, and the fourth to Buf3.

Note that for 10-bit or 8-bit analog data, DaquinOscope automatically adjusts the X-axis eXpand to fill the screen width, so that with 2 channels of 512 samples each the X axis shows half the total time, and with 4 channels only one quarter.

Buf0#Dn=Port?D reads 1024 bytes of digital data and sends bit n of each byte to a corresponding value in Buf0, scaled so that a set bit is stored as 16384 (half of screen full scale) and a clear bit is 0. Here n may be a digit 0-7, or a literal n to use the special bit channel select set via Ch#n=UA. (It can be read separately via UA=Ch?n.)

The 1024 digital bytes are stored internally and may be read to obtain other bits for other buffers using Port?C (Continued read) instead of Port?D. For example, Buf1#D1=Port?C will read bit 1 from each of those stored 1024 bytes and send it to Buf1.

The Ch#n channel select is separate from the normal Ch so that you can use BufV instead of an explicit Buf0 to Buf7 by setting Ch to the desired buffer number, while allowing independent bit selection via Ch#n.

As for the previous commands in this section, this is used by the _Dscope_Task subroutine of the DaquinOscope mini-app after DaqPort function Port#D1=hF3 requests 1024 bytes of data acquired via Port=$(hF1) + $(UM). In this case, UM has bit h80 set to indicate digital acquisition.


Status Read Commands:

UB=Port?B reads the baud rate of the open port into UB.

UM=Port?M sets UM with the port mode status. 0 indicates an error, while anything else is OK. The mode status is the return value of Windows function BuildCommDCB, set via the Port#O Open command according to the prior Port#M Mode command. (Or the Daqarta default... see Port Setup Commands.)

UF=Port?f sets UF with the initial DCB flags found during Port#O open.

UA=Port?F sets UA with the target DCB Flags (default=1) set via Port#Mf=UA. See Port Setup Commands.

UN=Port?N reads the UN port number set via Port#O open, 0 if it failed.


_ComDev_Scan Macro Subroutine:

Before a macro can use a COM port, it must be opened. That requires knowing the number of the port, a value in the 1-255 range. Windows assigns each COM device a unique number, and the number will be different when plugged into a different USB connector.

You could determine the COM number by opening Windows Control Panel and looking under the Ports group in the Device Manager. You could then use that number in the Port#O Open command. But if you hand-code the number into your macro, you have to always use the same device in the same USB connector on the same system. Your code won't be portable to another system, even using the identical device, since Windows will assign different numbers.

Daqarta provides the _ComDev_Scan macro subroutine to simplify this. You call it from your macro instead of using a direct Port#O command. It scans all 255 possible COM numbers, if needed, and tests each for the presence of a COM-type device, and also tests if it is a Daqarta-supported device. It builds a ComDev list which is saved to disk and updated as needed whenever _ComDev_Scan is used, so on future runs it doesn't need to run compatibility tests on devices it has found previously. Since every COM device has a unique number, any device found at that number in the list is positively identified.

_ComDev_Scan assumes you are using one of the supported Numato or Arduino devices. Each type is given a unique 4-character name, which is stored in the ComDev list at each index where it is found:

    "Ardu"      ;Arduino (Uno)
    "ArdU"      ;Arduino clone, needs delay
    "Nu08"      ;Numato 8
    "Nu16"      ;Numato 16
    "Nu32"      ;Numato 32
    "Nu64"      ;Numato 64

To use _ComDev_Scan you need to first set 2 parameters that tell it what you want it to find:

Posn#0 must hold the name of the requested device type, from the above table. For example, use Posn#0="Ardu" to open an Arduino device. Note that certain versions of the Arduino require a 2 second delay the first time they are opened in a session. The scan identifies these and gives them the "ArdU" name, and will automatically provide the delay only as needed. But it doesn't distinguish between these when matching the request; the "Ardu" name will find either type.

Alternatively, Posn#0=0 will find the first device of any supported type, whether Arduino or any Numato GPIO device.

Posn#1=1 tells _ComDev_Scan that you want the first device that matches the name given in Posn#0. This is to allow systems that use multiple devices (plugged into different USB connectors) to control which device is used for a given application. (Note that Posn#1=0 also finds the first matching device.)

As an example of how to use _ComDev_Scan here is the code used by DaquinOscope, which doesn't support Numato devices:

Posn#0="Ardu"      ;Only Arduino supported
Posn#1=0           ;Device count, 0 = first found
@_ComDev_Scan      ;Find and open device port

Here is the code used by DC_Chart_Recorder, which supports the Arduino and all Numato GPIO devices:

Posn#0=0           ;Accept Arduino or Numato08-64
Posn#1=0           ;Device count, 0 = first found
@_ComDev_Scan      ;Find and open device port

If a COM device is found, _ComDev_Scan begins by assuming the device is an Arduino running the DaqPort sketch, and sends a special command (Port#D2=h0DF0) that requests the DaqPort version number. It then waits up to 100 msec for a response, by giving a QD=Port?4 command every 10 msec requesting a 4-byte value. When it gets a response (as determined by a non-zero number of bytes read) it checks the QD value for a 'v' in the 2nd byte, indicating that the 3rd and 4th bytes hold a DaqPort version number. If not, it tests for a Numato device by attempting to read the all the GPIO digital states by sendinng Port="gpio readall". If it finds 8, 16, 32, or 64 bits returned, it assumes a valid Numato device of that type.

If _ComDev_Scan is unable to find or open the requested device, it displays a diagnostic message and exits not only the subroutine, but the caller (DaquinOscope or DC_Chart_Recorder, etc) as well.


_ComDev_Scan Macro Listing:

;On entry, Posn#0 holds a 4-char device name request,
;or 0 for any supported device:
;    Posn#0="Ardu"      ;Arduino
;           "Nu08"      ;Numato 8
;           "Nu16"      ;Numato 16
;           "Nu32"      ;Numato 32
;           "Nu64"      ;Numato 64
;Posn#1 holds the specific device number; 1 will
;return the first board that matches the Posn#0 type, 2 will
;return the 2nd match, etc.
;If a match is found, ComDev[0] holds the COM port number
;(1-255).

;EXAMPLE USE:
;Posn#0="Nu16"              ;Want Numato16 only
;Posn#1=3                   ;Want 3rd-listed instance only
;@_ComDev_Scan

ComDev[0]=0                 ;No matching device found yet
Un=Posn?1                   ;Specific device count (0 = first match)
IF.Posn?0=!0                ;Specific device requested?
    UI=1
    WHILE.UI=<256               ;Scan all list entries 1-255
        UN=Posn?0 | h20             ;Convert 4th char to lowercase (ArdU=Ardu)
        IF.(ComDev[UI] | h20)=UN    ;Match to requested name?
            IF.Posn?1=0                 ;Consider any match?
                Port#Q=UI                   ;Query port
                IF.Port?N=UI                ;Did it open?
                    LoopBreak=2               ;Done with scan if so
                ENDIF.
            ELSE.                       ;Else need specifc number
                Un=Un-1                    ;1-based count to 0-based
                IF.Un=0                    ;0 = first found
                    Port#Q=UI                   ;Query port
                    IF.Port?N=UI                ;Did it open?
                        LoopBreak=2               ;Done with scan
                    ELSE.
                        Mtr0="Port open failure."
                        LoopBreak=-1              ;Abort this and caller
                    ENDIF.
                ENDIF.
            ENDIF.
        ENDIF.
        UI=UI+1                     ;Next entry to scan
    WEND.
ELSE.
    UI=1
    WHILE.UI=<256               ;Scan all list entries 1-255
        IF.ComDev[UI]=>0            ;Not empty, nor -1 (unsupported)
            Port#Q=UI                   ;Query port
            IF.Port?N=UI                ;Did it open?
                LoopBreak=2               ;Done with scan
            ENDIF.
        ENDIF.
        UI=UI+1                     ;Next entry to scan
    WEND.
ENDIF.

;At this point UI should hold the number of the target COM port:
IF.UI=<256
    Port#O=UI                       ;Attempt to open port UI
    IF.(Port?M | Port?N)=0          ;Open failure?
        Mtr0="Port open failure."
        LoopBreak=-1                    ;Abort this and caller
    ENDIF.
    ComDev[0]=UI                    ;Save the port number
    IF.ComDev[UI]="ArdU"            ;Arduino with needed delay?
        IF.Port?f=!Port?F               ;First open?
            Mtr0="Waiting for Arduino init...."
            WaitSecs=2
            Mtr0=
        ENDIF.
    ENDIF.
    LoopBreak=0                     ;Return to caller
ENDIF.

;If device not found in list, scan ports for new device:
Mtr0=
Port#Ti=1                   ;Set timeouts to return if no data
Port#Tm=1
Port#Tc=1
Port#T=2                    ;Enable timeouts

UI=1
WHILE.UI=<256              ;Test COM1 to COM255
    IF.ComDev[UI]=0            ;If nothing listed yet
        Port#Q=UI                  ;Query to see if device present
        IF.Port?N=UI               ;Present?
            Mtr0="Testing COM" + UI
            Port#O=UI                  ;Open port
            IF.(Port?M | Port?N)=0     ;Open failure?
                Mtr0="Port open failure on COM" + UI
                ComDev[UI]=-1              ;List as incompatible
            ELSE.                      ;Else test for compatible device
                Port#S=">"                 ;Numato Stop char, Arduino test
                Port#D2=h0DF0              ;Request DaqPort version
                UN=0
                WHILE.UN=<10               ;Wait 0.1 sec max
                    QD=Port?4                  ;Try to read data
                    IF.Port?n=!0               ;Any bytes read?
                        LoopBreak=2                ;OK if so
                    ELSE.
                        WaitSecs=10m               ;Else wait a little
                    ENDIF.
                    UN=UN+1
                WEND.
                IF.Port?n=0                ;No bytes read?
                    WaitSecs=2                 ;Delayed-init Arduino?
                    Port#D2=h0DF0              ;Repeat Arduino test
                    UN=0
                    WHILE.UN=<10               ;Wait 0.1 sec max
                        QD=Port?4                  ;Try to read data
                        IF.Port?n=!0               ;Any bytes read?
                            LoopBreak=2                ;OK if so
                        ELSE.
                            WaitSecs=10m               ;Else wait a little
                        ENDIF.
                        UN=UN+1
                    WEND.
                    IF.Port?n=0                ;STILL no bytes read?
                        Mtr0="No compatible device on COM" + UI
                        ComDev[UI]=-1          ;List as incompatible
                    ELSE.
                        IF.((QD>>8) & hFF)=h76     ;"v" if Arduino
                            Mtr0="Delayed Arduino on COM" + UI
                            ComDev[UI]="ArdU"          ;NOTE ending 'U'
                        ELSE.
                            Mtr0="No compatible device on COM" + UI
                            ComDev[UI]=-1
                        ENDIF.
                    ENDIF.
                ELSE.
                    IF.((QD>>8) & hFF)=h76     ;"v" if Arduino
                        Mtr0="Found Arduino on COM" + UI
                        ComDev[UI]="Ardu"          ;Normal Arduino
                    ELSE.                      ;Else test for Numato
                        Port="gpio readall"        ;Read all digital inputs
                        UN=0
                        WHILE.UN=<10               ;Wait 0.1 sec max
                            Str0=Port?$                ;Copy returned string
                            IF.Port?n=!0               ;Any bytes read?
                                LoopBreak=2                ;OK if so
                            ELSE.
                                WaitSecs=10m               ;Else wait
                            ENDIF.
                            UN=UN+1
                        WEND.
                        IF.Port?n=0                ;No bytes read?
                            Mtr0="No compatible device on COM" + UI
                            ComDev[UI]=-1
                        ELSE.              ;Numato model = num digital pins
                            UN=Str0?N * 4      ;Num bits = string len * 4
                            IF.UN=<8           ;Must be 8-64
                                Mtr0="No compatible device on COM" + UI
                                ComDev[UI]=-1
                            ELSE.
                                Mtr0="Found Numato" + UN + " on COM" + UI
                                ComDev[UI]="Nu" << 16 + (str(UN) & hFFFF)
                            ENDIF.
                        ENDIF.
                    ENDIF.
                ENDIF.
                IF.Posn?0=0                ;Any match OK?
                    IF.ComDev[UI]=>0           ;Any found?
                        ComDev[0]=UI
                        LoopBreak=2            ;Done if so
                    ENDIF.
                ELSE.                      ;Else specific match
                    IF.(ComDev[UI] | h20)=Posn?0 | h20 ;Target match?
                        ComDev[0]=UI
                        LoopBreak=2                ;Done if so
                    ENDIF.
                ENDIF.
            ENDIF.
            WaitSecs=1
        ENDIF.
    ENDIF.
    UI=UI+1
WEND.
Port#T=0                        ;No timeouts

;Report results:
IF.Posn?0=0                        ;Request any supported device?
    IF.ComDev[0]=0
        Mtr0="Device not found."
        LoopBreak=-1
    ELSE.
        Mtr0=ComDev[UI](A) + " on COM" + ComDev[0]
    ENDIF.
ELSE.                                ;Else specific device only
    IF.Posn?1=0                          ;Any device of this type?
        IF.ComDev[0]=0
            Mtr0=Posn?0(A) + " device not found"
            LoopBreak=-1
        ELSE.
            Mtr0=ComDev[UI](A) + " on COM" + ComDev[0]
        ENDIF.
    ELSE.                               ;Else Nth device of type
        IF.ComDev[0]=0
            IF.UI=>255                      ;Not in ComDev list?
                Mtr0=Posn?0(A) +" #" + Posn?1 + "  not listed"
            ELSE.
                Mtr0=Posn?0(A) +" #" + Posn?1 + "  not conected to COM" + UI
            ENDIF.
        ELSE.
            Mtr0=ComDev[UI](A) +" #" + Posn?1 + " on COM" + ComDev[0]
        ENDIF.
    ENDIF.
ENDIF.

See also Macro Overview

GO:

Questions? Comments? Contact us!

We respond to ALL inquiries, typically within 24 hrs.
INTERSTELLAR RESEARCH:
Over 35 Years of Innovative Instrumentation
© Copyright 2007 - 2023 by Interstellar Research
All rights reserved