This chapter describes several different programming options for the Raspberry Pi (RPi), including scripted and compiled languages. An external LED control program is provided in most of the languages so that you can investigate each language’s structure and syntax. The advantages and disadvantages of each language type are discussed along with example uses. The chapter then focuses on the C/C++ and Python programming languages, describing their principles, and why object-oriented programming (OOP) is appropriate for the development of scalable embedded systems applications. The chapter details how you can interface directly to the Linux kernel using the GNU C library and finishes with a discussion on how the computational performance of Python code can be greatly improved. A single chapter can only scratch the surface on this topic, so this one focuses on physical programming with the RPi.
After completing this chapter, you should hopefully be able to do the following:
- Describe the multitude of issues that would impact on your choice of programming languages to use in building physical-computing applications for the RPi.
- Write basic scripting language program code on the RPi that interfaces to an LED, which is attached to an RPi GPIO.
- Compare and contrast scripting, hybrid, and compiled programming languages, and their application to the RPi.
- Write C code examples that interface to the RPi’s GPIOs.
- Describe the principles of OOP programming, and write C++ classes that provide program structure for physical-computing applications.
- Write C/C++ code that can interface directly to the Linux OS.
- Write C/C++ modules that can be called directly from Python.
Digital Media Resources
Here the digital resources referred to in the chapter web page are provided. There are high-resolution versions of some of the important figures and links to videos, resources and websites that are described in the chapter.
This is the circuit that is used to test interfacing the Raspberry Pi to many different programming languages in this chapter. The circuit can be wired using (a) a FET or (b) a bipolar transistor, as illustrated in these figures. Please click on the figure for a larger version.
Source Code Examples
As described in the book, here are source code examples for flashing an LED that is attached to a GPIO using different languages with markup highlighting. Essentially, each code example is performing the same task. Note: these code examples, and all code on this website, are pulled live from the exploringrpi GitHub repository.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
#!/bin/bash # A small Bash script to turn on and off an LED that is attached to GPIO 4 # using Linux sysfs. Written by Derek Molloy (www.derekmolloy.ie) for the # book Exploring Raspberry PI LED_GPIO=4 # Use a variable -- easy to change GPIO number # An example Bash functions function setLED { # $1 is the first argument that is passed to this function echo $1 >> "/sys/class/gpio/gpio$LED_GPIO/value" } # Start of the program -- start reading from here if [ $# -ne 1 ]; then # if there is not exactly one argument echo "No command was passed. Usage is: bashLED command," echo "where command is one of: setup, on, off, status and close" echo -e " e.g., bashLED setup, followed by bashLED on" exit 2 # error that indicates an invalid number of arguments fi echo "The LED command that was passed is: $1" if [ "$1" == "setup" ]; then echo "Exporting GPIO number $1" echo $LED_GPIO >> "/sys/class/gpio/export" sleep 1 # to ensure gpio has been exported before next step echo "out" >> "/sys/class/gpio/gpio$LED_GPIO/direction" elif [ "$1" == "on" ]; then echo "Turning the LED on" setLED 1 # 1 is received as $1 in the setLED function elif [ "$1" == "off" ]; then echo "Turning the LED off" setLED 0 # 0 is received as $1 in the setLED function elif [ "$1" == "status" ]; then state=$(cat "/sys/class/gpio/gpio$LED_GPIO/value") echo "The LED state is: $state" elif [ "$1" == "close" ]; then echo "Unexporting GPIO number $LED_GPIO" echo $LED_GPIO >> "/sys/class/gpio/unexport" fi |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
#!/usr/bin/lua -- Test Lua program to control an external LED that is attached to GPIO4 -- Written for the book "Exploring Raspberry Pi" by Derek Molloy local LED4_PATH = "/sys/class/gpio/gpio4/" -- gpio4 sysfs path local SYSFS_DIR = "/sys/class/gpio/" -- gpio sysfs path local LED_NUMBER = "4" -- The GPIO used -- Example function to write a value to the GPIO function writeGPIO(directory, filename, value) -- Debug: print(string.format("Opening the file: %s", directory..filename)) file = io.open(directory..filename, "w") file:write(value) file:close() end print("Starting the Lua LED Program") if arg[1]==nil then print("This program requires a command") print(" usage is: lua luaLED.lua command") print("where command is one of setup, on, off, status, or close") do return end end if arg[1]=="on" then print("Turning the LED on") writeGPIO(LED4_PATH, "value", "1") elseif arg[1]=="off" then print("Turning the LED off") writeGPIO(LED4_PATH, "value", "0") elseif arg[1]=="setup" then print("Setting up the LED GPIO") writeGPIO(SYSFS_DIR, "export", LED_NUMBER) os.execute("sleep 0.1") -- to ensure gpio exported by Linux writeGPIO(LED4_PATH, "direction", "out") elseif arg[1]=="close" then print("Closing down the LED GPIO") writeGPIO(SYSFS_DIR, "unexport", LED_NUMBER) elseif arg[1]=="status" then print("Getting the LED status") file = io.open(LED4_PATH.."value", "r") print(string.format("The LED state is %s.", file:read())) file:close() else print("Invalid command!") end print("End of the Lua LED Program") |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
#!/usr/bin/perl # A small Perl script to set up GPIO4 LED to be turned on or off from # Linux console. Written by Derek Molloy (derekmolloy.ie) for the # book Exploring Raspberry Pi. # Next line is required to import the usleep() function. use Time::HiRes qw(usleep); $LED4_PATH = "/sys/class/gpio/gpio4/"; $GPIO_SYSFS = "/sys/class/gpio/"; $GPIO_NUMBER = "4"; $command = $ARGV[0]; # Perl Write to Sysfs function, the path $_[0] is the first argument # the filename is the second argument $_[1] and the value to write # is the third argument $_[2] sub writeSysfs{ # print("Debug: Writing to $_[0]$_[1]\n"); open(FILE, ">" . $_[0] . $_[1] ) or die "Could not open the file, $!"; print FILE $_[2] ; close(FILE); } print "Starting the LED Perl Script\n"; # 0 means that there is exactly one argument if ( $#ARGV != 0 ){ print "There are no arguments. Usage is:\n"; print " bashLED Command, where command is one of\n"; print " setup, on, off, status, or close e.g. bashLED on\n"; exit 2; } print "The LED Command that was passed is: " . $command . "\n"; if ( $command eq "on" ){ print "Turning the LED on\n"; writeSysfs ($LED4_PATH, "value", "1"); } elsif ( $command eq "off" ){ print "Turning the LED off\n"; writeSysfs ($LED4_PATH, "value", "0"); } elsif ( $command eq "setup" ){ print "Exporting the GPIO entry\n"; writeSysfs ($GPIO_SYSFS, "export", $GPIO_NUMBER); usleep(100000); writeSysfs ($LED4_PATH, "direction", "out"); } elsif ( $command eq "close" ){ print "Unexporting the GPIO entry\n"; writeSysfs ($GPIO_SYSFS, "unexport", $GPIO_NUMBER); } elsif ( $command eq "status" ){ open(DATA, "<". $LED4_PATH . "value"); while(<DATA>){ print "$_"; } close(DATA); } print "End of the LED Perl Script\n"; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
#!/usr/bin/python3 # A small Python program to set up GPIO4 as an LED that can be # turned on or off from the Linux console. # Written by Derek Molloy for the book "Exploring Raspberry Pi" import sys from time import sleep LED4_PATH = "/sys/class/gpio/gpio4/" SYSFS_DIR = "/sys/class/gpio/" LED_NUMBER = "4" def writeLED ( filename, value, path=LED4_PATH ): "This function writes the value passed to the file in the path" fo = open( path + filename,"w") fo.write(value) fo.close() return print("Starting the GPIO LED4 Python script") if len(sys.argv)!=2: print("There is an incorrect number of arguments") print(" usage is: pythonLED.py command") print(" where command is one of setup, on, off, status, or close") sys.exit(2) if sys.argv[1]=="on": print("Turning the LED on") writeLED (filename="value", value="1") elif sys.argv[1]=="off": print("Turning the LED off") writeLED (filename="value", value="0") elif sys.argv[1]=="setup": print("Setting up the LED GPIO") writeLED (filename="export", value=LED_NUMBER, path=SYSFS_DIR) sleep(0.1); writeLED (filename="direction", value="out") elif sys.argv[1]=="close": print("Closing down the LED GPIO") writeLED (filename="unexport", value=LED_NUMBER, path=SYSFS_DIR) elif sys.argv[1]=="status": print("Getting the LED state value") fo = open( LED4_PATH + "value", "r") print(fo.read()) fo.close() else: print("Invalid Command!") print("End of Python script") |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
// Simple Node.js example program to set up GPIO LED4 to be turned on or off from // the Linux console. Written by Derek Molloy for the book "Exploring // Raspberry Pi" // Ignore the first two arguments (nodejs and the program name) var myArgs = process.argv.slice(2) var GPIO4_PATH = "/sys/class/gpio/gpio4/" var GPIO_SYSFS = "/sys/class/gpio/" var GPIO_NUMBER = 4 function writeGPIO( filename, value, path ){ var fs = require('fs') try { fs.writeFileSync(path+filename, value) } catch (err) { console.log("The Write Failed to the File: " + path+filename) } } console.log("Starting the RPi LED Node.js Program"); if (myArgs[0]==null){ console.log("There is an incorrect number of arguments."); console.log(" Usage is: nodejs nodejsLED.js command") console.log(" where command is one of: setup, on, off, status, or close.") process.exit(2) //exits with the error code 2 (incorrect usage) } switch (myArgs[0]) { case 'on': console.log("Turning the LED On") writeGPIO("value", "1", GPIO4_PATH) break case 'off': console.log("Turning the LED Off") writeGPIO("value", "0", GPIO4_PATH) break case 'setup': console.log("Exporting the LED GPIO") writeGPIO("export", GPIO_NUMBER, GPIO_SYSFS) // need to delay by 100ms or the GPIO will not be exported when setting the direction setTimeout(function(){ writeGPIO("direction", "out", GPIO4_PATH) },100) break case 'close': console.log("Unexporting the LED GPIO") writeGPIO("unexport", GPIO_NUMBER, GPIO_SYSFS) break case 'status': console.log("Getting the LED Status") fs = require('fs') fs.readFile(GPIO4_PATH+"value", 'utf8', function (err, data) { if (err) { return console.log(err) } console.log(data) }) break default: console.log("Invalid Command") } console.log("End of RPi Node.js script") |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
/** Simple LED Java Example. Written by Derek Molloy (derekmolloy.ie) for the book * Exploring Raspberry Pi */ package exploringRPi; import java.io.*; public class LEDExample { private static String GPIO4_PATH = "/sys/class/gpio/gpio4/"; private static String GPIO_SYSFS = "/sys/class/gpio/"; private static void writeSysfs(String filename, String value, String path){ try{ BufferedWriter bw = new BufferedWriter(new FileWriter (path+filename)); bw.write(value); bw.close(); } catch(IOException e){ System.err.println("Failed to access the RPi Sysfs file: " + filename); } } public static void main(String[] args) { System.out.println("Starting the LED Java Application"); if(args.length!=1) { System.out.println("There is an incorrect number of arguments."); System.out.println(" Correct usage is: LEDExample command"); System.out.println("where command is one of setup, on, off, status, or close"); System.exit(2); } if (args[0].equalsIgnoreCase("On") || args[0].equalsIgnoreCase("Off")){ System.out.println("Turning the LED " + args[0]); writeSysfs("value", args[0].equalsIgnoreCase("On")? "1":"0", GPIO4_PATH); } else if (args[0].equalsIgnoreCase("setup")){ System.out.println("Setting up the LED"); writeSysfs("export", "4", GPIO_SYSFS); try{ Thread.sleep(100); //sleep to ensure that gpio is exported } catch(InterruptedException e){} writeSysfs("direction", "out", GPIO4_PATH); } else if (args[0].equalsIgnoreCase("close")){ System.out.println("Closing down the LED"); writeSysfs("unexport", "4", GPIO_SYSFS); } else if (args[0].equalsIgnoreCase("status")){ try{ BufferedReader br = new BufferedReader(new FileReader(GPIO4_PATH+"value")); String line; while ((line = br.readLine()) != null) { System.out.println(line); } br.close(); } catch(IOException e){ System.err.println("Failed to access the sysfs entry: /value"); } } else { System.out.println("Invalid command"); } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
/** Simple Sysfs LED control program - written by Derek Molloy * simple OOP struture for the Exploring Raspberry Pi * * This program can be used to easliy control multiple LEDS using a class. * This program uses C++11 for the to_string() function and therefore must * be compiled with the -std=c++11 flag. */ #include<iostream> #include<fstream> #include<string> #include<unistd.h> // for the microsecond sleep function using namespace std; #define GPIO "/sys/class/gpio/" #define FLASH_DELAY 50000 // 50 milliseconds class LED{ private: // the following is part of the implementation string gpioPath; // private states int gpioNumber; void writeSysfs(string path, string filename, string value); public: // part of the public interface LED(int gpioNumber); // the constructor -- create the object virtual void turnOn(); virtual void turnOff(); virtual void displayState(); virtual ~LED(); // the destructor -- called automatically }; LED::LED(int gpioNumber){ // constructor implementation this->gpioNumber = gpioNumber; gpioPath = string(GPIO "gpio") + to_string(gpioNumber) + string("/"); writeSysfs(string(GPIO), "export", to_string(gpioNumber)); usleep(100000); // ensure GPIO is exported writeSysfs(gpioPath, "direction", "out"); } // This implementation function is "hidden" outside of the class void LED::writeSysfs(string path, string filename, string value){ ofstream fs; fs.open((path+filename).c_str()); fs << value; fs.close(); } void LED::turnOn(){ writeSysfs(gpioPath, "value", "1"); } void LED::turnOff(){ writeSysfs(gpioPath, "value", "0"); } void LED::displayState(){ ifstream fs; fs.open((gpioPath + "value").c_str()); string line; cout << "The current LED state is "; while(getline(fs,line)) cout << line << endl; fs.close(); } LED::~LED(){ // The destructor unexports the sysfs GPIO entries cout << "Destroying the LED with GPIO number " << gpioNumber << endl; writeSysfs(string(GPIO), "unexport", to_string(gpioNumber)); } int main(int argc, char* argv[]){ // the main function start point cout << "Starting the makeLEDs program" << endl; LED led1(4), led2(17); // create two LED objects cout << "Flashing the LEDs for 5 seconds" << endl; for(int i=0; i<50; i++){ // LEDs will alternate led1.turnOn(); // turn GPIO4 on led2.turnOff(); // turn GPIO17 off usleep(FLASH_DELAY); // sleep for 50ms led1.turnOff(); // turn GPIO4 off led2.turnOn(); // turn GPIO17 on usleep(FLASH_DELAY); // sleep for 50ms } led1.displayState(); // display final GPIO4 state led2.displayState(); // display final GPIO17 state cout << "Finished the makeLEDs program" << endl; return 0; } |
External Resources
- My EE402 Object-oriented Programming Course Notes: ee402.eeng.dcu.ie
Errata
- Page 184, the compiler generates platform-dependent assembler code, not platform-independent assembler code.
Recommended Books on the Content in this Chapter
Hello
Following the text in Ch5.
On trying to run the JavaLED Example on a RPi B+ V2 I found that in order to get it to work I needed to increase the delay in
Line 40: ” Thread.sleep(100); //sleep to ensure that gpio is exported”
as the script would report that
“Failed to access the RPi Sysfs file: direction”
I changed it to Thread.sleep(1000); and the script worked OK.
I presume that it is caused by the slower response of the RPi B+ compared with the RPi3
All the best
Jeff Haddow
Hello. I am far from an expert in C/C++, so you may well have a reason for it, but is there an error in line 68
int main(int argc, char* argv[])
which should be rather
int main(int argc, char *argv[])
This error (if that’s what it is) appears in the book and in the source on github.
Doing it char* argv[] results in a Segmentation error (pointer related kinds of stuff).
Thanks for a great book.
Hi Ken, the position of the * shouldn’t matter in this case as it always binds to the right-hand side (for example, you can also write it as char * argv[]). However, I’m confused with the segmentation error that’s resulting from the change. I’ll have to think about that one. Kind regards, Derek.
In Listing 5-19, should the line “cdef double dx, s” actually be “cdef double dx, sum”?
Making this change halves the execution time.
What does cython do with undeclared variables?
Hello,
As a new user of git, can I use git from the command prompt on the rpi to clone a project. I tried the following without success:
pi@raspberrypi:~ $ git clone https://github.com/exploringrpi/chp05/bashLED/bashLED
Cloning into ‘bashLED’…
remote: Not Found
fatal: repository ‘https://github.com/exploringrpi/chp05/bashLED/bashLED/’ not found
Please advise – thanks,
Chuck
Hi Chuck, You should pull down the entire repository rather than a sub directory of it. You then change to that sub directory on your local machine. The entire repository is fairly small, so it won’t take too long. Kind regards, Derek.
On page 137 the FET wiring example indicates a 1M resistance between Vin (and the text describes why) and ground yet on page 167 the wiring diagram does not. Is this an over site, does it mater either way. Thank you for any feed back.
Page 169 – Downloading the repository : Is it best advised to download the repository to the RPi or the local machine? If the local machine, how then would you copy the files to the RPi?
I ended up putting the repository on the RPi – I’m getting it
Could someone be so kind and set me straight…I built the circuit on page 167 (FET 2222LA) with a twist though. I’m not using the onboard 3.3vdc supply but rather an external 5 volt power supply so the result it a single wire tied to GPIO4. I’m not floating the external power supply as I have the negative terminal tied to the ground terminal on the supply…..
Ok here it is : when the GPIO gets unexported, or after a reboot, or if I change the channel to an input ->>> the durn LED lights up.
How is that possible? Is not the FET Gate totally isolated from the Drain and the Source?
I added a diode to the Gate….did not make a difference.
Thanks a bunch, Mike in Colorado Springs (719-471-3209)
Ok, I get it….On boot, some banks of GPIO channels are pulled up and other down. I measured each : [Pulled Up at boot: 2,3,4,5,6,7,8,14,15,IDSD IDSC] but check it for yourself please. I will choose my channel assignments wisely, flashing LED lights on boot Ok but opening a valve or running a pump not so great. I guess that’s why they make “industrial controls”
Also, I’ve been doing a bit of study and found many folks with on-line instructional videos are driving LED’s directly from the channel pins and not switching with a semiconductor. I’ll stick with the safe mode and use FETs.
Great book Derek….Thanks,
Mike
Hi!
in listing 5-1 which is also in this page on line 22 where we can read:
echo “Exporting GPIO number $1”
I consider it has no sense, it would be more logical to be:
echo “Exporting GPIO number ” $LED_GPIO
in order to be informed of which GPIO is configured
Santi
Hello Dr. Molloy, I followed the example from listing 5-21 and ran the build statement that followed on page 213. When I go to run the test.py python script, I get the following:
pi@raspberrypi:~/chp05/python3_C $ python3 test.py
Traceback (most recent call last):
File “test.py”, line 2, in
import ERPiModule
ImportError: dynamic module does not define init function (PyInit_ERPiModule)
What could be going wrong here?
By the way, whenever I use your module ERPiModule.so, it works fine. The problem is whenever I build the source myself, even when I build the source that I downloaded from your GitHub.
Thanks Ken, you are right. I too had segmentation fault and was banging my head. Suddenly I remembered to look at the website for any errata message, then found your posting. Definitely great book, thank you Derek.