Q: How many software engineers does it take to change a light bulb ? A: It can't be done; it's a hardware problem.
Introduction
LabWindows/CVI is a great package from National Instruments. Its main purpose is to be a powerful tool for writing data acquisition programs, but it's really a multipurpose 32 bits C compiler, doubled by a code generator and some of the most powerful C libraries ever seen on a PC (easy to use and very complete User Interface, signal/maths and more with Advanced Analysis, Data Acquisition, VXI, GPIB 488, VISA, TCP, DDE, formatting and I/O, Utilities and finally ANSI C). And that was when I originally wrote this page 20 years ago, nowadays there's a lot more.
Note: as of 2015, I offer an introductory/intermediate training course to LabWindows/CVI programming, of a duration of 3 or 4 days for up to 4 programmers with C experience. I can do this course in english, french or italian, and you can subscribe through Iris Training or contact me. The outline of the course is here.
Since 2019, most of the code is available on my gitlab page.
If you are looking for my old ReadSavePng.fp and ReadSaveJpeg.fp, I removed them from the list thinking that they were obsolete due to the fact that you can now use the GetBitmapFromFile(), SaveBitmapToJPEGFile() and SaveBitmapToPNGFile() functions, yet there are a few additional functions in my libraries that are still useful; so just in case you can find the libraries in this gitlab project, just pull the appropriate files.
Showing all attributes
Sometimes there's something wrong with a control. It doesn't behave as expected. Maybe you changed an attribute with the wrong panel/control association. And the cause is hard to pinpoint since it can happen in a totally unrelated piece of code. Unless NI makes a SetBreakOnAttributeChange function, we are stuck with looking at the properties of the control. But we can't do that from the debugger, it needs to be done from the code itself. And to further complicate it, every type of control has different attributes.
So I wrote this little module to dump all the valid attributes off a panel or control. It just prints them all. Two source files: ShowAttributes.h and >ShowAttributes.c.
Category:
LabWindows/CVI user interface Panel, Debugging, Function
Have you ever noticed that in the user interface editor you can move between panels using Ctrl-shift and the left or right arrow ? Well, here's a little piece of code that allows you to do the same thing in your C programs. In addition it will print or center your panels on request. Note that on Linux the keymaps are a bit different so you may want to verify them.
In this small source code, you'll also find functions to handle default keys (print, screenshot...), replace fonts, auto-size controls, find all controls or panels that fit a given criterium, get the center of a control, place it near a certain point, make a panel or tab active, find if a panel if embedded in a tab, move panels between top-level and tabbed (SDI/MDI)...
Category:
LabWindows/CVI user interface Panel, Navigation, Tricks
OK, so there are several predefined popups, like ConfirmPopup or GeneralPopup that you can call, and they will return the button clicked by the user. But how do I make a custom popup, for instance the classic [Yes|No|YesToAll|NoToAll|Cancel] popup ?
I mulled that one over for a while, until a suggestion from Martin J. Saxon: the trick is to call RunUserInterfaceagain... Here's the sourcecode for the [Yes|No|YesToAll|NoToAll|Cancel] popup and others.
Category:
ANSI C, LabWindows/CVI Function Panel, Popup, Graphics
Here's another example which allows you to change the format, precision and padding of a numeric control.
Note: you may be thinking about doing the same for a graph properties popup, but this is largely done already if you use legends and enable [Interactive Legend].
And yet another example which allows you to change the format, precision and a few other attributes to a graph axis.
And yet another example which allows you to change some cosmetics on a slider popup, including the ramp colors, needle width, etc.
And yet another simpler example which allows you to change the ON and OFF colors of a simple LED or button.
More events
There are many default events in LabWindows, but some are missing. For instance you can drag and drop files from explorer onto a panel only if you call the EnableDragAndDrop function. Something else that is sometimes necessary is shift-click, ctrl-click and alt-click. The code is below. The thing I haven't been able to figure out yet is how to separate and get the coordinates of mouse-down and mouse-up on a canvas, which is necessary for drawing a box for instance.
Category:
ANSI C, Windows, LabWindows/CVI, User Interface
2005/03/06
Original version.
#include <windows.h>
...
int CVICALLBACK cb_View (int panel, int control, int event, void *callbackData, int eventData1, int eventData2) {
BOOL KeyShift, KeyCtrl, KeyAlt;
// can be combined
KeyShift=GetAsyncKeyState(VK_SHIFT)>>8; // >>8 if the key is pressed right now,
KeyCtrl =GetAsyncKeyState(VK_CONTROL)>>8; // &0xFF if it's been pressed since the last call
KeyAlt =GetAsyncKeyState(VK_MENU)>>8;
switch (event) {
case EVENT_LEFT_CLICK:
if (KeyShift) ...;
if (KeyCtrl) ...;
if (KeyAlt) ...;
break;
...
Another trick that has to do with user input is to regroup the keyboard events to the top panel. For instance if you have a child or separate panel that catches a few keyboard event but you want all other keyboard events to go to the main one (useful for instance if the menu bar is only on the main panel), do the following in the child panel callback function:
switch (event) {
case EVENT_KEYPRESS:
switch (eventData1) { // Keyboard events recognized by child panel
case 'm': DoSomething(); return 1; // Act and swallow the event
...
}
// Key not recognized, passed to Main window
SetActivePanel(MainPanel);
FakeKeystroke (eventData1);
return 1; // We swallow the event but fake it again
}
Arbitrary time axis in strip charts
TRecent versions of CVI have added a SetCtrlAttribute (panel, control, ATTR_XFORMAT, VAL_ABSOLUTE_TIME_FORMAT) and also a relative time format. The problem is that the data must be sent to the strip chart precisely 1s apart. Thus you cannot use a timer which lacks the precision. So I wrote this set of functions which writes the dates as text labels on the axis. It should work with an arbitrary number of strip charts at the same time.
It works quite simply: you call TSL_Auto with the expected update rate of your data, and then you call TSL_PlotStripChart to add points to the strip chart. That's about all there is to it, even if the data comes at non-deterministic intervals. It can use any resolution from one second to a year.
Strip charts are a good way to offer real-time display of data but their programming can be a tad tricky. Here's a piece of code that takes a bunch of variable pointers and gives the user the possibility to display what he wants on how many strip charts as he wants. Basically.
It works quite simply: load the GSC panel from GenericStripChartUI.uir, then call GSC_SetProperties, then call GSC_AddVariable as many times as needed. Done.
If you compile the project with the GSC_DEBUG option, you get a full example executable. Also you can compile the file with TimeStripLabel.c or PanelManagement.h for added functionality.
X/Y plots are the main way to display one data versus another and here's a simple panel that will take an arbitrary list of double variables and lets the user chose a pair to display against each others. It will display data for a while and then silently remove the oldest to avoid overflow or overloaded display.
It works quite simply: load the GXY panel from GenericStripChartUI.uir (it's the same file as above, but does no require the use of strip charts), then call GXY_SetProperties, then call GXY_AddVariable as many times as needed. Done.
As of 2007, LabWindows can run as real-time. The setup is fairly complex: you develop your code on a Windows machine, in the usual CVI IDE, and then you push it onto a machine running a real-time minimalist OS called PharLap ETS. Now I've had some trouble getting that thing running. The remote machine (also called real-time target) can be a PC, a PXI box or some other compbination, but for PCs the requirements are actually pretty tight:
At least Pentium 3 or Athlon XP
64Mb memory min
32Gb FAT32 partition max
Specific chipset for the ethernet card (that's actually a though one)
PS/2 plugs for keyboard and mouse (rules out recent Dell machines for instance)
Bootable via USB (rules out a lot of older system, unless you update the Bios)
Floppy drive (rules out a lot of recent laptops)
Now NI gives you several ways to configure the target. I'll speak first about the OS installation, then about the network config:
USB Boot
If you can boot from USB, go in Measurement & Automation Explorer (MAX), in [Tools][RT Disk Utilities] and create a Desktop PC utility USB drive. You can do several different things when booting from this drive: run the real-time OS, install it on the drive, test to see if the hardware is compatible with the RTOS... Below I'll detail the phases of a floppy install, but it applies to a USB install as well.
Floppy test
In MAX, create a Desktop PC Boot disk. Boot your remote system with it. It should finish with a message similar to "Initializing network... Device 1 - MAC address:... - 0.0.0.0 (primary) System state: unconfigured". If you get an immediate lockup, the processor is probably not compatible. If you get an ETS Loader.ERR, make sure that you floppy drive is working properly (they are often full of dust or the floppies are so old as to be useless). You get another kind of message if your network card is not compatible.
Hard drive preparation
You need an empty disk, formated in FAT32, without MBR. Easier said than done. For instance departitionning an existing disk with [Start][Settings][Control Panel][Administrative Tools][Computer Management][Disk Management] won't work if you have grub or somesuch as a boot loader. Here's a clean way to do it:
If you have a non-Windows bootloader (Lilo, Grub...), boot the UBCD on your target and launch [Hard disk tools][Wiping tools], choose one of the wiping tools, run it, quit and reboot... Or install Windows just for the pleasure of wiping it !
Boot the UBCD on your target and launch [Filesystem tools][Partition Tools][Free FDisk], delete all partitions [3], create a primary DOS partition (32Gb max), set it as active, quit and reboot.
Boot the UBCD on your target and launch [DOS boot disks][FreeDOS boot disk], type FORMAT C: /S, reboot. Don't worry that it cannot copy Kernel.sys. The /S is for the boot record.
Boot with the PC Boot Disk Install floppy. Install the OS to the hard drive and reboot to the hard drive. You should get the message "Initializing network... Device 1 - MAC address:... - 0.0.0.0 (primary) System state: unconfigured"
Network configuration
NI documentation seems to imply that the network configuration happens by magic: the remote boots with the (non-possible) IP address 0.0.0.0, is seen from the master PC, and you configure and run it from there. Not quite. We have a tightly secured network and there was no way I could see an IP of 0.0.0.0. There's no information in the NI documentation as to what kind of broadcast was happening, so we could try to allow it at the firewall level or somesuch.
If you have 2 network cards into your master PC, connect the two of them with an inverted network cable and work from there.
Otherwise add a hub or switch between you master PC, the remote and your network. This will work, but you can't easily have the remote really far from the master...
Once you got one of the above working, go in MAX, [Remote Systems], find your system, set its IP, [Apply], reboot it.
CVI specific config
Go back to MAX, [Remote Systems], expand your target, [Software][Add/Remove software] (the button is above), select [LabWindows/CVI Runtime Engine for RT] and [LabWindows/CVI Network Variable], as well as other requirements (VISA...)
Anyway, to make a long story short, I wasn't able to properly run the RTOS on my test laptop: incompatible network card. It's the proper chipset but it needs to be PCI and not MiniPCI. End of the story for now.
LabWindows on Linux
As of version 8.0 (2007), National Instruments provides a version of LabWindows/CVI for Linux. It was updated in 2010. Here are some notes about using it.
RPM
Chose an RPM-based distro: Red Hat, Suse and a few others are in. Gentoo, Ubuntu are out unless you want to play with rpm/deb converters. NI lists a limited set of Linux distribution, but I've used another one with success, still RPM-based (Scientific Linux).
Non RPM ?
If you'd rather use a modern Linux such as Ubuntu, you can try to copy the rpms to a writable directory, then convert them to .deb using: alien --to-deb --scripts *.rpm
CD
The original Linux CD I had contained a Linux runtime engine but no Linux compiler (cvicc). I have no idea what I can do with a runtime without compiler. Note that unlike the free distribution of the runtime on Windows, the distribution of the Linux runtime costs money. I have no idea why there's a difference but it's worth asking NI.
Motif
You need OpenMotif 2.2 for the user interface (quite archaic IMHO): sudo yum install openmotif22.
Visa
In my case I want CVI to run Visa USB drivers. Visa is on the install CD, but can also be downloaded from here.
Kernel
Visa and a few other libraries are in the form of kernel modules, so if you change kernel version, you need to reinstall some of the CVI components. Note that I wasted time on the kernel issue for nothing: on Red Hat, the kernel gets installed in specific ways, either with # up2date kernel-devel or # up2date redhat-rpm-config rpm-build [...]. I used the manual way where you download the tar file from kernel.org, put it in /usr/src, ln, make menuconfig, etc... This does not work with the NI kernel modules. The kernel must be installed in the approved Red Hat way.
Development
The main difference with CVI for Windows is that there's no development environment... So you have to develop on Windows, particularly you have no choice for the UIR. I use a shared SMB folder between a Windows and a linux machine, create the project and the UIR files in the CVI IDE, compile and test on Windows. Then all I have to do is open a shell in the same directory on the Linux side and type cvicc project.prj to compile it. It creates an executable with the same name, but for Linux. Very simple.
Note that the compiler sometimes confuses Windows and Linux intermediate files, so if your compilation isn't huge, it's better to do rm -rf cvibuild* between each compile.
The underlying compiler is actually gcc and you can change the default parameters in the file /usr/local/natinst/cvi2010/bin/compile.ecc, for instance here I do change -g to -ggdb and -c99 to -gnu99 (to get rid of the 'warning : implicit declaration of function ...' message when using POSIX functions):
You can also change COMPLPATH to clang to use the same compiler than on Windows (just install clang).
X-windows
The compilation step runs fine in a text shell, but in order to run the program on Linux from windows, you need X-Windows. The one integrated with cygwin keeps crashing, and I'm having more luck with Xming. But currently I simply develop on a virtualbox WinXP in a directory exported by VirtualBox, and compile on linux. There's a bug when working this way: if you remove some text from a text file, the CVI editor truncates it, but the junk content behind the truncation remains in the file, so the Windows compilation works fine while the Linux one gives errors on extraneous characters which you can't see in the CVI editor. If you close and reopen CVI or if you open the file with a Linux editor, you can see the problem. It's a pain.
Libraries
Some additional NI libraries are installed with the compiler, they are present in /usr/local/vxipnp/linux/bin/) and can be linked to with 'cvicc -lvisa project.prj'.
Standard Linux libs can be used as well, for instance 'cvicc -lusb project.prj' for libusb which I've used with success to develop a USB driver for a custom instrument for both Linux and Windows.
Debuggin'
Use cvicc -debug project.prj and then run it as kdbg ./project or kdbg ./project_dbg (depending on your target setting). Then do [File][Open source], type *.c and [OK] so that the debugger has all your source files ready for stepping into. Very usable if a little 'ancient' compared to the CVI debugger.
Updates
The linux version is a bit behind the Windows version. For instance I've had trouble compiling some functions from toolbox.h, such as GetDiskSize in version 8.0.
Predefined Macros
The only new macro that I can find is _NI_linux_. Of course, all the WIN something macros go away. Careful that the _CVI_ macro which normally gives the version number IS NOT DEFINED.
Bugs
Here are some bugs I've noticed under Linux:
cvicc returns 255 even if the compilation is successful (solved in CVI 2010).
The compiler can't seem to be able to write its objects in cvibuild.AcqEDM if mounted on an SMB share from Windows. Workaround is to run rm -rf cvibuild*
Timer() returns 0 if the timezone is not set.
[Shift][Tab] doesn't work on UIR controls (solved in CVI 2010).
Panel resizing deadloops if you are running from an XWindows remote session. This is tricky to work around.
Also from an XWindows remote session, DisplayPanel or ActivatePanel won't send them on top.
Many even official libraries work incompletely with out much indication on what is supported or not: wintools.fp (d'oh!), toolbox.fp...
The ini file toolbox won't save files properly (solved in CVI 2010)
The keycodes returned as eventData1 in panel callbacks are highly random. Sometimes it's [A], sometimes [Shift][a]... Sometimes unreferenced bit 20 is activated.
Some tricks:
I found a LabWindows/CVI easter egg !!! I did a DAQ_Start acquisition with a wrong gain, but still got data in the resulting array. The data was weird, so I looked at it in hexadecimal... Actually, it's not so much an easter egg as it is a hexspeak magic debug value.