Build and use your first EPICS environment
In this lesson you build Experimental Physics and Industrial Control System (EPICS) base and its modules from source, and open a shell on the installed tree. You then start a soft input/output controller (IOC) that serves one process variable (PV). Last, you read and write that PV over Channel Access (CA) and pvAccess (PVA).
What you build in this lesson
At the end of the lesson you have:
- An installed tree at
<install_location>/1.4.0/debian-13/7.0.10, with EPICS base, every module, and the uldaq and open62541 vendor libraries. - A shell whose
PATHandLD_LIBRARY_PATHpoint at that tree. - A running IOC that serves the PV
tutorial:value, and a second shell that reads and writes it.
The lesson runs on a Debian 13 host. It needs:
- The host packages for EPICS base and the modules. The continuous integration (CI) workflows install them with https://github.com/jeonghanlee/pkg_automation; see Package setup in the OS workflows.
- Access to GitHub, and about 3.5 GB of free disk space for the sources, the build products, and the installed tree.
- An empty directory that you own, as the working directory of the lesson. The lesson starts there and keeps one shell open until the IOC stage.
The whole lesson takes about 12 to 15 minutes on a 20-core host; make build
is the longest step.
Fetch EPICS-env and set the install location
-
Clone EPICS-env and enter the clone:
git clone https://github.com/jeonghanlee/EPICS-env cd EPICS-env -
Set the install location in
configure/CONFIG_SITE.local:echo "INSTALL_LOCATION=<install_location>" > configure/CONFIG_SITE.local<install_location>is an absolute path that you can write to, such as/home/<user>/epics-lesson. Set it before running an action such asmake initormake build, because actions try to create this directory. Query-only invocations, such asmake print-INSTALL_LOCATION_EPICS, create no installation directory or generated configuration file. The default install location is${HOME}/epics. -
Print the path of the installed tree:
make print-INSTALL_LOCATION_EPICSThe output is:
<install_location>/1.4.0/debian-13/7.0.10The path adds the EPICS-env release
1.4.0, the operating systemdebian-13, and the EPICS base version7.0.10under your location. The stages below create this tree and fill it.
Build the vendor libraries
The measComp module links the uldaq library and the opcua module links the
open62541 library. In this lesson both libraries go into the vendor
directory of the installed tree.
-
Save the
vendorpath in a shell variable:VENDOR_PATH="$(make print-INSTALL_LOCATION_EPICS)/vendor" -
Point the two modules at that directory in
configure/RELEASE.local:echo "VENDOR_ULDAQ_PATH=${VENDOR_PATH}" > configure/RELEASE.local echo 'OPEN62541_PATH=\$$\$$\(\_OPEN62541_CONFIG_OPCUA\)/../../../vendor' >> configure/RELEASE.localType the second line exactly as shown. Its escapes let the installed
opcuaconfiguration find thevendordirectory relative to its own location. -
Look at the file:
cat configure/RELEASE.localThe file holds two lines:
VENDOR_ULDAQ_PATH=<install_location>/1.4.0/debian-13/7.0.10/vendor OPEN62541_PATH=\$$\$$\(\_OPEN62541_CONFIG_OPCUA\)/../../../vendor -
Build uldaq into the
vendordirectory:git clone https://github.com/jeonghanlee/uldaq-env ../uldaq-env echo "INSTALL_LOCATION=${VENDOR_PATH}" > ../uldaq-env/configure/CONFIG_SITE.local make -C ../uldaq-env init conf build install -
Build open62541 into the
vendordirectory:git clone https://github.com/jeonghanlee/open62541-env ../open62541-env echo "INSTALL_LOCATION=${VENDOR_PATH}" > ../open62541-env/configure/CONFIG_SITE.local make -C ../open62541-env init conf build install -
List the libraries:
ls "${VENDOR_PATH}/lib"The output lists the libraries of both packages:
cmake libopen62541.so libopen62541.so.1 libopen62541.so.1.3.15 libuldaq.a libuldaq.la libuldaq.so libuldaq.so.1 libuldaq.so.1.2.1 pkgconfigThe installed tree exists and holds its first directory,
vendor.
Fetch and patch the sources
-
Clone EPICS base and every module at its pinned tag or commit:
make init -
List the source trees:
ls -d *-srcThe output lists 33 source trees:
asyn-src autosave-src busy-src calc-src caPutLog-src epics-base-src ether_ip-src feed-core-src iocStats-src linStat-src lua-src mca-src MCoreUtils-src measComp-src modbus-src motorMotorSim-src motor-src opcua-src pcas-src pmac-src pscdrv-src pvxs-src pyDevSup-src QPC-src recsync-src retools-src rgamv2-src scaler-src sequencer-src snmp-src sscan-src std-src StreamDevice-srcEPICS base and 32 modules sit next to each other at the repository top. Module pins and dependencies lists the pin of each one. The list above follows the sort order of the
en_US.UTF-8locale; under another locale, such asC,lsorders the names differently. -
Apply the upstream fixes that EPICS-env carries:
make patchThe output starts with the first two patches of EPICS base:
Patching epics-base-src with the file : <clone>/patch/7.0.10-01-b2d2758-putnotify-type-check.p0.patch patching file modules/database/src/ioc/db/dbPutNotifyBlocker.cpp Patching epics-base-src with the file : <clone>/patch/7.0.10-pr0817-mbbi-cosv-aftc.p0.patch patching file modules/database/src/std/rec/mbbiRecord.c<clone>is the absolute path of your EPICS-env clone. EachPatchingline names one patch file from thepatchdirectory; the run applies 37 of them.
Configure, build, and install
-
Write the site configuration of base and every module:
make confThe command prints one empty line. It writes, among other files,
RELEASE.localat the repository top, which tells every module where the installed base is:cat RELEASE.localThe file holds two lines:
EPICS_BASE:=<install_location>/1.4.0/debian-13/7.0.10/base SUPPORT= -
Build base and every module:
make buildThis is the long step. Base installs into the tree as it builds, and each module installs when its build completes.
-
Install the setup and reset scripts, the
commonIocshfragments, and the version file, and complete the base and module installs:make install -
Create the unversioned module links:
make symlinksThe environment script finds the
pvxstools through the linkpvxs, so this step is required for the next stage. -
Look at the top level of the installed tree:
LC_ALL=C make exist LEVEL=1The output is:
<install_location>/1.4.0/debian-13/7.0.10 |-- .versions |-- base |-- modules |-- resetEpicsEnv.bash |-- setEpicsEnv.bash `-- vendor 4 directories, 3 filesLC_ALL=Cmakestreedraw its lines with American Standard Code for Information Interchange (ASCII) characters, as shown above. Repeating installation also leaves backups of replaced setup and reset scripts; those files add entries to the listing.
Open a shell on the installed tree
-
Source the environment script from the installed tree:
source <install_location>/1.4.0/debian-13/7.0.10/setEpicsEnv.bashThe script prints a summary that starts with these lines:
Set the EPICS Environment as follows: THIS Source NAME : setEpicsEnv.bash THIS Source PATH : <install_location>/1.4.0/debian-13/7.0.10 EPICS_BASE : <install_location>/1.4.0/debian-13/7.0.10/base EPICS_HOST_ARCH : linux-x86_64 EPICS_MODULES : <install_location>/1.4.0/debian-13/7.0.10/modules -
Find the IOC program that the lesson uses:
command -v softIocPVXThe output is:
<install_location>/1.4.0/debian-13/7.0.10/modules/pvxs/bin/linux-x86_64/softIocPVXsoftIocPVXcomes from thepvxsmodule. It serves its records over both CA and PVA; thesoftIocprogram of base serves CA only. -
Keep CA and PVA traffic of this lesson on the loopback interface:
export EPICS_CA_AUTO_ADDR_LIST=NO EPICS_CA_ADDR_LIST=127.0.0.1 EPICS_CAS_INTF_ADDR_LIST=127.0.0.1 export EPICS_PVA_AUTO_ADDR_LIST=NO EPICS_PVA_ADDR_LIST=127.0.0.1 EPICS_PVAS_INTF_ADDR_LIST=127.0.0.1Without these settings, a client on a host with more than one network interface can receive replies from the same IOC over several addresses and print a warning for each one.
Start a soft IOC with one record
-
Create a directory for the IOC and enter it:
mkdir ../first-ioc cd ../first-ioc -
Create the file
first.dbwith one analog output record:record(ao, "tutorial:value") { field(VAL, "42") field(PINI, "YES") }PINIprocesses the record once at start, so the PV holds42with a valid time stamp. -
Start the IOC with the database:
softIocPVX -d first.dbThe IOC prints a banner and waits at its prompt:
INFO: PVXS QSRV2 is loaded, permitted, and ENABLED. Starting iocInit ############################################################################ ## EPICS R7.0.10-github.com/jeonghanlee/EPICS-env ## Rev. R7.0.10-dirty ## Rev. Date Git: 2025-12-15 17:11:22 -0600 ############################################################################ iocRun: All initialization complete 7.0.10 >The banner names the EPICS-env site version.
dirtymarks the base source tree thatmake patchchanged. The prompt7.0.10 >waits for IOC shell commands. The output above is from a terminal; when the IOC output goes to a pipe, the same lines can appear in a different order. -
At the IOC prompt, list the records:
dblThe IOC prints the name of its one record:
tutorial:value
Read and write the PV from a second shell
-
Open a second terminal in the working directory of the lesson and set up the same environment without the summary:
source <install_location>/1.4.0/debian-13/7.0.10/setEpicsEnv.bash disable export EPICS_CA_AUTO_ADDR_LIST=NO EPICS_CA_ADDR_LIST=127.0.0.1 EPICS_CAS_INTF_ADDR_LIST=127.0.0.1 export EPICS_PVA_AUTO_ADDR_LIST=NO EPICS_PVA_ADDR_LIST=127.0.0.1 EPICS_PVAS_INTF_ADDR_LIST=127.0.0.1 -
Read the PV over CA:
caget tutorial:valueThe output is:
tutorial:value 42 -
Read the PV over PVA:
pvget tutorial:valueThe output shows the time stamp and the value:
tutorial:value 2026-09-26 23:16:46.167 42PVA also carries the time stamp that
PINIset. Your output shows the time at which your IOC processed the record. -
Write the value
7over CA:caput tutorial:value 7The output shows the value before and after the write:
Old : tutorial:value 42 New : tutorial:value 7 -
Read the value again:
caget tutorial:valueThe output is:
tutorial:value 7 -
To stop the IOC, type
exitat the IOC prompt in the first terminal.
You built an installed tree from source, set up a shell on it, and exchanged a value with a running IOC over both protocols.
Pages to read after this lesson
- Build and install the environment repeats the build as a task with its verification.
- Set up a shell with the environment lists every variable the environment script sets.
- Run the verification gates checks the tree you built.
- Uninstall and clean removes the tree and the sources.
- The installed tree explains the layout.