Debugging C/C++ Code on a Raspberry Pi with Visual Studio
So recently I’ve been playing around with Raspberry Pi type Internet of Things (IoT) devices… In my case I’m experimenting with OrangePi Zero and the C.H.I.P. Both of these are way small, way powerful processor headless computers. I’m trying to do fun and interesting things using these as control devices for special input/output. One of the things I’m interested in is ultra low latency… that is, an absolute minimal amount of time between user input and computer output.
I found an interesting article comparing the use of different programming languages to control things on a Raspberry Pi. Basically the guy hooks up an oscilloscope to and I/O pin, and then turns the pin on and off via different programs / shell commands. Its clear from that test, if you want to control things quickly, go to C for the win. So C it is.
I started playing around with code. I started playing around with different hardware.
First go was with the Orange Pi Zero, with Armbian Linux Server Image (Debian Jessie Legacy v 3.4.113 ) First test was to be using simple General Purpose Input/Output (GPIO) to blink some LED’s. It turns out the Orange Pi is a little bit off standard. To get the GPIO to work, we need to use a modified WiringPi library, courtesy of Github and user Zhaolei.
Here’s a photo of the Orange Pi Zero, an add-on shield with extra USB connections, an audio jack, a microphone and an infrared receiver. I also made a couple of LED jumpers for easy blinky I/O testing. In my case I wanted to use the on board microphone and audio jack… they work pretty well.
Let’s talk a little bit about software. With this device, I always planned on a headless installation. Generally my only contact would be thru SSH, either from desktop / laptop (Git Bash Command Line Interpreter) or mobile phone (with either the Termius or WebSSH app.) Coding consists of me writing original in Atom.io, then copying the code and pasting it into .c files via the nano editor. Compilation and shell run commands are run via the SSH tool.
I ALWAYS want a backup copy of the software I’m writing on my laptop/desktop. The only downside is there is no great way to delete a large block of code with nano. Frankly its easier to delete the file and copy/paste new again.
- Setup the remote device. Probably best to see that things are updated, via
$ sudo apt-get updatefollowed by
$ sudo apt-get upgrade.
- Verify Git is installed on the remote device. If not there, add it with
$ sudo apt-get install git-core
- Install the WiringOp (Orange Pi) library, via
$ git clone https://github.com/zhaolei/WiringOP.git -b h3
- Compile that library on the Orange Pi via
$ cd WiringOP
$ chmod +x ./build
$ sudo ./build
- Create a test file at the user directory (~/) via
$ nano GPIO.cCopy, paste the code from Atom.io. Exit nano, save the file.
- Compile the test file…
$ gcc GPIO.c -o GPIO -lwiringPiThe gcc -lwiringPi command links content from the wiringOP library.
- Finally run the compiled file via
$ ./GPIO. If you did this right, you should see some blinking LED’s. Yipee.
So far, so good. Now its about this time, I’m realizing that I will need to be writing some pretty involved programs. And one thing I don’t have is the ability to debug my code interactively in a convenient method. Its about this time in my research that I stumble over this posting, Visual C++ for Linux Development Wow. No, really… Wow. You can use Visual Studio to manage code, keep copy on your laptop/desktop computer, push code to remote device, AND run code in debugger mode. Way cool. The system uses GDB (the GNU DeBugger) to manage the process remotely.
Now at this point, I spent a heck of a long time trying to understand how Visual C++ for Linux development actually worked. I had more than a few problems, and I couldn’t tell if my troubles were based on my custom libraries or my Visual Studio setup. The answer here was to go back to basics, run the thing one step at a time and see that everything worked well. Now my first off-standard was my choice of the Orange Pi Zero. For a Visual Studio proveout, I reverted back to basics, and used a Raspberry Pi Model B. I set up a clean install.
- Clear SD card via SDFormatter tool (Format type = Full, Format size adjustment = On). I used a 16Gb Samsung Evo MicroSD card in holder.
- Download Raspbian Jessie Lite Minimal Image from raspberrypi.org
- Unzip it. Push OS to SD card via Etcher.io
- Update/Upgrade the OS. Add Git (same as above).
- Add the WiringPi library to the remote device per these installation instructions.
$ git clone git://git.drogon.net/wiringPi
$ cd ~/wiringPi
- You can verify the install via
$ gpio readallwhich creates a handy pinout identification map.
At this point its time to start up Microsoft Visual Studio. I’m using Visual Studio Community 2015, version 14.0.25431.01 with Update 3.
- You are going to want to download and install the Visual C++ for Linux Development extension.
- Install a few tools on the remote device
$ sudo apt-get install openssh-server g++ gdb gdbserver
- Add a few LED’s to your Raspberry Pi. I added one LED to wiringPi Pin #0 and another to wPi pin #1.
- For me, this added one LED to actual pin 11, GND to pin 9 and one LED to pin 12, GND to pin 6. Verify that you have the LED’s oriented in the correct direction.
- Create a new Project. Select Templates –> Visual C++ –> Cross Platform –> Linux
- For this quick test, select ‘Blink (Raspberry)’. Accept the defaults, with one exception. Give the file name a .c suffix (and not a .cpp suffix)
- You will have to set up the program as a ARM processor program, with a pull down selection in the top menu bar.
- At some point you will have to add login credentials, via (Top Menu) Tools –> Options –> Cross Platform –> Connection Manager.
- You can observe output via (Top Menu) Debug –> Linux Console.
- When you click “Remote GDB Debugger” Visual Studio performs the compilation and execution processes.
Visual Studio creates the following files on the remote device (in this case, my Raspberry Pi). Project = Blink, code = main.c
And that Blink.out file, it is fully executable via ssh and
$ ./Blink.out . Note that the WiringPi library is located elsewhere on the remote linux device. If you inspect the sample code carefully, you will note two things.
- Right click on the Blink project in the Solution Explorer. Choose Properties –> Linker –> Input. In the block entitled “Library Dependencies” you will note ‘wiringPi’ This is the command line that tells the system to look for that library on the remote device. The files needed are actually located at /usr/local/lib (normally xxx.so files). Note: there is one thing here I wasn’t very happy about. What if you’ve neglected to compile the library files correctly? If you do that, you get an error message “fatal error: wiringPi.h: No such file or directory”. Wait, what? For a missing file on a remote device, that error message seems to be lacking, and probably should be improved. It’s not immediately obvious that the error is for code content on the remote device, instead, you are wondering what you did wrong on the desktop/laptop machine. My recommendation is “fatal error: wiringPi.h: No such file or directory at remote Linux device.” or words to that effect. The folks at Microsoft seem to agree, and as a result of my email to them, they’ve added this to their open issues list.
- The other thing of note on the Blink project is all the notes involving
// LED Pin - wiringPi pin 0 is BCM_GPIO 17.So in the history of Arduino and Raspberry Pi’s there have been a whole lot of implementations of GPIO pin numbering. The whole WiringPi GPIO thing gives you the chance to custom define different pin schema’s. I will admit for most of us the whole thing is confusing. In this example, its very easy to get pin #0 to function. Starting from the code as written, its way difficult to get pin #1 to function. In this case BCM_GPIO pin #17 = WiringPi pin #0 = physical Pin #11, but what pin is used for WiringPi Pin #1? Hint, it you use the mating BCM_GPIO pin (#18) that is a total fail. Why? Because you have to declare each BCM_GPIO pin special via the Property Pages –> Build Events –> Remote Post-Build Event command. Yes it is important to understand that process, but not, Not, NOT for a beginner exercise. I wasted a good portion of time trying to understand the entire numbering scheme. For 99% of us, its just best to understand our hardware, ssh
$ gpio readallto obtain a pin map for your hardware, and then simply use the WiringPi (wPi) pin numbering.
And heck, to make things easier, I’ll include my beginner approved code sample, RaspberryPiBlink.c (suggest you right click, and save document).
And by the way, just so we don’t forget why we’re using Visual Studio with the Raspberry Pi for programming… with the Gnu Debugger installed we can STEP DEBUG our program on the REMOTE device from within Visual Studio on the laptop/desktop easily. You can see a variable’s content. You can see the order of processing for complex calls. This is way cool, and well worth the tiny bit of extra effort it takes to get everything set up smoothly.