Raspberry Pi Documentation

Raspberry Pi OS


Raspberry Pi OS is a free operating system based on Debian, optimised for the Raspberry Pi hardware, and is the recommended operating system for normal use on a Raspberry Pi. The OS comes with over 35,000 packages: pre-compiled software bundled in a nice format for easy installation on your Raspberry Pi.

Raspberry Pi OS is under active development, with an emphasis on improving the stability and performance of as many Debian packages as possible on Raspberry Pi.

Updating and upgrading Raspberry Pi OS

It’s important to keep your Raspberry Pi up to date. The first and probably the most important reason is security. A device running Raspberry Pi OS contains millions of lines of code that you rely on. Over time, these millions of lines of code will expose well-known vulnerabilities, which are documented in publicly available databases, meaning that they are easy to exploit. The only way to mitigate these exploits as a user of Raspberry Pi OS is to keep your software up to date, as the upstream repositories track CVEs closely and try to mitigate them quickly.

The second reason, related to the first, is that the software you are running on your device most certainly contains bugs. Some bugs are CVEs, but bugs could also be affecting the desired functionality without being related to security. By keeping your software up to date, you are lowering the chances of hitting these bugs.

Using APT

The easiest way to manage installing, upgrading, and removing software is using APT (Advanced Packaging Tool) from Debian. To update software in Raspberry Pi OS, you can use the apt tool from a Terminal window.

Keeping your operating system up-to-date

APT keeps a list of software sources on your Raspberry Pi in a file at /etc/apt/sources.list. Before installing software, you should update your package list with apt update. Open a Terminal window and type:

sudo apt update

Next, upgrade all your installed packages to their latest versions with the following command:

sudo apt full-upgrade

Note that full-upgrade is used in preference to a simple upgrade, as it also picks up any dependency changes that may have been made.

Generally speaking, doing this regularly will keep your installation up to date for the particular major Raspberry Pi OS release you are using (e.g. Bookworm). It will not update from one major release to another (e.g. from Buster to Bullseye, or from Bullseye to Bookworm).

However, there are occasional changes made in the Raspberry Pi OS image that will require manual intervention, for example a newly introduced package. These are not installed with an upgrade, as this command only updates the packages you already have installed.

The kernel and firmware are installed as a Debian package, and so will also get updates when using the procedure above. These packages are updated infrequently and after extensive testing.

If moving an existing SD card to a new Raspberry Pi model (for example the Raspberry Pi Zero 2 W), you may also need to update the kernel and the firmware first, using the instructions above.

Running out of space

When running sudo apt full-upgrade, it will show how much data will be downloaded, and how much space it will take up on the SD card. It’s worth checking with df -h that you have enough free disk space, as unfortunately apt will not do this for you. Also be aware that downloaded package files (.deb files) are kept in /var/cache/apt/archives. You can remove these in order to free up space with sudo apt clean (sudo apt-get clean in older releases of apt).

Upgrading from previous operating system versions

Upgrading an existing image is sometimes possible, but is not guaranteed to work in every circumstance, and we do not recommend it. If you do wish to try upgrading your operating system version, we strongly suggest making a backup first. We accept no responsibility for the loss of any data during a failed update.

The latest version of Raspberry Pi OS is based on Debian Bookworm. The previous version was based on Debian Bullseye.

Searching for software

You can search the archives for a package with a given keyword using apt-cache search:

apt-cache search locomotive
sl - Correct you if you type `sl' by mistake

You can view more information about a package before installing it with apt-cache show:

apt-cache show sl
Package: sl
Version: 3.03-17
Architecture: armhf
Maintainer: Hiroyuki Yamamoto <[email protected]>
Installed-Size: 114
Depends: libc6 (>= 2.4), libncurses5 (>= 5.5-5~), libtinfo5
Homepage: http://www.tkl.iis.u-tokyo.ac.jp/~toyoda/index_e.html
Priority: optional
Section: games
Filename: pool/main/s/sl/sl_3.03-17_armhf.deb
Size: 26246
SHA256: 42dea9d7c618af8fe9f3c810b3d551102832bf217a5bcdba310f119f62117dfb
SHA1: b08039acccecd721fc3e6faf264fe59e56118e74
MD5sum: 450b21cc998dc9026313f72b4bd9807b
Description: Correct you if you type `sl' by mistake
 Sl is a program that can display animations aimed to correct you
 if you type 'sl' by mistake.
 SL stands for Steam Locomotive.

Installing a package with APT

sudo apt install tree

Typing this command should tell the user how much disk space the package will take up, and asks for confirmation of the package installation. Entering Y (or just pressing Enter, as yes is the default action) will allow the installation to occur. This can be bypassed by adding the -y flag to the command:

sudo apt install tree -y

Installing this package makes tree available for the user.

Uninstalling a package with APT

You can uninstall a package with apt remove:

sudo apt remove tree

The user is prompted to confirm the removal. Again, the -y flag will auto-confirm.

You can also choose to completely remove the package and its associated configuration files with apt purge:

sudo apt purge tree

Using rpi-update

Updating Raspberry Pi firmware

In some circumstances it may be necessary to update the VideoCore firmware in a Raspberry Pi operating system (OS) image without going through the normal upgrade process. This white paper documents how to use the normal upgrade process, and also gives information on how to bypass the standard update process if it is not suitable.

rpi-update is a command line application that will update your Raspberry Pi OS kernel and VideoCore firmware to the latest pre-release versions.

Pre-release versions of software are not guaranteed to work. You should not use rpi-update on any system unless recommended to do so by a Raspberry Pi engineer. It may leave your system unreliable or even completely broken. It should not be used as part of any regular update process.

The rpi-update script was originally written by Hexxeh, but is now supported by Raspberry Pi engineers. The script source is in the rpi-update repository.

What it does

rpi-update will download the latest pre-release version of the Linux kernel, its matching modules, device tree files, along with the latest versions of the VideoCore firmware. It will then install these files to relevant locations on the SD card, overwriting any previous versions.

All the source data used by rpi-update comes from the rpi-firmware repository. This repository simply contains a subset of the data from the official firmware repository, as not all the data from that repo is required.

Running rpi-update

If you are sure that you need to use rpi-update, it is advisable to take a backup of your current system first as running rpi-update could result in a non-booting system.

rpi-update needs to be run as root. Once the update is complete you will need to reboot.

sudo rpi-update
sudo reboot

It has a number of options documented in the rpi-update repository.

How to get back to safety

If you have done an rpi-update and things are not working as you wish but your Raspberry Pi is still bootable, you can return to the stable release using:

sudo apt-get update
sudo apt install --reinstall raspi-firmware
If your device still runs Raspberry Pi OS Bullseye, use the following command to reinstall raspberrypi-kernel instead: sudo apt install --reinstall libraspberrypi0 libraspberrypi-{bin,dev,doc} raspberrypi-{kernel,bootloader}.

You will need to reboot your Raspberry Pi for these changes to take effect.

Playing audio and video

The simplest way of playing audio and video on Raspberry Pi is to use the pre-installed VLC application. This is hardware accelerated, and can play back many popular audio and video file formats. More extensive information can be found at https://www.videolan.org/

The VLC application

To play an audio or video file in the Raspberry Pi OS desktop environment, double-click on the file in the file-manager, and VLC will automatically launch and play the file. Alternatively, you can launch "VLC Media Player" from the "Sound & Video" menu, and then select "Media" → "Open File…​" and navigate to the file you want to play. By default, the audio is sent over the HDMI link; if you instead want to output the audio over the headphone jack, right-click on the speaker icon in the top-right corner of the screen, and select "AV Jack".

For more fine-grained control, you can also launch VLC from the command line. For the examples below, we used a short clip from Big Buck Bunny.

wget --trust-server-names http://rptl.io/big-buck-bunny
vlc big-buck-bunny-1080p.mp4

To prevent the VLC GUI staying open after your file has finished playing, you can add the --play-and-exit flag:

vlc --play-and-exit big-buck-bunny-1080p.mp4

You can also add the --fullscreen flag to force a video into fullscreen mode. This can also lead to smoother playback in some circumstances.

vlc --play-and-exit --fullscreen big-buck-bunny-1080p.mp4

If you use cvlc instead of vlc with any of these commands, then the VLC GUI won’t be shown:

cvlc --play-and-exit big-buck-bunny-1080p.mp4

If you have a raw H264 stream, for example as captured from the Raspberry Pi Camera Module, you will get much smoother playback in VLC by encapsulating the stream inside a container format. This can be done using ffmpeg. For example, this command converts video.h264 to a containerised video.mp4 at 30fps:

ffmpeg -r 30 -i video.h264 -c:v copy video.mp4

Playing audio and video on Raspberry Pi OS Lite

Begin by installing the necessary VLC components:

sudo apt install --no-install-recommends vlc-bin vlc-plugin-base

And then download and play an example audio file:

wget --trust-server-names http://rptl.io/startup-music
cvlc --play-and-exit computer-startup-music.mp3

To force the audio output to a particular device, you should use:

cvlc --play-and-exit -A alsa --alsa-audio-device <alsa-device> computer-startup-music.mp3

…​replacing <alsa-device> with one of the following options:

ALSA device Description


The headphone jack


The HDMI output on a Raspberry Pi Zero, or Raspberry Pi Model 1, 2 or 3


The HDMI0 output on a Raspberry Pi 4 or 400


The HDMI1 output on a Raspberry Pi 4 or 400

Use the following command to get a list of all ALSA devices on your Raspberry Pi:

aplay -L | grep sysdefault

To force the video output to a particular device, you need to use:

cvlc --play-and-exit --drm-vout-display <drm-device> big-buck-bunny-1080p.mp4

…​replacing <drm-device> with one of the following options:

DRM device Description


The HDMI output on a Raspberry Pi Zero, or Raspberry Pi Model 1, 2 or 3; or the HDMI0 output on a Raspberry Pi 4 or 400


The HDMI1 output on a Raspberry Pi 4 or 400


The Raspberry Pi Touch Display

Use the following command to get a list of all DRM devices on your Raspberry Pi:

kmsprint | grep Connector

You can combine these options, so to direct the video output to the touchscreen, and the audio output to the headphone jack, you’d use:

cvlc --play-and-exit --fullscreen --drm-vout-display DSI-1 -A alsa --alsa-audio-device sysdefault:CARD=Headphones your_video.mp4

Using a USB webcam

Rather than using the Raspberry Pi camera module, you can use a standard USB webcam to take pictures and video on your Raspberry Pi.

The quality and configurability of the Raspberry Pi Camera Modules is highly superior to a standard USB webcam.

First, install the fswebcam package:

sudo apt install fswebcam

Next, add your username to the video group, otherwise you will see 'permission denied' errors:

sudo usermod -a -G video <username>

To check that the user has been added to the group correctly, use the groups command.

Basic usage

Enter the command fswebcam followed by a filename, and a picture will be taken using the webcam and saved to the filename specified:

fswebcam image.jpg

This command will show the following information:

--- Opening /dev/video0...
Trying source module v4l2...
/dev/video0 opened.
No input was specified, using the first.
Adjusting resolution from 384x288 to 352x288.
--- Capturing frame...
Corrupt JPEG data: 2 extraneous bytes before marker 0xd4
Captured frame in 0.00 seconds.
--- Processing captured image...
Writing JPEG image to 'image.jpg'.
Basic image capture
The small default resolution used, and the presence of a banner showing the timestamp.

The webcam used in this example has a resolution of 1280 x 720. So to specify the resolution you want the image to be taken at, use the -r flag:

fswebcam -r 1280x720 image2.jpg

This command will show the following information:

--- Opening /dev/video0...
Trying source module v4l2...
/dev/video0 opened.
No input was specified, using the first.
--- Capturing frame...
Corrupt JPEG data: 1 extraneous bytes before marker 0xd5
Captured frame in 0.00 seconds.
--- Processing captured image...
Writing JPEG image to 'image2.jpg'.
Full resolution image

A picture is now taken using the full resolution of the webcam, with the banner present.

Removing the banner

Now add the --no-banner flag:

fswebcam -r 1280x720 --no-banner image3.jpg

…​which shows the following information:

--- Opening /dev/video0...
Trying source module v4l2...
/dev/video0 opened.
No input was specified, using the first.
--- Capturing frame...
Corrupt JPEG data: 2 extraneous bytes before marker 0xd6
Captured frame in 0.00 seconds.
--- Processing captured image...
Disabling banner.
Writing JPEG image to 'image3.jpg'.
Full resolution image with no banner

Now the picture is taken at full resolution with no banner.

Automating image capture

You can write a Bash script which takes a picture with the webcam. The script below saves the images in the /home/pi/webcam directory, so create the webcam subdirectory first with:

mkdir webcam

To create a script, open up your editor of choice and write the following example code:


DATE=$(date +"%Y-%m-%d_%H%M")

fswebcam -r 1280x720 --no-banner /home/pi/webcam/$DATE.jpg

This script will take a picture and name the file with a timestamp. Saving the file as webcam.sh, first make the file executable:

chmod +x webcam.sh

Then run with:


…​which would run the commands in the file and give the usual output.

--- Opening /dev/video0...
Trying source module v4l2...
/dev/video0 opened.
No input was specified, using the first.
--- Capturing frame...
Corrupt JPEG data: 2 extraneous bytes before marker 0xd6
Captured frame in 0.00 seconds.
--- Processing captured image...
Disabling banner.
Writing JPEG image to '/home/pi/webcam/2013-06-07_2338.jpg'.

Time-lapse captures

You can use cron to schedule taking a series of pictures at a given interval, such as once a minute, to capture a time-lapse scene.

First open the cron table for editing:

crontab -e

This will either ask which editor you would like to use, or open in your default editor. Once you have the file open in an editor, add the following line to schedule taking a picture every minute (referring to the Bash script from above):

* * * * * /home/pi/webcam.sh 2>&1

Save and exit, and you should see the message:

crontab: installing new crontab

Ensure your script does not save each picture with the same filename. This will overwrite the picture every time.


There are several useful command-line utilities pre-installed in Raspberry Pi OS.


The kmsprint tool can be used to list the display-modes supported by the monitors attached to the Raspberry Pi. Use kmsprint to see details of the monitors connected to the Raspberry Pi, and kmsprint -m to see a list of all the display modes supported by each monitor. You can find source code for the kmsprint utility on Github.


The vcgencmd tool is used to output information from the VideoCore GPU on the Raspberry Pi. You can find source code for the vcgencmd utility on GitHub.

To get a list of all commands supported by vcgencmd, use vcgencmd commands. Some useful commands and their required parameters are listed below.


The vcos command has two useful sub-commands:

  • version displays the build date and version of the firmware on the VideoCore

  • log status displays the error log status of the various VideoCore firmware areas


Displays the build date and version of the VideoCore firmware.


Returns the throttled state of the system. This is a bit pattern. A bit being set indicates the following meanings:

Bit Hex value Meaning



Undervoltage detected



Arm frequency capped



Currently throttled



Soft temperature limit active



Undervoltage has occurred



Arm frequency capping has occurred



Throttling has occurred



Soft temperature limit has occurred


Returns the temperature of the SoC as measured by its internal temperature sensor. On Raspberry Pi 4, measure_temp pmic returns the temperature of the PMIC.

measure_clock [clock]

This returns the current frequency of the specified clock. The options are:

clock Description


ARM core(s)


GPU core


H.264 block


Image Sensor Pipeline


3D block




PWM block (analogue audio output)


SD card interface


Pixel valves


Analogue video encoder




Display Parallel Interface

e.g. vcgencmd measure_clock arm

measure_volts [block]

Displays the current voltages used by the specific block.

block Description


VC4 core voltage


SDRAM Core Voltage


SDRAM I/O voltage


SDRAM Phy Voltage


Displays the content of the OTP (one-time programmable) memory inside the SoC. These are 32-bit values, indexed from 8 to 64. See the OTP bits page for more details.

get_config [configuration item|int|str]

Display value of the configuration setting specified: alternatively, specify either int (integer) or str (string) to see all configuration items of the given type. For example:

vcgencmd get_config total_mem

…​returns the total memory on the device in megabytes.

get_mem type

Reports on the amount of memory addressable by the Arm and the GPU. To show the amount of Arm-addressable memory, use vcgencmd get_mem arm; to show the amount of GPU-addressable memory, use vcgencmd get_mem gpu. Note that on devices with more than 1GB of memory, the arm parameter will always return 1GB minus the gpu memory value, since the GPU firmware is only aware of the first 1GB of memory. To get an accurate report of the total memory on the device, see the total_mem configuration item and the get_config section above.

codec_enabled [type]

Reports whether the specified codec type is enabled. Possible options for type are AGIF, FLAC, H263, H264, MJPA, MJPB, MJPG, MPG2, MPG4, MVC0, PCM, THRA, VORB, VP6, VP8, WMV9, WVC1. Note that because the H.265 HW block on the Raspberry Pi 4 and 400 is not part of the VideoCore GPU, its status is not accessed via this command.


Displays statistics on any OOM (out of memory) events occurring in the VideoCore memory space.


Displays statistics from the relocatable memory allocator on the VideoCore.


Returns the current speed, voltage and temperature of the ring oscillator.


vclog is an application to display log messages from the VideoCore GPU from Linux running on the Arm. It needs to be run as root.

sudo vclog --msg prints out the message log, whilst sudo vclog --assert prints out the assertion log.

Accessibility options

Visual aids

Users of Raspberry Pi OS who have visual impairments can find helpful tools in the Recommended Software menu.

We offer the Orca screen reader to better navigate the Raspberry Pi operating system, along with a screen magnifier to increase the readability of UI and screen elements.

Orca screen reader

You can install Orca screen reader from the Recommended Software section of the main Raspberry Pi menu. Alternatively you can press Ctrl + Alt + Space to automatically install Orca.

When booting Raspberry Pi OS for the first time after installing a new image, an automatic spoken reminder will play after 30 seconds. This reminder will provide instructions on how to install Orca.

Python on Raspberry Pi

Python 3 is installed by default on Raspberry Pi OS, and is used for many important functions. Interfering with the system Python installation can cause problems for your operating system, so it’s important that if you install third-party Python libraries, you use the correct package-management tools.

There are two routes to installing libraries into the default python distribution. You can use apt and install pre-configured system packages, or you can use pip to install packages which are not distributed as part of Raspberry Pi OS.

From Bookworm onwards, packages installed via pip must be installed into a Python Virtual Environment using venv. This has been introduced by the Python community, not by Raspberry Pi; see PEP 668 for more details.

Installing Python packages using apt

Installing packages using apt is the preferred method for installing Python libraries under Raspberry Pi OS.

Packages installed via apt are tested, are usually pre-compiled so they install faster, and are designed for Raspberry Pi OS. They won’t break your system. Installing via this route also means that all required dependencies are also installed, and a log of installation is maintained by the OS so installation can be easily rolled back (libraries can be uninstalled) if needed.

For instance, to install the Python 3 library to support the Raspberry Pi Build HAT you would:

$ sudo apt install python3-build-hat

…​to install the pre-built library.

Using apt makes installing larger packages, like numpy (which has many native dependencies including a Fortran compiler), much simpler than installing individual packages using Python’s own package-management system.

If you want to install a Python library called "foobar" you can use apt search foobar to find the exact package name. In most cases, you’ll find that the required package is going to be called python-foobar or python3-foobar.

About Python virtual environments

In previous versions of the operating system, it was possible to install libraries directly, system-wide, using the package installer for Python, commonly known as pip. You’ll find the following sort of command in many tutorials online.

$ pip install buildhat

In newer versions of Raspberry Pi OS, and other operating systems, this is disallowed. If you try and install a Python package system-wide you’ll receive an error similar to this:

$ pip install buildhat
error: externally-managed-environment

× This environment is externally managed
╰─> To install Python packages system-wide, try apt install
  python3-xyz, where xyz is the package you are trying to

  If you wish to install a non-Debian-packaged Python package,
  create a virtual environment using python3 -m venv path/to/venv.
  Then use path/to/venv/bin/python and path/to/venv/bin/pip. Make
  sure you have python3-full installed.

  For more information visit http://rptl.io/venv

note: If you believe this is a mistake, please contact your Python installation or OS distribution provider. You can override this, at the risk of breaking your Python installation or OS, by passing --break-system-packages.
hint: See PEP 668 for the detailed specification.

This error is generated because you’re trying to install a third-party package into the system Python. A long-standing practical problem for Python users has been conflicts between OS package managers like apt and Python-specific package management tools like pip. These conflicts include both Python-level API incompatibilities and conflicts over file ownership.

Therefore from Bookworm onwards, packages installed via pip must be installed into a Python virtual environment using venv. A virtual environment is a container where you can safely install third-party modules so they won’t interfere with, or break, your system Python.

Using pip with virtual environments

To use a virtual environment you will need to create a container to store the environment. There are several ways you can do this depending on how you want to work with Python.

Using a separate environment for each project

One way you can proceed is to create a new virtual environment for each Python project you make. Here, you’ll create a directory to hold your own code along with a virtual environment directory:

$ mkdir my_project
$ cd my_project
$ python -m venv env

If you now look inside the my_project directory you’ll see a directory called env.

$ ls -la
total 12
drwxr-xr-x  3 pi pi 4096 Oct  3 14:34 .
drwx------ 20 pi pi 4096 Oct  3 14:34 ..
drwxr-xr-x  5 pi pi 4096 Oct  3 14:34 env
If you want to inherit the currently installed packages from the system Python, you should create your virtual environment using python -m venv --system-site-packages env.

Inside this directory is a full Python distribution. To activate your virtual environment and make that version of Python the one you’re currently using, you should type:

$ source env/bin/activate
(env) $

You’ll see that your prompt is now prepended with (env) to indicate that you’re no longer using the system Python. Instead, you’re using the version of Python contained inside your virtual environment. Any changes you make here won’t cause problems for your system Python; nor will any new modules you install into your environment.

(env) $ which python

If you install a third-party package, it’ll install into the Python distribution in your virtual environment:

(env) $ pip install buildhat
Looking in indexes: https://pypi.org/simple, https://www.piwheels.org/simple
Collecting buildhat
  Downloading https://www.piwheels.org/simple/buildhat/buildhat-0.5.12-py3-none-any.whl (57 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 57.8/57.8 kB 2.8 MB/s eta 0:00:00
Collecting gpiozero
  Downloading https://www.piwheels.org/simple/gpiozero/gpiozero-2.0-py3-none-any.whl (150 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 150.5/150.5 kB 6.9 MB/s eta 0:00:00
Collecting pyserial
  Downloading https://www.piwheels.org/simple/pyserial/pyserial-3.5-py2.py3-none-any.whl (90 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 90.6/90.6 kB 7.5 MB/s eta 0:00:00
Collecting colorzero
  Downloading https://www.piwheels.org/simple/colorzero/colorzero-2.0-py2.py3-none-any.whl (26 kB)
Requirement already satisfied: setuptools in ./env/lib/python3.11/site-packages (from colorzero->gpiozero->buildhat) (66.1.1)
Installing collected packages: pyserial, colorzero, gpiozero, buildhat
Successfully installed buildhat-0.5.12 colorzero-2.0 gpiozero-2.0 pyserial-3.5
(env) $

Now, if you pip list, you’ll see that your current version of Python includes your new modules.

(env) $ pip list
Package    Version
---------- -------
buildhat   0.5.12
colorzero  2.0
gpiozero   2.0
pip        23.0.1
pyserial   3.5
setuptools 66.1.1

After writing your code, you can run it from the command line inside the virtual environment as you’d expect, by invoking Python as usual.

(env) $ ls -la
total 12
drwxr-xr-x  3 pi pi 4096 Oct  3 14:34 .
drwx------ 20 pi pi 4096 Oct  3 14:34 ..
drwxr-xr-x  5 pi pi 4096 Oct  3 14:34 env
-rw-r--r--  1 pi pi    0 Oct  3 14:45 my_code.py
(env) $ python my_code.py
Hello World!
(env) $

You can leave your virtual environment and return to using the system Python by typing:

(env) $ deactivate

…​and demonstrate to yourself you’ve done so by checking the installed packages using pip list.

Using a separate environment for each user

An alternative method to creating a virtual environment for each of your Python projects is to create a single virtual environment for your user account, and then activate that environment before running any of your Python code. This approach may be preferred if you commonly install the same set of modules for each project, and don’t want to have to bother creating individual Python environments for each project, essentially just duplicating your environment.

$ python -m venv ~/.env
$ source ~/.env/bin/activate
(.env) $

You can check again that you’re in a separate environment by using pip list:

(.env) $ pip list
Package    Version
---------- -------
pip        23.0.1
setuptools 66.1.1

…​and leave it using deactivate.

(.env) $ deactivate

Using the Thonny editor

Thonny is our recommended editor when you’re working with Python on the Raspberry Pi. By default, Thonny uses the system Python. However, you can switch to using a Python virtual environment by clicking on the interpreter menu located at the bottom right of the Thonny window. Clicking on this will offer you a menu to select a configured interpreter or to Configure interpreter…​.

thonny venv

Selecting this will open a popup allowing you to create a new virtual environment.

GPIO and the 40-pin Header

A powerful feature of the Raspberry Pi is the row of GPIO (general-purpose input/output) pins along the top edge of the board. A 40-pin GPIO header is found on all current Raspberry Pi boards although it is unpopulated on Raspberry Pi Zero, Raspberry Pi Zero W, and Raspberry Pi Zero 2 W. The GPIO headers on all boards have a 0.1-inch (2.54 mm) pin pitch.

GPIO pins

Any of the GPIO pins can be designated in software as an input or output pin and used for a wide range of purposes.

GPIO layout
The GPIO pin numbering scheme is not in numerical order. GPIO pins 0 and 1 are present on the board (physical pins 27 and 28), but are reserved for advanced use.


Two 5V pins and two 3.3V pins are present on the board, as well as a number of ground pins (GND), which can not be reconfigured. The remaining pins are all general-purpose 3.3V pins, meaning outputs are set to 3.3V and inputs are 3.3V-tolerant.


A GPIO pin designated as an output pin can be set to high (3.3V) or low (0V).


A GPIO pin designated as an input pin can be read as high (3.3V) or low (0V). This is made easier with the use of internal pull-up or pull-down resistors. Pins GPIO2 and GPIO3 have fixed pull-up resistors, but for other pins this can be configured in software.

Other functions

As well as simple input and output devices, the GPIO pins can be used with a variety of alternative functions, some are available on all pins, others on specific pins.

  • PWM (pulse-width modulation)

    • Software PWM available on all pins

    • Hardware PWM available on GPIO12, GPIO13, GPIO18, GPIO19

  • SPI

    • SPI0: MOSI (GPIO10); MISO (GPIO9); SCLK (GPIO11); CE0 (GPIO8), CE1 (GPIO7)

    • SPI1: MOSI (GPIO20); MISO (GPIO19); SCLK (GPIO21); CE0 (GPIO18); CE1 (GPIO17); CE2 (GPIO16)

  • I2C

    • Data: (GPIO2); Clock (GPIO3)

    • EEPROM Data: (GPIO0); EEPROM Clock (GPIO1)

  • Serial

    • TX (GPIO14); RX (GPIO15)

GPIO pinout

A GPIO reference can be accessed on your Raspberry Pi by opening a terminal window and running the command pinout. This tool is provided by the GPIO Zero Python library, which is installed by default in Raspberry Pi OS.

While connecting up simple components to the GPIO pins is perfectly safe, it’s important to be careful how you wire things up. LEDs should have resistors to limit the current passing through them. Do not use 5V for 3.3V components. Do not connect motors directly to the GPIO pins, instead use an H-bridge circuit or a motor controller board.


In order to use the GPIO ports, your user must be a member of the gpio group. The default user account is a member by default, other users need to be added manually.

sudo usermod -a -G gpio <username>

GPIO in Python

Using the GPIO Zero library makes it easy to control GPIO devices with Python. The library is comprehensively documented at gpiozero.readthedocs.io.


To control an LED connected to GPIO17:

from gpiozero import LED
from time import sleep

led = LED(17)

while True:

Run this in an IDE like Thonny, and the LED will blink on and off repeatedly.

LED methods include on(), off(), toggle(), and blink().


To read the state of a button connected to GPIO2:

from gpiozero import Button
from time import sleep

button = Button(2)

while True:
    if button.is_pressed:

Button functionality includes the properties is_pressed and is_held; callbacks when_pressed, when_released, and when_held; and methods wait_for_press() and wait_for_release.

Button and LED

To connect the LED and button together, you can use this code:

from gpiozero import LED, Button

led = LED(17)
button = Button(2)

while True:
    if button.is_pressed:


from gpiozero import LED, Button

led = LED(17)
button = Button(2)

while True:


from gpiozero import LED, Button

led = LED(17)
button = Button(2)

button.when_pressed = led.on
button.when_released = led.off