This repository demonstrates four development workflows for the National Instruments cRIO platform using Python. Each workflow builds on the previous, progressing from pure local simulation through hybrid FPGA/DAQmx control to fully remote operation with live data streaming.
| # | DAQmx location | FPGA control | Where Python runs |
|---|---|---|---|
| 1 | Dev machine (simulated cDAQ) → real cRIO | None | Dev machine, then cRIO |
| 2 | cRIO (via SSH) | LabVIEW interactive VI | cRIO (SSH) |
| 3 | cRIO (via SSH) | Python nifpga (SSH) |
cRIO (SSH) |
| 4 | Remote machine → cRIO via gRPC | Python nifpga (SSH) |
Split: FPGA on cRIO, DAQmx on dev machine |
ni9220acquisition.py # Workflow 1 – DAQmx demo (dev machine with simulated cDAQ)
.bashrc # cRIO shell encoding fix (copy to /home/admin/)
PythonFPGAControl/
analog-input.py # Workflow 4 – gRPC DAQmx client (runs on dev machine)
stubs/ # Generated gRPC/protobuf stubs (nidaqmx, nidevice, etc.)
source/
cRIO Code/ # Deploy all files here to /home/admin/PythonFPGAControl/ on cRIO
ni9220acquisition.py # Workflow 1 – cRIO variant (physical channel default)
ai.py # Workflow 2/3 – cRIO-local DAQmx with termchart plotting
fpga_interface_sine-pwm.py # Workflow 3 – interactive nifpga CLI (sine-pwm.lvbitx)
sine-pwm.lvbitx # FPGA bitfile for Workflow 3
grpc-device-server # NI gRPC Device Server binary (Workflow 4)
LabVIEW Project/
fpga_interface.py # Workflow 2 – FPGA offset-control CLI (criosineshift bitfile)
fpga_interface_pwm-sine.py # Workflow 3 – alternate nifpga CLI with input validation
cRIO Sine Shift.lvproj # LabVIEW project for the sine-shift FPGA VI
FPGA Bitfiles/ # Compiled .lvbitx bitfiles for LabVIEW project VIs
gRPC Client/Client/
analog-input.py # Workflow 4 – alternate gRPC client copy
stubs/ # Protobuf stubs co-located with the alternate client
Reference Material/ # NI 9381 example LabVIEW projects and bitfiles
Goal: Write and validate DAQmx acquisition code on a dev machine using a simulated cDAQ chassis, then deploy the same script unchanged to a cRIO with real C-Series hardware.
- Open NI MAX and create a simulated cDAQ chassis (e.g.
NIcDAQ-9177) with an NI 9220 module in Slot 1. - Run
ni9220acquisition.pyfrom the repo root. The default channelNIcDAQ-9177/Mod1/ai0matches the simulated chassis.
python ni9220acquisition.pyThe script runs a continuous analog input task at 10 kS/s, prints block-level min/max/avg and effective throughput, and stops on q + Enter or Ctrl+C.
- Copy
PythonFPGAControl/source/cRIO Code/ni9220acquisition.pyto the cRIO. Its default channel isMod1/ai0, matching a real cRIO slot layout. - SSH into the cRIO and run:
ssh admin@<crio-hostname>
python /home/admin/PythonFPGAControl/ni9220acquisition.pyAdjust physical_channel in the script if your NI 9220 is in a different slot.
A terminal-plotting variant (ai.py) is also available on the cRIO for visual output via termchart:
python /home/admin/PythonFPGAControl/ai.pyGoal: Run an FPGA VI interactively from LabVIEW while a Python DAQmx task running on the cRIO acquires data. Python is invoked remotely from a dev machine over SSH.
- Open
PythonFPGAControl/source/LabVIEW Project/cRIO Sine Shift.lvprojin LabVIEW. - Deploy and run the FPGA VI interactively on the cRIO target (LabVIEW handles the bitfile download and FPGA session).
- In a separate terminal on your dev machine, SSH into the cRIO and run the Python DAQmx acquisition script:
ssh admin@<crio-hostname>
python /home/admin/PythonFPGAControl/ai.pyWhile LabVIEW holds the FPGA session, you can still adjust FPGA output parameters from Python using fpga_interface.py — but only when LabVIEW is not holding an exclusive FPGA lock. The script accepts a sine-wave offset (0–4) or stop:
python /home/admin/PythonFPGAControl/fpga_interface.py
# Enter: 0, 1, 2, 3, 4, or stopThis targets criosineshift_FPGATarget_NI9381AdvancedIO.lvbitx on RIO0.
Goal: Control the FPGA entirely from Python using the nifpga driver on the cRIO, while simultaneously running a DAQmx acquisition task — all invoked remotely over SSH from a dev machine. No LabVIEW runtime is required.
scp -r "PythonFPGAControl/source/cRIO Code/"* admin@<crio-hostname>:/home/admin/PythonFPGAControl/ssh admin@<crio-hostname>
python /home/admin/PythonFPGAControl/fpga_interface_sine-pwm.pyThe interactive CLI loads sine-pwm.lvbitx on RIO0 and accepts the following commands:
| Command | Effect |
|---|---|
sine |
Switch FPGA output to sine wave mode |
pwm |
Switch FPGA output to PWM mode |
scale X |
Set amplitude scale (0.0–5.0) |
duty X |
Set PWM duty cycle (0.0–1.0) |
stop |
Halt FPGA loop and close session |
Open a second SSH connection to the cRIO and run:
python /home/admin/PythonFPGAControl/ai.pyBoth processes run concurrently on the cRIO. The FPGA generates the signal; DAQmx acquires it.
Goal: Control the FPGA from Python on the cRIO over SSH, while DAQmx acquisition is driven entirely from a dev machine using the NI gRPC Device Server. Acquired data streams back to the dev machine and is displayed in a Matplotlib plot.
- Deploy everything from
PythonFPGAControl/source/cRIO Code/to the cRIO (see Workflow 3 deploy command above). - Start the NI gRPC Device Server on the cRIO:
ssh admin@<crio-hostname>
/home/admin/PythonFPGAControl/grpc-device-serverThe server listens on port 31763 by default.
- In a second SSH session, start FPGA control:
python /home/admin/PythonFPGAControl/fpga_interface_sine-pwm.pyInstall dependencies on the dev machine:
cd PythonFPGAControl/stubs
pip install poetry && poetry install
pip install matplotlib numpyRun the gRPC client:
python PythonFPGAControl/analog-input.py [server_ip] [server_port] [physical_channel]
# Example:
python PythonFPGAControl/analog-input.py crio-9049 31763 Mod1/ai0The client creates a finite analog input task on the cRIO (1000 samples at 1 kS/s), reads the data over gRPC, and displays it in a Matplotlib window — all without NI-DAQmx installed on the dev machine.
- Python 3.9–3.11 (
<3.12required by gRPC stubs package) nidaqmx— NI-DAQmx Python API (Workflow 1 dev/simulation only)grpcio >= 1.49.1,numpy,matplotlib— Workflow 4 gRPC client- NI MAX — for creating simulated cDAQ chassis (Workflow 1)
- LabVIEW with FPGA module — for building/deploying
.lvbitxbitfiles and running interactive VI (Workflow 2)
nidaqmx— NI-DAQmx Python APInifpga— NI FPGA Python APItermchart— terminal plotting (required byai.pyonly)- NI cRIO runtime and NI-DAQmx support installed via NI Package Manager
Install gRPC stub dependencies on the dev machine:
cd PythonFPGAControl/stubs
pip install poetry
poetry install- NI MAX (dev machine): create a simulated cDAQ chassis with an NI 9220 in Slot 1 to develop and test Workflow 1 without hardware.
- NI-DAQmx: install on both the dev machine (Workflow 1) and the cRIO.
- NI cRIO Linux RT runtime: install on the target device.
- LabVIEW FPGA module: required to build and deploy
.lvbitxbitfiles and to run the interactive VI in Workflow 2. - cRIO shell encoding: copy
.bashrcfrom the repo root to/home/admin/on the cRIO to setLANG=C.UTF-8and avoid encoding issues in the Linux RT shell.
| Script | Where it runs | Default channel | How to adjust |
|---|---|---|---|
ni9220acquisition.py (repo root) |
Dev machine | NIcDAQ-9177/Mod1/ai0 |
physical_channel variable |
source/cRIO Code/ni9220acquisition.py |
cRIO | Mod1/ai0 |
physical_channel variable |
source/cRIO Code/ai.py |
cRIO | Mod1/ai0 |
add_ai_voltage_chan() call |
PythonFPGAControl/analog-input.py |
Dev machine | Mod1/ai0 |
PHYSICAL_CHANNEL constant or CLI arg |
| FPGA scripts | cRIO | RIO0 |
fpgaTarget variable |
- FPGA scripts hardcode the bitfile path
/home/admin/PythonFPGAControl/<bitfile>.lvbitx. UpdatebitfilePathin the script if your cRIO deploy path differs. - Workflows 2 and 3 are mutually exclusive per FPGA session: only one process can hold the FPGA open at a time. LabVIEW (Workflow 2) and
nifpga(Workflow 3) cannot both control the FPGA simultaneously. - The Workflow 4 gRPC client uses a finite acquisition (1000 samples). Increase
samps_per_chanandnum_samps_per_chaninanalog-input.pyfor longer captures. - Available compiled bitfiles are in
PythonFPGAControl/source/LabVIEW Project/FPGA Bitfiles/. The sine-pwm demo usessine-pwm.lvbitxinsource/cRIO Code/.