Skip to content

1. Programming Basics for Raspberry Pi

This guide introduces the essentials of C++ programming on the Raspberry Pi, from setting up your development environment to compiling and debugging your first programs. By the end of this guide, you'll have a solid foundation for the GPIO programming tutorials that follow.

Why C++ on Raspberry Pi?

C++ is an excellent choice for Raspberry Pi projects that require:

  • High performance: C++ runs 10-100× faster than Python for compute-intensive tasks
  • Direct hardware access: Control GPIO pins, SPI, I2C, and UART at the register level
  • Memory efficiency: Critical when working with models that have limited RAM (Pi Zero: 512MB)
  • Real-time processing: Minimal latency for robotics, audio, and sensor applications
  • Industry relevance: C++ skills transfer directly to embedded systems and professional development

The Raspberry Pi supports modern C++ standards (C++17, C++20) through the GNU C++ compiler (g++).

Setting Up Your Development Environment

Installing the Compiler Toolchain

Update your system and install essential development tools:

sudo apt update
sudo apt install build-essential gdb cmake

This installs:

  • g++ — The GNU C++ compiler
  • gcc — The GNU C compiler (needed for some libraries)
  • make — The build automation tool
  • gdb — The GNU debugger
  • cmake — A cross-platform build system
  • Standard C/C++ headers and libraries

Verifying Your Installation

1
2
3
4
5
6
# Check compiler version
g++ --version
# Expected output: g++ (Debian 12.x.x) 12.x.x or similar

# Check which C++ standards are supported
g++ -v --help 2>&1 | grep -oP 'std=c\+\+\d+'

Choosing a Code Editor

You have several options for writing C++ on Raspberry Pi:

Editor Best For Installation
nano Quick edits, beginners Pre-installed
VS Code (Remote SSH) Full IDE experience from your PC Install on your PC, connect via SSH
Vim/Neovim Power users, terminal workflows sudo apt install vim
Geany Lightweight GUI IDE on Pi Desktop sudo apt install geany
Code::Blocks Traditional C++ IDE sudo apt install codeblocks

Recommended: VS Code Remote SSH

The best development experience is to install Visual Studio Code on your desktop/laptop and connect to your Raspberry Pi via the Remote - SSH extension. You get full IntelliSense, debugging, and a modern editor while the code runs natively on the Pi.

1
2
3
# On the Pi, ensure SSH is enabled:
sudo systemctl enable ssh
sudo systemctl start ssh

Then in VS Code on your PC: Ctrl+Shift+P → "Remote-SSH: Connect to Host" → pi@raspberrypi.local

Creating Your First C++ Program

Create a new file called hello.cpp:

1
2
3
4
5
6
#include <iostream>

int main() {
    std::cout << "Hello, Raspberry Pi!" << std::endl;
    return 0;
}

Compiling and Running

Basic Compilation

g++ -o hello hello.cpp
./hello

You should see the output Hello, Raspberry Pi! displayed in your terminal.

Understanding the Compilation Process

When you run g++ -o hello hello.cpp, the compiler performs four stages:

graph LR A["Source Code<br/>hello.cpp"] --> B["Preprocessor<br/>#include expansion"] B --> C["Compiler<br/>Assembly code"] C --> D["Assembler<br/>Object file (.o)"] D --> E["Linker<br/>Executable (hello)"] style A fill:#ff6b6b,stroke:#333,stroke-width:1px,color:#fff style B fill:#ffa94d,stroke:#333,stroke-width:1px,color:#fff style C fill:#ffd43b,stroke:#333,stroke-width:1px,color:#000 style D fill:#69db7c,stroke:#333,stroke-width:1px,color:#000 style E fill:#4dabf7,stroke:#333,stroke-width:1px,color:#fff

You can see each stage individually:

# Preprocessor only (output goes to hello.i)
g++ -E hello.cpp -o hello.i

# Compile to assembly (output goes to hello.s)
g++ -S hello.cpp -o hello.s

# Compile to object file (output goes to hello.o)
g++ -c hello.cpp -o hello.o

# Link object file to create executable
g++ hello.o -o hello

Important Compiler Flags

Flag Purpose Example
-o name Name the output file g++ -o myapp main.cpp
-std=c++17 Specify C++ standard version g++ -std=c++17 main.cpp
-Wall Enable all common warnings g++ -Wall main.cpp
-Wextra Enable extra warnings g++ -Wextra main.cpp
-Werror Treat warnings as errors g++ -Werror main.cpp
-g Include debug information (for gdb) g++ -g main.cpp
-O2 Optimize for speed g++ -O2 main.cpp
-Os Optimize for size g++ -Os main.cpp
-pthread Enable POSIX threads g++ -pthread main.cpp
-lwiringPi Link against WiringPi library g++ main.cpp -lwiringPi

Recommended flags for development:

g++ -std=c++17 -Wall -Wextra -g -o myapp main.cpp

Recommended flags for release:

g++ -std=c++17 -O2 -o myapp main.cpp

Multi-File Projects

Real projects consist of multiple source files. Here's a typical structure:

1
2
3
4
5
my_project/
├── main.cpp        # Entry point
├── sensor.h        # Sensor class header
├── sensor.cpp      # Sensor class implementation
└── Makefile        # Build instructions

Header File (sensor.h)

#ifndef SENSOR_H
#define SENSOR_H

#include <string>

class Sensor {
public:
    Sensor(const std::string& name, int gpio_pin);
    int read();
    std::string getName() const;
private:
    std::string name_;
    int gpio_pin_;
};

#endif // SENSOR_H

Implementation File (sensor.cpp)

#include "sensor.h"
#include <iostream>
#include <cstdlib>  // for rand()

Sensor::Sensor(const std::string& name, int gpio_pin)
    : name_(name), gpio_pin_(gpio_pin) {}

int Sensor::read() {
    // Simulated sensor reading (replace with actual GPIO read later)
    int value = std::rand() % 100;
    std::cout << name_ << " on GPIO " << gpio_pin_
              << " reads: " << value << std::endl;
    return value;
}

std::string Sensor::getName() const {
    return name_;
}

Main File (main.cpp)

#include <iostream>
#include "sensor.h"

int main() {
    Sensor temp("Temperature", 4);
    Sensor humidity("Humidity", 17);

    std::cout << "=== Sensor Readings ===" << std::endl;
    temp.read();
    humidity.read();

    return 0;
}

Compiling Multi-File Projects

1
2
3
4
5
6
7
8
9
# Compile each source file to object file
g++ -std=c++17 -Wall -c main.cpp -o main.o
g++ -std=c++17 -Wall -c sensor.cpp -o sensor.o

# Link object files into executable
g++ main.o sensor.o -o sensor_app

# Run
./sensor_app

Build Automation with Makefile

Typing compile commands manually gets tedious. A Makefile automates the process:

# Compiler settings
CXX      = g++
CXXFLAGS = -std=c++17 -Wall -Wextra -g
TARGET   = sensor_app

# Source files
SRCS = main.cpp sensor.cpp
OBJS = $(SRCS:.cpp=.o)

# Default target
all: $(TARGET)

# Link
$(TARGET): $(OBJS)
    $(CXX) $(OBJS) -o $(TARGET)

# Compile .cpp to .o
%.o: %.cpp
    $(CXX) $(CXXFLAGS) -c $< -o $@

# Clean build artifacts
clean:
    rm -f $(OBJS) $(TARGET)

# Rebuild from scratch
rebuild: clean all

.PHONY: all clean rebuild

Makefile indentation

Makefiles require tabs, not spaces for indentation before commands. If you get *** missing separator. Stop., check that you're using actual tab characters.

Now you can build with a single command:

1
2
3
make          # Build the project
make clean    # Remove build artifacts
make rebuild  # Clean and rebuild

For more complex projects, CMake is the standard:

# CMakeLists.txt
cmake_minimum_required(VERSION 3.16)
project(SensorApp LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

add_executable(sensor_app
    main.cpp
    sensor.cpp
)

Build with CMake:

1
2
3
4
mkdir build && cd build
cmake ..
make
./sensor_app

Debugging with GDB

When your program crashes or produces wrong results, gdb helps you find the problem:

1
2
3
4
5
# Compile with debug info
g++ -std=c++17 -g -o myapp main.cpp sensor.cpp

# Start the debugger
gdb ./myapp

Essential GDB commands:

Command Shortcut Action
run r Start the program
break main b main Set breakpoint at main()
break sensor.cpp:15 b sensor.cpp:15 Set breakpoint at line 15
next n Execute next line (step over)
step s Step into function
continue c Continue to next breakpoint
print variable p variable Print variable value
backtrace bt Show call stack
quit q Exit GDB

Example debugging session:

$ gdb ./sensor_app
(gdb) break main
Breakpoint 1 at 0x1234: file main.cpp, line 5.
(gdb) run
Starting program: ./sensor_app

Breakpoint 1, main () at main.cpp:5
5       Sensor temp("Temperature", 4);
(gdb) next
6       Sensor humidity("Humidity", 17);
(gdb) print temp.getName()
$1 = "Temperature"
(gdb) continue
=== Sensor Readings ===
Temperature on GPIO 4 reads: 42
Humidity on GPIO 17 reads: 67
[Inferior 1 exited normally]
(gdb) quit

GPIO Libraries for Raspberry Pi

When you're ready to control real hardware, you'll need a GPIO library. Here's a comparison:

Library Language Status Best For
lgpio C/C++ ✅ Active (recommended) Pi 5 and all modern models
pigpio C/C++ ✅ Active Precise timing, PWM, servo control
WiringPi C/C++ ⚠️ Discontinued (fork available) Legacy projects
gpiod C/C++ ✅ Active Kernel-level GPIO via libgpiod
RPi.GPIO Python ⚠️ No Pi 5 support Simple Python scripts
gpiozero Python ✅ Active Beginner-friendly Python
1
2
3
sudo apt install python3-lgpio  # Python bindings
# For C/C++ headers:
sudo apt install liblgpio-dev

Installing pigpio

1
2
3
sudo apt install pigpio python3-pigpio
sudo systemctl enable pigpiod
sudo systemctl start pigpiod

Common Pitfalls for Beginners

Watch out for these common mistakes

  1. Forgetting to save before compiling — Your changes won't be included in the build
  2. Missing semicolons — Every statement in C++ must end with ;
  3. Using = instead of === assigns, == compares
  4. Not checking return values — Always check if file operations and hardware access succeed
  5. Running GPIO programs without sudo — Most GPIO libraries need root access:
    sudo ./my_gpio_program
    
  6. Compiling with the wrong standard — Use -std=c++17 for modern features

Next Steps

You now have a solid C++ development environment on your Raspberry Pi. Continue to the hands-on tutorials:

➡️ Controlling an LED with GPIO — Your first hardware project: blinking an LED with C++ code.