Stream protocol¶
The Arduino is interfaced to EPICS through a USB connection (with asyn [1]) and uses the cmd_response sketch to define the interface language. Software must be written in EPICS to communicate in this new language. One easy method is to develop a Stream Device protocol. [2]
The communication between the Arduino and the EPICS IOC goes through several layers, Expanding part of the technology stack shown earlier:
- EPICS IOC software
- EPICS IOC startup file
- EPICS database
- Stream Device protocol
- asyn
- Linux computer
- USB
- Arduino
- cmd_response sketch
Using Stream Device, there is much less work to do than developing traditional EPICS device support (such as [3]). See this Serial Stream Device tutorial [4] for more details.
The Stream Device protocol we develop here is very general and supports most features of the cmd_response sketch.
[1] | The EPICS asyn package is used to communicate with the USB device. |
[2] | EPICS Stream Device: http://epics.web.psi.ch/software/streamdevice/ |
[3] | “Very Simple Example of EPICS Device Support”: http://www-linac.kek.jp/epics/second/ |
[4] | Serial Stream Device tutorial: http://www.aps.anl.gov/epics/modules/soft/asyn/HowToDoSerial_StreamDevice.html |
protocols¶
The support consists of a Stream protocol file. The protocol file implements the desired cmd_response language commands so they may be access by an EPICS database file (see EPICS database).
Each of these protocols maps one (or more) cmd_response commands.
protocol | meaning |
---|---|
ai(pin) | read analogRead(pin) into the record’s RVAL |
ai_mean(pin) | read time-averaged analogRead(pin) into RVAL |
bi(pin) | read digitalRead(pin) into the record’s VAL |
bo(pin) | write digitalWrite(pin) from the record’s VAL |
pwm(pin) | write analogWrite(pin) from the record’s RVAL |
period | set the current averaging period, ms from the record’s RVAL |
rate | read the number of samples/second |
All records will set the DTYP to stream
and then provide the
name of the protocol and file in either the INP or OUT field.
Note
For the bo
and pwm
protocols, the selected
pin will be configured by that protocol for output during
record initialization.
ai(pin)
¶
purpose: | read |
---|---|
cmd_response: | calls |
EPICS record: | |
database example: | |
record(ai, "A0:raw") {
field(DTYP, "stream")
field(INP, "@cmd_response.proto ai(0) $(PORT)")
field(AOFF, "0")
field(ASLO, "0.004887585532746823069403714565") # 5 VDC / 1023 ADC units
field(HOPR, "5")
field(LOPR, "0")
}
|
In the example, the DTYP field indicates a stream protocol will be used.
The INP field says to find protocol ai(pin)
(where pin = 0)
in file cmd_response.proto
. The $(PORT)
is passed to the database
from the IOC startup file and tells Streams which asyn port to use.
Since the value returned from the Arduino is an integer, it needs to be scaled to the range of 0 .. 5 VDC. The analog input channels (a.k.a., ADC: analog-digital converter) on the Arduino are 10-bit. They may have a value from 0 to 1023. Thus, an ADC value of 1023 represents a reading of a 5 VDC signal.
The ASLO field should be set at zero since these integers will never represent a negative voltage. Also HOPR and LOPR are the EPICS display limits (5 and 0 VDC, respectively).
The stream protocol takes care of setting up the channel
for averaging by calling !ai:watch \$1 1
in the record initialization.
ai_mean(pin)
¶
purpose: | read time-averaged |
---|---|
cmd_response: | calls |
EPICS record: | |
database example: | |
record(ai, "A0") {
field(DTYP, "stream")
field(INP, "@cmd_response.proto ai_mean(0) $(PORT)")
field(AOFF, "0")
field(ASLO, "0.000004887585532746823069403714565") # 5 VDC / 1023 ADC units / 1000 multiplier
field(HOPR, "5")
field(LOPR, "0")
}
|
Similar to ai(pin), the computation must also account for the multiplier, \(k\). Here we assume the default value of 1000.
The stream protocol also commands the Arduino
(during its initialization) to start watching
channel pin
, to take time averages.
bi(pin)
¶
purpose: | read |
---|---|
cmd_response: | calls |
EPICS record: | |
database example: | |
record(bi, "bit0") {
field(DTYP, "stream")
field(INP, "@cmd_response.proto bi(0) $(PORT)")
}
|
The stream protocol takes care of setting up the channel
for input by calling !pin \$1 0
in the record initialization.
bo(pin)
¶
purpose: | write |
---|---|
cmd_response: | calls |
EPICS record: | |
database example: | |
record(bo, "bit2") {
field(DTYP, "stream")
field(OUT, "@cmd_response.proto bo(2) $(PORT)")
}
|
The stream protocol takes care of setting up the channel
for output by calling !pin \$1 1
in the record initialization.
pwm(pin)
¶
purpose: | write |
---|---|
cmd_response: | calls |
EPICS record: | |
database example: | |
record(ao, "pwm11") {
field(DTYP, "stream")
field(OUT, "@cmd_response.proto pwm(11) $(PORT)")
field(AOFF, "0")
field(ASLO, "0.01960784313725490196078431372549") # 5 VDC / 255 ADC units
field(EGU, "VDC")
field(PREC, "3")
field(HOPR, "5")
field(LOPR, "0")
field(DRVH, "5")
field(DRVL, "0")
}
|
The stream protocol takes care of setting up the channel
for pulse width modulation (PWM) output
by calling !pin \$1 1
in the record initialization.
period
¶
purpose: | set the current averaging period, ms |
---|---|
cmd_response: | calls |
EPICS record: | |
database example: | |
record(ao, "period") {
field(DTYP, "stream")
field(OUT, "@cmd_response.proto period $(PORT)")
field(AOFF, "0")
field(ASLO, "0.001") # raw units are milliseconds
field(EGU, "s")
}
|
This ao record instance (called period) is configured to use seconds as the engineering unit. The AOFF and ASLO fields will make the conversion seconds to the integer milliseconds used by the the cmd_response sketch. We show here our intent to use seconds by setting the EGU field.
rate
¶
purpose: | read the number of samples/second |
---|---|
cmd_response: | calls |
EPICS record: | |
database example: | |
record(ai, "rate") {
field(DTYP, "stream")
field(INP, "@cmd_response.proto rate $(PORT)")
}
|
file: cmd_reponse.proto
¶
The file is too large for this documentation.
EPICS Streams protocol: | |
---|---|
cmd_response.proto |