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 |
Python Demo Slides.pdf # Presentation slides
HostCode/ # Code that runs on the dev machine
ni9220acquisition.py # Workflow 1 – DAQmx demo (dev machine with simulated cDAQ)
analog-input.py # Workflow 4 – gRPC DAQmx client
gRPCClient/
analog-input.py # Workflow 4 – gRPC client (co-located with stubs)
stubs/ # Generated gRPC/protobuf stubs (nidaqmx, nidevice, etc.)
cRIOCode/ # Deploy all files here to /home/admin/cRIOCode/ 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
termchart.py # Terminal plotting utility (bundled, used by ai.py)
grpc-device-server # NI gRPC Device Server binary (Workflow 4)
LabVIEW FPGA Project/ # LabVIEW project and associated Python scripts
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
Install Python on the cRIO (one time):
ssh admin@<crio-hostname>
sudo opkg install python3Deploy the cRIO code:
scp -r cRIOCode/ admin@<crio-hostname>:/home/admin/That's all that's needed to run Workflows 1–3 on the cRIO. Workflow 4 additionally requires the grpc-device-server binary, which is included in cRIOCode/.
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 theHostCodedirectory. The default channelNIcDAQ-9177/Mod1/ai0matches the simulated chassis.
python HostCode/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.
ssh admin@<crio-hostname>
python /home/admin/cRIOCode/ni9220acquisition.pyAdjust physical_channel in the script if your NI 9220 is in a different slot. Its default channel is Mod1/ai0, matching a real cRIO slot layout.
A terminal-plotting variant (ai.py) is also available on the cRIO for visual output:
python /home/admin/cRIOCode/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
LabVIEW FPGA 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/cRIOCode/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/LabVIEW FPGA Project/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.
ssh admin@<crio-hostname>
python /home/admin/cRIOCode/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/cRIOCode/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.
- Start the NI gRPC Device Server on the cRIO:
ssh admin@<crio-hostname>
/home/admin/cRIOCode/grpc-device-serverThe server listens on port 31763 by default.
- In a second SSH session, start FPGA control:
python /home/admin/cRIOCode/fpga_interface_sine-pwm.pyInstall dependencies on the dev machine:
cd HostCode/gRPCClient/stubs
pip install poetry && poetry install
pip install matplotlib numpyRun the gRPC client:
python HostCode/analog-input.py [server_ip] [server_port] [physical_channel]
# Example:
python HostCode/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)
- Python 3 (
sudo opkg install python3) - NI cRIO runtime with NI-DAQmx and NI-FPGA support installed via NI Package Manager
| Script | Where it runs | Default channel | How to adjust |
|---|---|---|---|
HostCode/ni9220acquisition.py |
Dev machine | NIcDAQ-9177/Mod1/ai0 |
physical_channel variable |
cRIOCode/ni9220acquisition.py |
cRIO | Mod1/ai0 |
physical_channel variable |
cRIOCode/ai.py |
cRIO | Mod1/ai0 |
add_ai_voltage_chan() call |
HostCode/analog-input.py |
Dev machine | Mod1/ai0 |
PHYSICAL_CHANNEL constant or CLI arg |
| FPGA scripts | cRIO | RIO0 |
fpgaTarget variable |
- FPGA bitfile paths are resolved relative to the script location using
os.path, so they work regardless of where the files are deployed on the cRIO. - 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. - Compiled bitfiles for the LabVIEW project are in
LabVIEW FPGA Project/FPGA Bitfiles/. The sine-pwm demo usessine-pwm.lvbitxincRIOCode/.