pi-top Python SDK (Preview)¶
A simple, modular interface for interacting with a pi-top and its related accessories and components.
Supports all pi-top devices:

Supports pi-top Maker Architecture (PMA):

Supports all pi-top peripherals:

Status: Active Development¶
This SDK is currently in active development. Please be patient while we work towards v1.0.0!
Backwards Compatibility¶
When this library reaches v1.0.0, we will aim to maintain backwards-compatibility thereafter. Until then, every effort will be made to ensure stable support, but it cannot be guaranteed. Breaking changes will be clearly documented.
About¶
This SDK aims to provide an easy-to-use framework for managing a pi-top. It includes a Python 3 package (pitop), with several custom modules and classes for interfacing with a range of pi-top devices and peripherals. It also contains CLI utilities, to interact with your pi-top using the terminal.
The SDK is included out-of-the-box with pi-topOS.
Ensure that you keep your system up-to-date to enjoy the latest features and bug fixes.
This library is installed as a Python 3 module called pitop. It includes several submodules that allow you to easily interact with most of the hardware inside a pi-top.
You can easily connect different components of the system using the modules available in the library:
from time import sleep
from pitop import UltrasonicSensor, Miniscreen
utrasonic = UltrasonicSensor("D1")
miniscreen = Miniscreen()
while True:
miniscreen.display_text(utrasonic.distance)
sleep(0.1)
Check out the Overview chapter for more information on what you can do.
The SDK also contains a Command Line Interface (CLI). See the ‘pi-top’ command for more information.
Table of Contents¶
Getting Started¶
Installing the SDK¶
pi-topOS¶
This SDK is pre-installed on pi-topOS, so you don’t need to install it manually!
Using apt¶
The recommended way of getting the latest version is through apt.
Check out Using pi-top Hardware with Raspberry Pi OS in the pi-top knowledge base for how to do this.
Note
If you only want to install the SDK, then you can replace the “Install software packages” step:
sudo apt install -y python3-pitop
This will also install additional packages onto your system that the SDK requires in order to work.
Using PyPI¶
In general, this is not recommended.
You can also install the latest version of the SDK through PyPI in your pi-top with:
pip3 install pitop
You’ll need to install one extra dependency for the SDK to work when using pip:
sudo apt install libatlas-base-dev -y
Note
This will not install the system packages required for all areas of the SDK to work. This may be useful if you wish to use a virtualenv with a different version dependency to the system.
Building from source¶
In general, this is not recommended.
Building from source is simple:
git clone https://github.com/pi-top/pi-top-Python-SDK.git
cd pi-top-Python-SDK
pip3 install -e .
You’ll need to install one extra dependency for the SDK to work when using pip:
sudo apt install libatlas-base-dev -y
Note
This will not install the system packages required for all areas of the SDK to work. This may be useful if you wish to use a virtualenv with a different version dependency to the system.
Checking that the SDK is installed and working¶
Try and run the following:
pi-top devices hub
If this works, then you should be good to go! Go and check out the Examples section!
Overview¶
This API provides features that are selectively available, depending on the pi-top device that you are using. To find out what is available for your pi-top device, see the relevant section below.
Choose your pi-top device to go to the relevant section:
This API provides some convenience classes for common System Peripheral Devices, such as:
- Camera
- Keyboard
pi-top [4]¶
Interacting with onboard pi-top [4] hardware¶
pi-top [4] supports the following API devices/components for its onboard hardware:
pi-top [4] does not support the following API devices/components:
This is due to the fact that pi-top [4] has no attached display, and the pi-top [4] official display’s brightness is handled in hardware with physical brightness buttons, and the backlight is handled by DPMS (the operating system’s internal screen blanking functionality).
Physical computing with pi-top [4]¶
pi-top [4] supports the following API devices/components for physical computing:
The pi-topPULSE can be used as a Raspberry Pi HAT with a pi-top [4]. The USB camera library can be used with any USB camera, and - whilst technically can be used with any Raspberry Pi/pi-top, was designed with the pi-top [4] and PMA in mind.
pi-top [4] does not support the following API devices/components:
This is due to the fact that pi-topPROTO+ makes use of the legacy ‘modular rail’, which has no way of connecting to a pi-top [4].
Check out the key concepts for pi-top Maker Architecture for more information.
pi-top laptops¶
Interacting with onboard pi-top laptop hardware¶
pi-top laptops (Original pi-top and pi-top [3]) support the following API devices/components for their onboard hardware:
pi-top laptops does not support the following API devices/components:
This is due to the fact that pi-top laptops do not include the pi-top [4]’s miniscreen.
Using peripherals with a pi-top laptop¶
pi-top laptops (Original pi-top and pi-top [3]) support the following API devices/components for use with peripherals:
Note that the USB camera library works with any pi-top with a USB camera connected. This was designed for pi-top [4] usage, but due to its general purpose functionality, it can technically be used if desired.
pi-topSPEAKER support is provided automagically by pi-topd, and so there is no exposed API for this.
pi-top laptops does not support the following API devices/components:
This is due to the fact that PMA is only available for pi-top [4].
pi-topCEED¶
Interacting with onboard pi-topCEED hardware¶
pi-top laptops (Original pi-top and pi-top [3]) support the following API devices/components for their onboard hardware:
pi-top laptops does not support the following API devices/components:
This is due to the fact that pi-topCEED does not include a battery or the pi-top [4]’s miniscreen.
Using peripherals with a pi-topCEED¶
pi-topCEED supports the following API devices/components for use with peripherals:
Note that the USB camera library works with any pi-top with a USB camera connected. This was designed for pi-top [4] usage, but due to its general purpose functionality, it can technically be used if desired.
pi-topSPEAKER support is provided automagically by pi-topd, and so there is no exposed API for this.
pi-topCEED does not support the following API devices/components:
This is due to the fact that PMA is only available for pi-top [4].
Key Concepts¶
pi-top Maker Architecture¶
This section aims to clarify the various components of PMA, and the terminology that is required to get the most out of it.
Inputs and Outputs¶
A component can be classified as an Input or Output, according to how it behaves.
An Input component generates electric signals that can be interpreted as information when read. For example, when a button is clicked, the electric signal it produces lets you know that it’s state changed.
An Output component receives electric signals and performs an action based on them. For example, a buzzer; when no signal is applied it will be silent; however when an electric signal is applied, it will generate sound.
Digital and Analog¶
Components can also be classified according to the type of electric signals they use.
Digital components only use digital electric signals; digital signals are discrete and carry information in binary form, most of the times consisting in different voltage values. This change in voltage can be read by a Raspberry Pi directly.
Analog components use analog electric signals; analog signals are continuous and can have infinite values in a determined range. Raspberry Pi can’t directly read these signals since it’s a digital component. To be able to manage analog signals, the Foundation and Expansion plates include an Analog to Digital Converter (ADC). This device converts the analog signal from the component into a digital signal that can be interpreted by the Raspberry Pi.
Ports and Pins¶
The pi-top Maker Architecture (PMA) connector on the pi-top [4] makes available all GPIO from the Raspberry Pi to the Foundation and Expansion Plates.
This means that the ports located in these plates are mapped to the GPIO header on the Raspberry Pi, providing easy and standard access through Grove connectors to these pins.
Foundation and Expansion Plates have multiple connectors that can be used to interface with different kind components.

Motor Ports¶
Communicates a motor encoder component with the motor controller, located inside the Expansion Plate.
These ports are labeled from M0 to M3
ServoMotor Ports¶
Communicates a servo motor component with the servomotor controller, located inside the Expansion Plate.
These ports are labeled from S0 to S3.
Identifying PMA port for a component¶
The components included in the Foundation Kit & Robotics Kit can be classified according to how they operate and communicate.
Digital component¶
These components should be connected to a Digital Port on the Foundation/Expansion Plates.
The Digital components included in the Foundation & Robotics Kits are:
Analog component¶
These components should be connected to a Analog Port on the Foundation/Expansion Plates, labeled from A0 to A3.
The Analog components included in the Foundation & Robotics Kits are:
Motor component¶
An electromechanical component that is controlled by communicating with a microprocessor located inside the Expansion Plate.
These components should be connected to a Motor Port or to ServoMotor Port on the Expansion Plate, depending on the component used.
The Motor component included in the Robotics Kits are:
- MotorEncoder (connects to a Motor Port)
- ServoMotor (connects to a ServoMotor Port)
More Information¶
For more information about pi-top Maker Architecture, check out the pi-top Knowledge Base.
pi-top [4] Miniscreen¶

The miniscreen of the pi-top [4] can be found on the front, comprised of an 128x64 pixel OLED screen and 4 programmable buttons.
The pt-miniscreen package (pt-sys-oled in earlier versions of pi-topOS), provided out-of-the-box with pi-topOS (and available for Raspberry Pi OS), provides a convenient interactive menu interface, using the pi-top [4]’s miniscreen OLED display and buttons for navigation and actions. This menu includes useful information and options about the system state and configuration.
When a user program creates an instance of the miniscreen, the system menu will clear itself and start to ignore button press events until the user program exits. This is true, regardless of whether or not the OLED display or the buttons were intended to be used.
Warning
When you write a program that interacts with the pi-top [4] miniscreen, the miniscreen display will clear itself, ready to be controlled by user code.
The system menu cannot be accessed until the program exits, at which point the system menu is automatically restored.
Note
For convenience, it is recommended that you provide yourself with an easy method of being able to exit your program. It is recommended that you configure an input (such as the miniscreen’s ‘cancel’ button) to trigger an exit. This is particularly helpful if you wish to start/stop your project headlessly (that is, without requiring a display or keyboard/mouse).
Here is one way of achieving this:
from time import sleep
from pitop import Pitop
pitop = Pitop()
miniscreen = pitop.miniscreen
miniscreen.display_multiline_text("Press cancel to exit!", font_size=22)
while not miniscreen.cancel_button.is_pressed:
sleep(0.1)
miniscreen.display_multiline_text("Bye!")
sleep(2)
If you wish to make use of any of the functionality in system menu, have a go at implementing it yourself in your own project!
Recipes¶
In addition to the examples provided for each component/device in the API reference section of this documentation, the following recipes demonstrate some of the more advanced capabilities of the pi-top Python SDK. In particular, these recipes focus on practical use-cases that make use of multiple components/devices within the pi-top Python SDK.
Be sure to check out each component/device separately for simple examples of how to use them.
PMA: Using a Button to Control an LED¶
from time import sleep
from pitop import LED, Button
button = Button("D1")
led = LED("D2")
# Connect button to LED
button.when_pressed = led.on
button.when_released = led.off
# Wait for Ctrl+C to exit
try:
while True:
sleep(1)
except KeyboardInterrupt:
pass
Robotics Kit: DIY Rover¶
from threading import Thread
from time import sleep
from pitop import BrakingType, EncoderMotor, ForwardDirection
# Setup the motors for the rover configuration
motor_left = EncoderMotor("M3", ForwardDirection.CLOCKWISE)
motor_right = EncoderMotor("M0", ForwardDirection.COUNTER_CLOCKWISE)
motor_left.braking_type = BrakingType.COAST
motor_right.braking_type = BrakingType.COAST
# Define some functions for easily controlling the rover
def drive(target_rpm: float):
print("Start driving at target", target_rpm, "rpm...")
motor_left.set_target_rpm(target_rpm)
motor_right.set_target_rpm(target_rpm)
def stop_rover():
print("Stopping rover...")
motor_left.stop()
motor_right.stop()
def turn_left(rotation_speed: float):
print("Turning left...")
motor_left.stop()
motor_right.set_target_rpm(rotation_speed)
def turn_right(rotation_speed: float):
print("Turning right...")
motor_right.stop()
motor_left.set_target_rpm(rotation_speed)
# Start a thread to monitor the rover
def monitor_rover():
while True:
print(
"> Rover motor RPM's (L,R):",
round(motor_left.current_rpm, 2),
round(motor_right.current_rpm, 2),
)
sleep(1)
monitor_thread = Thread(target=monitor_rover, daemon=True)
monitor_thread.start()
# Go!
rpm_speed = 100
for _ in range(4):
drive(rpm_speed)
sleep(5)
turn_left(rpm_speed)
sleep(5)
stop_rover()
Robotics Kit: Robot - Moving Randomly¶
from random import randint
from time import sleep
from pitop import Pitop
from pitop.robotics.drive_controller import DriveController
# Create a basic robot
robot = Pitop()
drive = DriveController(left_motor_port="M3", right_motor_port="M0")
robot.add_component(drive)
# Use miniscreen display
robot.miniscreen.display_multiline_text("hey there!")
def random_speed_factor():
# 0.01 - 1, 0.01 resolution
return randint(1, 100) / 100
def random_sleep():
# 0.5 - 2, 0.5 resolution
return randint(1, 4) / 2
# Move around randomly
robot.drive.forward(speed_factor=random_speed_factor())
sleep(random_sleep())
robot.drive.left(speed_factor=random_speed_factor())
sleep(random_sleep())
robot.drive.backward(speed_factor=random_speed_factor())
sleep(random_sleep())
robot.drive.right(speed_factor=random_speed_factor())
sleep(random_sleep())
Robotics Kit: Robot - Line Detection¶
from signal import pause
from pitop import Camera, DriveController, Pitop
from pitop.processing.algorithms.line_detect import process_frame_for_line
# Assemble a robot
robot = Pitop()
robot.add_component(DriveController(left_motor_port="M3", right_motor_port="M0"))
robot.add_component(Camera())
# Set up logic based on line detection
def drive_based_on_frame(frame):
processed_frame = process_frame_for_line(frame)
if processed_frame.line_center is None:
print("Line is lost!", end="\r")
robot.drive.stop()
else:
print(f"Target angle: {processed_frame.angle:.2f} deg ", end="\r")
robot.drive.forward(0.25, hold=True)
robot.drive.target_lock_drive_angle(processed_frame.angle)
robot.miniscreen.display_image(processed_frame.robot_view)
# On each camera frame, detect a line
robot.camera.on_frame = drive_based_on_frame
pause()
Displaying camera stream in pi-top [4]’s miniscreen¶
from pitop import Camera, Pitop
camera = Camera()
pitop = Pitop()
camera.on_frame = pitop.miniscreen.display_image
Robotics Kit: Robot - Control using Bluedot¶
Note
BlueDot is a Python library that allows you to control Raspberry Pi projects remotely. This example demonstrates a way to control a robot with a virtual joystick.
from signal import pause
from threading import Lock
from bluedot import BlueDot
from pitop import DriveController
bd = BlueDot()
bd.color = "#00B2A2"
lock = Lock()
drive = DriveController(left_motor_port="M3", right_motor_port="M0")
def move(pos):
if lock.locked():
return
if any(
[
pos.angle > 0 and pos.angle < 20,
pos.angle < 0 and pos.angle > -20,
]
):
drive.forward(pos.distance, hold=True)
elif pos.angle > 0 and 20 <= pos.angle <= 160:
turn_radius = 0 if 70 < pos.angle < 110 else pos.distance
speed_factor = -pos.distance if pos.angle > 110 else pos.distance
drive.right(speed_factor, turn_radius)
elif pos.angle < 0 and -160 <= pos.angle <= -20:
turn_radius = 0 if -110 < pos.angle < -70 else pos.distance
speed_factor = -pos.distance if pos.angle < -110 else pos.distance
drive.left(speed_factor, turn_radius)
elif any(
[
pos.angle > 0 and pos.angle > 160,
pos.angle < 0 and pos.angle < -160,
]
):
drive.backward(pos.distance, hold=True)
def stop(pos):
lock.acquire()
drive.stop()
def start(pos):
if lock.locked():
lock.release()
move(pos)
bd.when_pressed = start
bd.when_moved = move
bd.when_released = stop
pause()
Using the pi-topPULSE’s LED matrix to show the battery level¶
from time import sleep
from pitop import Pitop
from pitop.pulse import ledmatrix
def draw_battery_outline(): # Draw the naked battery
for y in range(0, 6):
ledmatrix.set_pixel(1, y, 64, 64, 255)
ledmatrix.set_pixel(5, y, 64, 64, 255)
for x in range(2, 5):
ledmatrix.set_pixel(x, 0, 64, 64, 255)
ledmatrix.set_pixel(x, 6, 192, 192, 192)
ledmatrix.show()
def update_battery_state(charging_state, capacity):
r = 0
g = 0
b = 0
if charging_state == 0:
if capacity < 11:
r = 255
else:
g = 255
elif charging_state == 1:
r = 255
g = 225
cap = int(capacity / 20) + 1
if cap < 0:
cap = 0
if cap > 5:
cap = 5
if cap > 0:
for y in range(1, cap + 1):
ledmatrix.set_pixel(2, y, r, g, b)
ledmatrix.set_pixel(3, y, r, g, b)
ledmatrix.set_pixel(4, y, r, g, b)
if cap == 0:
cap = 1
if cap < 6:
if (capacity < 50) and (charging_state == 0):
# blinking warning
for i in range(1, 3):
for y in range(cap + 1, 6):
ledmatrix.set_pixel(2, y, 0, 0, 0)
ledmatrix.set_pixel(3, y, 0, 0, 0)
ledmatrix.set_pixel(4, y, 0, 0, 0)
ledmatrix.show()
sleep(0.4)
for y in range(cap + 1, 6):
ledmatrix.set_pixel(2, y, 255, 0, 0)
ledmatrix.set_pixel(3, y, 255, 0, 0)
ledmatrix.set_pixel(4, y, 255, 0, 0)
ledmatrix.show()
sleep(0.4)
else:
for y in range(cap + 1, 6):
ledmatrix.set_pixel(2, y, 0, 0, 0)
ledmatrix.set_pixel(3, y, 0, 0, 0)
ledmatrix.set_pixel(4, y, 0, 0, 0)
ledmatrix.show()
sleep(5)
return 0
def main():
ledmatrix.rotation(0)
ledmatrix.clear() # Clear the display
draw_battery_outline() # Draw the battery outline
battery = Pitop().battery
while True:
try:
charging_state, capacity, _, _ = battery.get_full_state()
update_battery_state(charging_state, capacity) # Fill battery with capacity
except Exception as e:
print("Error getting battery info: " + str(e))
if __name__ == "__main__":
main()
Choose a pi-top [4] miniscreen startup animation¶
Note
This code makes use of the GIPHY SDK. Follow the instructions here to find out how to apply for an API Key to use with this project.
Replace <MY GIPHY KEY> with the key provided (keep the quotes).
You can change the type of images that you get by changing SEARCH_TERM = “Monochrome” to whatever you want.
import json
from configparser import ConfigParser
from os import geteuid
from random import randint
from signal import pause
from sys import exit
from time import sleep
from urllib.parse import urlencode
from urllib.request import urlopen
from PIL import Image
from requests.models import PreparedRequest
from pitop.miniscreen import Miniscreen
def is_root():
return geteuid() == 0
if not is_root():
print("Admin access required - please run this script with 'sudo'.")
exit()
# Define Giphy parameters
SEARCH_LIMIT = 10
SEARCH_TERM = "Monochrome"
CONFIG_FILE_PATH = "/etc/pt-miniscreen/settings.ini"
STARTUP_GIF_PATH = "/home/pi/miniscreen-startup.gif"
API_KEY = "<MY GIPHY KEY>"
# Define global variables
gif = None
miniscreen = Miniscreen()
req = PreparedRequest()
req.prepare_url(
"http://api.giphy.com/v1/gifs/search",
urlencode({"q": SEARCH_TERM, "api_key": API_KEY, "limit": f"{SEARCH_LIMIT}"}),
)
def display_instructions_dialog():
miniscreen.select_button.when_pressed = play_random_gif
miniscreen.cancel_button.when_pressed = None
miniscreen.display_multiline_text(
"Press SELECT to load a random GIF!", font_size=18
)
def display_user_action_select_dialog():
miniscreen.select_button.when_pressed = save_gif_as_startup
miniscreen.cancel_button.when_pressed = play_random_gif
miniscreen.display_multiline_text(
"SELECT: save GIF as default startup animation. CANCEL: load new GIF",
font_size=12,
)
def display_loading_dialog():
miniscreen.select_button.when_pressed = None
miniscreen.cancel_button.when_pressed = display_instructions_dialog
miniscreen.display_multiline_text("Loading random GIF...", font_size=18)
def display_saving_dialog():
miniscreen.select_button.when_pressed = None
miniscreen.cancel_button.when_pressed = None
miniscreen.display_multiline_text(
"GIF saved as default startup animation!", font_size=18
)
# Saving is fast, so we need to wait a short while for the message to be seen on the display
sleep(1)
def play_random_gif():
global gif
# Show "Loading..." while processing for a GIF
display_loading_dialog()
# Get GIF data from Giphy
with urlopen(req.url) as response:
data = json.loads(response.read())
# Extract random GIF URL from JSON response
gif_url = data["data"][randint(0, SEARCH_LIMIT - 1)]["images"]["fixed_height"][
"url"
]
# Load GIF from URL
gif = Image.open(urlopen(gif_url))
# Play one loop of GIF animation
miniscreen.play_animated_image(gif)
# Ask user if they want to save it
display_user_action_select_dialog()
def save_gif_as_startup():
# Display "saving" dialog
display_saving_dialog()
# Save file to home directory
gif.save(STARTUP_GIF_PATH, save_all=True)
config = ConfigParser()
cfg_section = "Bootsplash"
if not config.has_section(cfg_section):
config.add_section(cfg_section)
config.set(cfg_section, "Path", STARTUP_GIF_PATH)
with open(CONFIG_FILE_PATH, "w") as f:
config.write(f)
# Go back to the start
display_instructions_dialog()
# Display initial dialog
display_instructions_dialog()
# Wait indefinitely for user input
pause()
API - pi-top Device¶
Pitop¶
This class represents a pi-top device. Each of the on-board features of pi-tops can be accessed from this object.
Note
This class has been built with pi-top [4] in mind, as is in early development. You may notice that some features do not behave as expected on other platforms.
If you would like to help us with development, please refer to the Contributing document in this repository for information!
Here is some sample code demonstrating how the various subsystems of a pi-top [4] can be accessed and used:
from time import sleep
from PIL import Image
from pitop import Pitop
# Set up pi-top
pitop = Pitop()
# Say hi!
pitop.miniscreen.display_text("Hello!")
sleep(2)
# Display battery info
battery_capacity = pitop.battery.capacity
battery_charging = pitop.battery.is_charging
pitop.miniscreen.display_multiline_text(
"Battery Status:\n"
f"-Capacity: {battery_capacity}%\n"
f"-Charging: {battery_charging}",
font_size=15,
)
sleep(2)
# Configure buttons to do something
keep_running = True
def display_gif_and_exit():
image = Image.open(
"/usr/lib/python3/dist-packages/pitop/miniscreen/images/rocket.gif"
)
pitop.miniscreen.play_animated_image(image)
pitop.miniscreen.display_text("Bye!")
sleep(2)
global keep_running
keep_running = False
pitop.miniscreen.select_button.when_pressed = display_gif_and_exit
pitop.miniscreen.cancel_button.when_pressed = display_gif_and_exit
pitop.miniscreen.up_button.when_pressed = display_gif_and_exit
pitop.miniscreen.down_button.when_pressed = display_gif_and_exit
pitop.miniscreen.display_multiline_text("Press any button...", font_size=25)
# Sleep until `display_gif_and_exit` runs
while keep_running:
sleep(0.3)
Although it is possible to access pi-top subsystems individually, it is recommended to access them via this class.
Class Reference: Pitop¶
-
class
pitop.
Pitop
[source]¶ Represents a pi-top Device.
When creating a Pitop object, multiple properties will be set, depending on the pi-top device that it’s running the code. For example, if run on a pi-top [4], a miniscreen attribute will be created as an interface to control the miniscreen OLED display, but that won’t be available for other pi-top devices.
The Pitop class is a Singleton. This means that only one instance per process will be created. In practice, this means that if in a particular project you instance a Pitop class in 2 different files, they will share the internal state.
- property miniscreen
- If using a pi-top [4], this property returns a
pitop.miniscreen.Miniscreen
object, to interact with the device’s Miniscreen. - property oled
- Refer to miniscreen.
- property battery
- If using a pi-top with a battery, this property returns a
pitop.battery.Battery
object, to interact with the device’s battery.
-
own_state
¶ Representation of an object state that will be used to determine the current state of an object.
All pi-tops come with some software-controllable onboard hardware. These sections of the API make it easy to access and change the state of your pi-top hardware.
Using the Pitop object¶
Attaching objects and saving configuration to a file¶
from time import sleep
from pitop import LED, Pitop
from pitop.robotics.drive_controller import DriveController
pitop = Pitop()
drive_controller = DriveController()
led = LED("D0", name="red_led")
# Add components to the Pitop object
pitop.add_component(drive_controller)
pitop.add_component(led)
# Do something with the object
pitop.red_led.on()
pitop.drive.forward(0.5)
sleep(2)
pitop.red_led.off()
pitop.drive.stop()
# Store configuration to a file
pitop.save_config("/home/pi/my_custom_config.json")
Loading an existing configuration¶
from time import sleep
from pitop import Pitop
# Load configuration from a previous session
pitop = Pitop.from_file("/home/pi/my_custom_config.json")
# Check the loaded configuration
print(pitop.config)
# Do something with your device
pitop.red_led.on()
pitop.drive.forward(0.5)
sleep(2)
pitop.red_led.off()
pitop.drive.stop()
# Check the state of all the components attached to the Pitop object
pitop.print_state()
pi-top Battery¶
This class provides a simple way to check the current onboard pi-top battery state, and handle some state change events.
This class will work with original pi-top, pi-top [3] and pi-top [4]. pi-topCEED has no onboard battery, and so will not work.
from pitop import Pitop
battery = Pitop().battery
print(f"Battery capacity: {battery.capacity}")
print(f"Battery time remaining: {battery.time_remaining}")
print(f"Battery is charging: {battery.is_charging}")
print(f"Battery is full: {battery.is_full}")
print(f"Battery wattage: {battery.wattage}")
def do_low_battery_thing():
print("Battery is low!")
def do_critical_battery_thing():
print("Battery is critically low!")
def do_full_battery_thing():
print("Battery is full!")
def do_charging_battery_thing():
print("Battery is charging!")
def do_discharging_battery_thing():
print("Battery is discharging!")
# To invoke a function when the battery changes state, you can assign the function to various 'when_' data members
battery.when_low = do_low_battery_thing
battery.when_critical = do_critical_battery_thing
battery.when_full = do_full_battery_thing
battery.when_charging = do_charging_battery_thing
battery.when_discharging = do_discharging_battery_thing
# Another way to react to battery events is to poll
while True:
if battery.is_full:
do_full_battery_thing()
pi-top Display¶
This class provides a simple way to check the current onboard pi-top display state, and handle state change events.
This class will work with original pi-top, pi-topCEED and pi-top [3].
Note
Not compatible with pi-top [4].
pi-top [4] has no onboard display, and the official pi-top [4] FHD Display is not software-controllable.
from signal import pause
from time import sleep
from pitop import Pitop
pitop = Pitop()
display = pitop.display
# Get display information
print(f"Display brightness: {display.brightness}")
print(f"Display blanking timeout: {display.blanking_timeout}")
print(f"Display backlight is on: {display.backlight}")
print(f"Display lid is open: {display.lid_is_open}")
# Change the brightness levels incrementally
display.increment_brightness()
display.decrement_brightness()
# Set brightness explicitly
display.brightness = 7
# Set screen blank state
display.blank()
display.unblank()
# Set screen blanking timeout (s)
display.blanking_timeout = 60
# Define some functions to call on events
def do_brightness_changed_thing(new_brightness):
print(new_brightness)
print("Display brightness has changed!")
def do_screen_blanked_thing():
print("Display is blanked!")
def do_screen_unblanked_thing():
print("Display is unblanked!")
def do_lid_closed_thing():
print("Display lid is closed!")
def do_lid_opened_thing():
print("Display lid is open!")
# 'Wire up' functions to display events
display.when_brightness_changed = do_brightness_changed_thing
display.when_screen_blanked = do_screen_blanked_thing
display.when_screen_unblanked = do_screen_unblanked_thing
display.when_lid_closed = do_lid_closed_thing
display.when_lid_opened = do_lid_opened_thing
# Wait indefinitely for events to be handled in the background
pause()
# Or alternatively poll
print("Polling for if lid is open (Original pi-top/pi-top [3] only)")
while True:
if display.lid_is_open:
do_lid_opened_thing()
sleep(0.1)
pi-top [4] Miniscreen¶

The miniscreen of the pi-top [4] can be found on the front, comprised of an 128x64 pixel OLED screen and 4 programmable buttons.
Check out Key Concepts: pi-top [4] Miniscreen for useful information about how this class works.
Using the Miniscreen’s OLED Display¶

The OLED display is an array of pixels that can be either on or off. Unlike the pixels in a more advanced display, such as the monitor you are most likely reading this on, the display is a “1-bit monochromatic” display. Text and images can be displayed by directly manipulating the pixels.
The pitop.miniscreen.Miniscreen
class directly provides display functions for the OLED.
Displaying text¶
from time import sleep
from pitop import Pitop
pitop = Pitop()
miniscreen = pitop.miniscreen
miniscreen.display_multiline_text("Hello, world!", font_size=20)
sleep(5)
Showing an image¶
from time import sleep
from pitop import Pitop
pitop = Pitop()
miniscreen = pitop.miniscreen
miniscreen.display_image_file(
"/usr/lib/python3/dist-packages/pitop/miniscreen/images/rocket.gif"
)
sleep(2)
Loop a GIF¶
from PIL import Image, ImageSequence
from pitop import Pitop
pitop = Pitop()
miniscreen = pitop.miniscreen
rocket = Image.open("/usr/lib/python3/dist-packages/pitop/miniscreen/images/rocket.gif")
while True:
for frame in ImageSequence.Iterator(rocket):
miniscreen.display_image(frame)
Displaying an GIF once¶
from PIL import Image
from pitop import Pitop
pitop = Pitop()
miniscreen = pitop.miniscreen
rocket = Image.open("/usr/lib/python3/dist-packages/pitop/miniscreen/images/rocket.gif")
miniscreen.play_animated_image(rocket)
Displaying an GIF once through frame by frame¶
from PIL import Image, ImageSequence
from pitop import Pitop
pitop = Pitop()
miniscreen = pitop.miniscreen
rocket = Image.open("/usr/lib/python3/dist-packages/pitop/miniscreen/images/rocket.gif")
for frame in ImageSequence.Iterator(rocket):
miniscreen.display_image(frame)
Displaying an GIF looping in background¶
from time import sleep
from PIL import Image
from pitop import Pitop
pitop = Pitop()
miniscreen = pitop.miniscreen
image = Image.open("/usr/lib/python3/dist-packages/pitop/miniscreen/images/rocket.gif")
# Run animation loop in background by setting `background` to True
miniscreen.play_animated_image(image, background=True, loop=True)
# Do stuff while showing image
print("Counting to 100 while showing animated image on miniscreen...")
for i in range(100):
print("\r{}".format(i), end="", flush=True)
sleep(0.2)
print("\rFinished!")
# Stop animation
miniscreen.stop_animated_image()
Handling basic 2D graphics drawing and displaying¶
from PIL import Image, ImageDraw, ImageFont
from pitop import Pitop
pitop = Pitop()
miniscreen = pitop.miniscreen
image = Image.new(
miniscreen.mode,
miniscreen.size,
)
canvas = ImageDraw.Draw(image)
miniscreen.set_max_fps(1)
def clear():
canvas.rectangle(miniscreen.bounding_box, fill=0)
print("Drawing an arc")
canvas.arc(miniscreen.bounding_box, 0, 180, fill=1, width=1)
miniscreen.display_image(image)
clear()
print("Drawing an image")
# Note: this is an animated file, but this approach will only show the first frame
demo_image = Image.open(
"/usr/lib/python3/dist-packages/pitop/miniscreen/images/rocket.gif"
).convert("1")
canvas.bitmap((0, 0), demo_image, fill=1)
miniscreen.display_image(image)
clear()
print("Drawing a chord")
canvas.chord(miniscreen.bounding_box, 0, 180, fill=1)
miniscreen.display_image(image)
clear()
print("Drawing an ellipse")
canvas.ellipse(miniscreen.bounding_box, fill=1)
miniscreen.display_image(image)
clear()
print("Drawing a line")
canvas.line(miniscreen.bounding_box, fill=1)
miniscreen.display_image(image)
clear()
print("Drawing a pieslice")
canvas.pieslice(miniscreen.bounding_box, 0, 180, fill=1)
miniscreen.display_image(image)
clear()
print("Drawing a point")
canvas.point(miniscreen.bounding_box, fill=1)
miniscreen.display_image(image)
clear()
print("Drawing a polygon")
canvas.polygon(miniscreen.bounding_box, fill=1)
miniscreen.display_image(image)
clear()
print("Drawing a rectangle")
canvas.rectangle(miniscreen.bounding_box, fill=1)
miniscreen.display_image(image)
clear()
print("Drawing some text")
canvas.text((0, 0), "Hello\nWorld!", font=ImageFont.load_default(), fill=1)
miniscreen.display_image(image)
Displaying a clock¶
from datetime import datetime
from PIL import Image, ImageDraw
from pitop import Pitop
pitop = Pitop()
miniscreen = pitop.miniscreen
miniscreen.set_max_fps(1)
image = Image.new(
miniscreen.mode,
miniscreen.size,
)
canvas = ImageDraw.Draw(image)
bounding_box = (32, 0, 95, 63)
big_hand_box = (
bounding_box[0] + 5,
bounding_box[1] + 5,
bounding_box[2] - 5,
bounding_box[3] - 5,
)
little_hand_box = (
bounding_box[0] + 15,
bounding_box[1] + 15,
bounding_box[2] - 15,
bounding_box[3] - 15,
)
while True:
current_time = datetime.now()
# Clear
canvas.rectangle(bounding_box, fill=0)
# Draw face
canvas.ellipse(bounding_box, fill=1)
# Draw hands
angle_second = (current_time.second * 360 / 60) - 90
canvas.pieslice(big_hand_box, angle_second, angle_second + 2, fill=0)
angle_minute = (current_time.minute * 360 / 60) - 90
canvas.pieslice(big_hand_box, angle_minute, angle_minute + 5, fill=0)
angle_hour = (
(current_time.hour * 360 / 12) + (current_time.minute * 360 / 12 / 60)
) - 90
canvas.pieslice(little_hand_box, angle_hour, angle_hour + 5, fill=0)
# Display to screen
miniscreen.display_image(image)
Display a particle-based screensaver¶
from random import randint
from PIL import Image, ImageDraw
from pitop import Pitop
pitop = Pitop()
miniscreen = pitop.miniscreen
image = Image.new(
miniscreen.mode,
miniscreen.size,
)
canvas = ImageDraw.Draw(image)
speed_factor = 15
particles = []
class Particle:
def __init__(self, x, y):
self.x = x
self.y = y
self.update()
def get_position(self):
return (self.x, self.y)
def update(self):
dx = (
(self.x - (miniscreen.width / 2)) / speed_factor
if self.x < (miniscreen.width / 2)
else (self.x - (miniscreen.width / 2)) / speed_factor
)
dy = (
(self.y - (miniscreen.height / 2)) / speed_factor
if self.y < (miniscreen.height / 2)
else (self.y - (miniscreen.height / 2)) / speed_factor
)
self.x += dx
self.y += dy
def add_new_particle():
x = randint(0, miniscreen.width)
y = randint(0, miniscreen.height)
particles.append(Particle(x, y))
while True:
# Clear display
canvas.rectangle(miniscreen.bounding_box, fill=0)
particles.clear()
speed_factor = randint(5, 30)
particle_count = randint(5, 50)
for count in range(particle_count):
add_new_particle()
for _ in range(100):
for particle in particles:
x, y = particle.get_position()
if (x < 0 or x > miniscreen.width) or (y < 0 or y > miniscreen.height):
particles.remove(particle)
add_new_particle()
else:
canvas.point((x, y), fill=1)
particle.update()
miniscreen.display_image(image)
Prim’s algorithm¶
from random import randint, random
from time import sleep
from PIL import Image, ImageDraw
from pitop import Pitop
# https://en.wikipedia.org/wiki/Maze_generation_algorithm
pitop = Pitop()
miniscreen = pitop.miniscreen
image = Image.new(
miniscreen.mode,
miniscreen.size,
)
canvas = ImageDraw.Draw(image)
miniscreen.set_max_fps(50)
def draw_pixel(pos):
canvas.point(pos, fill=1)
miniscreen.display_image(image)
drawn_pixels.append(pos)
width = (miniscreen.width // 2) * 2 - 1
height = (miniscreen.height // 2) * 2 - 1
while True:
print("Initialising...")
canvas.rectangle(miniscreen.bounding_box, fill=0)
drawn_pixels = list()
complexity = int(random() * (5 * (width + height)))
density = int(random() * ((width // 2) * (height // 2)))
print("Drawing the borders...")
for x in range(width):
draw_pixel((x, 0))
draw_pixel((x, (height // 2) * 2))
for y in range(height):
draw_pixel((0, y))
draw_pixel(((width // 2) * 2, y))
print("Filling the maze...")
for i in range(density):
x, y = randint(0, width // 2) * 2, randint(0, height // 2) * 2
if (x, y) not in drawn_pixels:
draw_pixel((x, y))
for j in range(complexity):
neighbours = []
if x > 1:
neighbours.append((x - 2, y))
if x < width - 3:
neighbours.append((x + 2, y))
if y > 1:
neighbours.append((x, y - 2))
if y < height - 3:
neighbours.append((x, y + 2))
if len(neighbours):
x_, y_ = neighbours[randint(0, len(neighbours) - 1)]
if (x_, y_) not in drawn_pixels:
draw_pixel((x_, y_))
draw_pixel((x_ + (x - x_) // 2, y_ + (y - y_) // 2))
x, y = x_, y_
print("Done!")
sleep(10)
2-Player Pong Game¶
from random import randrange
from time import sleep
from PIL import Image, ImageDraw, ImageFont
from pitop import Pitop
# Game variables
BALL_RADIUS = 2
PADDLE_SIZE = (2, 20)
PADDLE_CTRL_VEL = 4
class Ball:
def __init__(self):
self.pos = [0, 0]
self.vel = [0, 0]
# 50/50 chance of direction
self.init(move_right=randrange(0, 2) == 0)
def init(self, move_right):
self.pos = [miniscreen.width // 2, miniscreen.height // 2]
horz = randrange(1, 3)
vert = randrange(1, 3)
if move_right is False:
horz = -horz
self.vel = [horz, -vert]
@property
def x_pos(self):
return self.pos[0]
@property
def y_pos(self):
return self.pos[1]
def is_aligned_with_paddle_horizontally(self, paddle):
return abs(self.x_pos - paddle.x_pos) <= BALL_RADIUS + PADDLE_SIZE[0] // 2
def is_aligned_with_paddle_vertically(self, paddle):
return abs(self.y_pos - paddle.y_pos) <= BALL_RADIUS + PADDLE_SIZE[1] // 2
def is_touching_paddle(self, paddle):
hor = self.is_aligned_with_paddle_horizontally(paddle)
ver = self.is_aligned_with_paddle_vertically(paddle)
return hor and ver
@property
def is_touching_vertical_walls(self):
return (
self.y_pos <= BALL_RADIUS
or self.y_pos >= miniscreen.height + 1 - BALL_RADIUS
)
def change_direction(self, change_x=False, change_y=False, speed_factor=1.0):
x_vel = -self.vel[0] if change_x else self.vel[0]
self.vel[0] = speed_factor * x_vel
y_vel = -self.vel[1] if change_y else self.vel[1]
self.vel[1] = speed_factor * y_vel
def update(self):
self.pos = [x + y for x, y in zip(self.pos, self.vel)]
if self.is_touching_vertical_walls:
self.change_direction(change_y=True, speed_factor=1.0)
@property
def bounding_box(self):
def get_circle_bounds(center, radius):
x0 = center[0] - radius
y0 = center[1] - radius
x1 = center[0] + radius
y1 = center[1] + radius
return (x0, y0, x1, y1)
return get_circle_bounds(self.pos, BALL_RADIUS)
class Paddle:
def __init__(self, start_pos=[0, 0]):
self.pos = start_pos
self.vel = 0
self.score = 0
def increase_score(self):
self.score += 1
@property
def x_pos(self):
return self.pos[0]
@property
def y_pos(self):
return self.pos[1]
@y_pos.setter
def y_pos(self, new_y):
self.pos[1] = new_y
@property
def touching_top(self):
return self.y_pos - PADDLE_SIZE[1] // 2 <= 0
@property
def touching_bottom(self):
return self.y_pos + PADDLE_SIZE[1] // 2 >= miniscreen.height - 1
def update(self):
moving_down = self.vel > 0
if self.touching_top and not moving_down:
return
if self.touching_bottom and moving_down:
return
self.y_pos += self.vel
if self.touching_top:
self.y_pos = PADDLE_SIZE[1] // 2
if self.touching_bottom:
self.y_pos = miniscreen.height - PADDLE_SIZE[1] // 2 - 1
@property
def bounding_box(self):
return (
self.x_pos,
self.y_pos - PADDLE_SIZE[1] // 2,
self.x_pos,
self.y_pos + PADDLE_SIZE[1] // 2,
)
def update_button_state():
down_pressed = miniscreen.down_button.is_pressed
up_pressed = miniscreen.up_button.is_pressed
select_pressed = miniscreen.select_button.is_pressed
cancel_pressed = miniscreen.cancel_button.is_pressed
if down_pressed == up_pressed:
l_paddle.vel = 0
elif down_pressed:
l_paddle.vel = PADDLE_CTRL_VEL
elif up_pressed:
l_paddle.vel = -PADDLE_CTRL_VEL
if select_pressed == cancel_pressed:
r_paddle.vel = 0
elif select_pressed:
r_paddle.vel = PADDLE_CTRL_VEL
elif cancel_pressed:
r_paddle.vel = -PADDLE_CTRL_VEL
def update_positions():
round_finished = False
l_paddle.update()
r_paddle.update()
ball.update()
paddles = {l_paddle, r_paddle}
for paddle in paddles:
if ball.is_aligned_with_paddle_horizontally(paddle):
if ball.is_touching_paddle(paddle):
ball.change_direction(change_x=True, speed_factor=1.1)
else:
other_paddle = paddles - {paddle}
other_paddle = other_paddle.pop()
other_paddle.increase_score()
ball.init(move_right=other_paddle == r_paddle)
paddle.y_pos = miniscreen.height // 2
other_paddle.y_pos = miniscreen.height // 2
round_finished = True
break
return round_finished
def draw(wait=False):
canvas = ImageDraw.Draw(image)
# Clear screen
canvas.rectangle(miniscreen.bounding_box, fill=0)
# Draw ball
canvas.ellipse(ball.bounding_box, fill=1)
# Draw paddles
canvas.line(l_paddle.bounding_box, fill=1, width=PADDLE_SIZE[0])
canvas.line(r_paddle.bounding_box, fill=1, width=PADDLE_SIZE[0])
# Draw score
font = ImageFont.truetype("VeraMono.ttf", size=12)
canvas.multiline_text(
(1 * miniscreen.width // 3, 2),
str(l_paddle.score),
fill=1,
font=font,
align="center",
)
canvas.multiline_text(
(2 * miniscreen.width // 3, 2),
str(r_paddle.score),
fill=1,
font=font,
align="center",
)
# Display image
miniscreen.display_image(image)
if wait:
sleep(1.5)
# Internal variables
pitop = Pitop()
miniscreen = pitop.miniscreen
miniscreen.set_max_fps(30)
ball = Ball()
l_paddle = Paddle([PADDLE_SIZE[0] // 2 - 1, miniscreen.height // 2])
r_paddle = Paddle([miniscreen.width - 1 - PADDLE_SIZE[0] // 2, miniscreen.height // 2])
image = Image.new(
miniscreen.mode,
miniscreen.size,
)
def main():
while True:
update_button_state()
draw(update_positions())
if __name__ == "__main__":
main()
Class Reference: pi-top [4] Miniscreen¶
-
class
pitop.miniscreen.
Miniscreen
[source]¶ Represents a pi-top [4]’s miniscreen display.
Also owns the surrounding 4 buttons as properties (
up_button
,down_button
,select_button
,cancel_button
). Seepitop.miniscreen.miniscreen.MiniscreenButton
for how to use these buttons.-
bottom_left
¶ Gets the bottom-left corner of the miniscreen display.
Returns: The coordinates of the bottom left of the display’s bounding box as an (x,y) tuple. Return type: tuple
-
bottom_right
¶ Gets the bottom-right corner of the miniscreen display.
Returns: The coordinates of the bottom right of the display’s bounding box as an (x,y) tuple. Return type: tuple
-
bounding_box
¶ Gets the bounding box of the miniscreen display.
Returns: The device’s bounding box as an (top-left x, top-left y, bottom-right x, bottom-right y) tuple. Return type: tuple
Gets the cancel button of the pi-top [4] miniscreen.
Returns: A gpiozero-like button instance representing the cancel button of the pi-top [4] miniscreen. Return type: pitop.miniscreen.miniscreen.MiniscreenButton
-
center
¶ Gets the center of the miniscreen display.
Returns: The coordinates of the center of the display’s bounding box as an (x,y) tuple. Return type: tuple
-
clear
()¶ Clears any content displayed in the miniscreen display.
-
contrast
(new_contrast_value)¶ Sets the contrast value of the miniscreen display to the provided value.
Parameters: new_contrast_value (int) – contrast value to set, between 0 and 255.
-
device
¶ Gets the miniscreen display device instance.
Return type: pitop.miniscreen.oled.core.contrib.luma.oled.device.sh1106
-
display
(force=False)¶ Displays what is on the current canvas to the screen as a single frame.
Warning
This method is deprecated and will be deleted on the next major release of the SDK.
This method does not need to be called when using the other draw functions in this class, but is used when the caller wants to use the canvas object to draw composite objects and then render them to screen in a single frame.
-
display_image
(image, xy=None, invert=False)¶ Render a static image to the screen from a file or URL at a given position.
The image should be provided as a PIL Image object.
Parameters:
-
display_image_file
(file_path_or_url, xy=None, invert=False)¶ Render a static image to the screen from a file or URL at a given position.
The display’s positional properties (e.g. top_left, top_right) can be used to assist with specifying the xy position parameter.
Parameters:
-
display_multiline_text
(text, xy=None, font_size=None, font=None, invert=False, anchor=None, align=None)¶ Renders multi-lined text to the screen at a given position and size. Text that is too long for the screen will automatically wrap to the next line.
The display’s positional properties (e.g. top_left, top_right) can be used to assist with specifying the xy position parameter.
Parameters: - text (string) – The text to render
- xy (tuple) – The position on the screen to render the image. If not provided or passed as None the image will be drawn in the top-left of the screen.
- font_size (int) – The font size in pixels. If not provided or passed as None, the default font size will be used
- font (string) – A filename or path of a TrueType or OpenType font. If not provided or passed as None, the default font will be used
- invert (bool) – Set to True to flip the on/off state of each pixel in the image
- align (str) – PIL ImageDraw alignment to use
- anchor (str) – PIL ImageDraw text anchor to use
-
display_text
(text, xy=None, font_size=None, font=None, invert=False, align=None, anchor=None)¶ Renders a single line of text to the screen at a given position and size.
The display’s positional properties (e.g. top_left, top_right) can be used to assist with specifying the xy position parameter.
Parameters: - text (string) – The text to render
- xy (tuple) – The position on the screen to render the image. If not provided or passed as None the image will be drawn in the top-left of the screen.
- font_size (int) – The font size in pixels. If not provided or passed as None, the default font size will be used
- font (string) – A filename or path of a TrueType or OpenType font. If not provided or passed as None, the default font will be used
- invert (bool) – Set to True to flip the on/off state of each pixel in the image
- align (str) – PIL ImageDraw alignment to use
- anchor (str) – PIL ImageDraw text anchor to use
Gets the down button of the pi-top [4] miniscreen.
Returns: A gpiozero-like button instance representing the down button of the pi-top [4] miniscreen. Return type: pitop.miniscreen.miniscreen.MiniscreenButton
-
draw
()¶ warning:: This method is deprecated in favor of
display_image()
anddisplay_text()
, and will be deleted on the next major release of the SDK.
-
draw_image
(image, xy=None)¶ warning:: This method is deprecated in favor of
display_image()
, and will be deleted on the next major release of the SDK.
-
draw_image_file
(file_path_or_url, xy=None)¶ warning:: This method is deprecated in favor of
display_image_file()
, and will be deleted on the next major release of the SDK.
-
draw_multiline_text
(text, xy=None, font_size=None)¶ warning:: This method is deprecated in favor of
display_multiline_text()
, and will be deleted on the next major release of the SDK.
-
draw_text
(text, xy=None, font_size=None)¶ warning:: This method is deprecated in favor of
display_text()
, and will be deleted on the next major release of the SDK.
-
hide
()¶ The miniscreen display is put into low power mode.
The previously shown image will re-appear when show() is given, even if the internal frame buffer has been changed (so long as display() has not been called).
-
is_active
¶ Determine if the current miniscreen instance is in control of the miniscreen hardware.
Returns: whether the miniscreen instance is in control of the miniscreen hardware. Return type: bool
-
mode
¶
-
play_animated_image
(image, background=False, loop=False)¶ Render an animation or a image to the screen.
Use stop_animated_image() to end a background animation
Parameters:
-
play_animated_image_file
(file_path_or_url, background=False, loop=False)¶ Render an animated image to the screen from a file or URL.
Parameters:
-
prepare_image
(image_to_prepare)¶ Formats the given image into one that can be used directly by the OLED.
Parameters: image_to_prepare ( PIL.Image.Image
) – Image to be formatted.Return type: PIL.Image.Image
-
refresh
()¶
-
reset
(force=True)¶ Gives the caller access to the miniscreen display (i.e. in the case the system is currently rendering information to the screen) and clears the screen.
Gets the select button of the pi-top [4] miniscreen.
Returns: A gpiozero-like button instance representing the select button of the pi-top [4] miniscreen. Return type: pitop.miniscreen.miniscreen.MiniscreenButton
-
set_control_to_hub
()¶ Signals the pi-top hub to take control of the miniscreen display.
-
set_control_to_pi
()¶ Signals the pi-top hub to give control of the miniscreen display to the Raspberry Pi.
-
set_max_fps
(max_fps)¶ Set the maximum frames per second that the miniscreen display can display. This method can be useful to control or limit the speed of animations.
This works by blocking on the OLED’s display methods if called before the amount of time that a frame should last is not exceeded.
Parameters: max_fps (int) – The maximum frames that can be rendered per second
-
should_redisplay
(image_to_display=None)¶ Determines if the miniscreen display needs to be refreshed, based on the provided image. If no image is provided, the content of the display’s deprecated internal canvas property will be used.
Parameters: image_to_display ( PIL.Image.Image
or None) – Image to be displayed.Return type: bool
-
show
()¶ The miniscreen display comes out of low power mode showing the previous image shown before hide() was called (so long as display() has not been called)
-
sleep
()¶ The miniscreen display in set to low contrast mode, without modifying the content of the screen.
-
spi_bus
¶ Gets the SPI bus used by the miniscreen display to receive data as an integer. Setting this property will modify the SPI bus that the OLED uses. You might notice a flicker in the screen.
Parameters: bus (int) – Number of the SPI bus for the OLED to use. Accepted values are 0 or 1.
-
stop_animated_image
()¶ Stop background animation started using start(), if currently running.
-
top_left
¶ Gets the top left corner of the miniscreen display.
Returns: The coordinates of the center of the display’s bounding box as an (x,y) tuple. Return type: tuple
-
top_right
¶ Gets the top-right corner of the miniscreen display.
Returns: The coordinates of the top right of the display’s bounding box as an (x,y) tuple. Return type: tuple
Gets the up button of the pi-top [4] miniscreen.
Returns: A gpiozero-like button instance representing the up button of the pi-top [4] miniscreen. Return type: pitop.miniscreen.miniscreen.MiniscreenButton
-
visible
¶ Gets whether the device is currently in low power state.
Returns: whether the screen is in low power mode Return type: bool
-
wake
()¶ The miniscreen display is set to high contrast mode, without modifying the content of the screen.
-
when_system_controlled
¶ Function to call when user gives back control of the miniscreen to the system.
This is used by pt-miniscreen to update its ‘user-controlled’ application state.
-
when_user_controlled
¶ Function to call when user takes control of the miniscreen.
This is used by pt-miniscreen to update its ‘user-controlled’ application state.
-
Using the Miniscreen’s Buttons¶

The miniscreen’s buttons are simple, and behave in a similar way to the other button-style components in this SDK. Each miniscreen button can be queried for their “is pressed” state, and also invoke callback functions for when pressed and released.
The pitop.miniscreen.Miniscreen
class provides these buttons as properties:
>>> from pitop import Pitop
>>> pitop = Pitop()
>>> miniscreen = pitop.miniscreen
>>> miniscreen.up_button
<pitop.miniscreen.miniscreen.MiniscreenButton object at 0xb3e44e50>
>>> miniscreen.down_button
<pitop.miniscreen.miniscreen.MiniscreenButton object at 0xb3e44d30>
>>> miniscreen.select_button
<pitop.miniscreen.miniscreen.MiniscreenButton object at 0xb3e44e90>
>>> miniscreen.cancel_button
<pitop.miniscreen.miniscreen.MiniscreenButton object at 0xb3e44e70>
Here is an example demonstrating 2 ways to make use of these buttons:
from time import sleep
from pitop import Pitop
pitop = Pitop()
miniscreen = pitop.miniscreen
up = miniscreen.up_button
down = miniscreen.down_button
def do_up_thing():
print("Up button was pressed")
def do_down_thing():
print("Down button was pressed")
def do_another_thing():
print("do_another_thing invoked")
def select_something():
print("select_something called")
# To invoke a function when the button is pressed/released,
# you can assign the function to the 'when_pressed' or 'when_released' data member of a button
print("Configuring miniscreen's up and down button events...")
up.when_pressed = do_up_thing
down.when_pressed = do_down_thing
down.when_released = do_another_thing
# Another way to react to button events is to poll the is_pressed data member
print("Polling for if select button is pressed...")
while True:
if miniscreen.select_button.is_pressed:
select_something()
sleep(0.1)
Class Reference: pi-top [4] Miniscreen Button¶
-
class
pitop.miniscreen.miniscreen.
MiniscreenButton
[source]¶ Represents one of the 4 buttons around the miniscreen display on a pi- top [4].
Should not be created directly - instead, use
pitop.miniscreen.Miniscreen
.-
when_pressed
¶ Get or set the ‘when pressed’ button state callback function. When set, this callback function will be invoked when this event happens.
Parameters: callback (Function) – Callback function to run when a button is pressed.
-
when_released
¶ Get or set the ‘when released’ button state callback function. When set, this callback function will be invoked when this event happens.
Parameters: callback (Function) – Callback function to run when a button is released.
-
API - pi-top Maker Architecture (PMA) Components¶


The Foundation & Expansion Plates and all the parts included in the Foundation & Robotics Kit are known as the pi-top Maker Architecture (PMA).
Each PMA component has a Python class provided for it.
Check out Key Concepts: pi-top Maker Architecture for useful information to get started with using PMA.
Button¶

Note
This is a Digital Component which connects to a Digital Port [D0-D7].
from time import sleep
from pitop import Button
button = Button("D5")
def on_button_pressed():
print("Pressed!")
def on_button_released():
print("Released!")
button.when_pressed = on_button_pressed
button.when_released = on_button_released
while True:
if button.is_pressed is True: # When button is pressed it will return True
print(button.value)
sleep(1)
-
class
pitop.pma.
Button
(port_name, name='button')[source]¶ Encapsulates the behaviour of a push-button.
A push-button is a simple switch mechanism for controlling some aspect of a circuit.
Parameters: - port_name (str) – The ID for the port to which this component is connected
- name (str) – Component name, defaults to button. Used to access this component when added to a
pitop.Pitop
object.
-
active_time
¶ The length of time (in seconds) that the device has been active for. When the device is inactive, this is
None
.
-
close
()[source]¶ Shut down the device and release all associated resources. This method can be called on an already closed device without raising an exception.
This method is primarily intended for interactive use at the command line. It disables the device and releases its pin(s) for use by another device.
You can attempt to do this simply by deleting an object, but unless you’ve cleaned up all references to the object this may not work (even if you’ve cleaned up all references, there’s still no guarantee the garbage collector will actually delete the object at that point). By contrast, the close method provides a means of ensuring that the object is shut down.
For example, if you have a buzzer connected to port D0, but then wish to attach an LED instead:
>>> from pitop import Buzzer, LED >>> bz = Buzzer("D0") >>> bz.on() >>> bz.off() >>> bz.close() >>> led = LED("D0") >>> led.blink()
Device
descendents can also be used as context managers using thewith
statement. For example:>>> from pitop import Buzzer, LED >>> with Buzzer("D0") as bz: ... bz.on() ... >>> with LED("D0") as led: ... led.on() ...
-
closed
¶ Returns
True
if the device is closed (see theclose()
method). Once a device is closed you can no longer use any other methods or properties to control or query the device.
-
config
¶ Returns a dictionary with the set of parameters that can be used to recreate an object.
-
classmethod
from_config
(config_dict)¶ Creates an instance of a Recreatable object with parameters in the provided dictionary.
-
classmethod
from_file
(path)¶ Creates an instance of an object using the JSON file from the provided path.
-
held_time
¶ The length of time (in seconds) that the device has been held for. This is counted from the first execution of the
when_held
event rather than when the device activated, in contrast toactive_time
. If the device is not currently held, this isNone
.
-
hold_repeat
¶ If
True
,when_held
will be executed repeatedly withhold_time
seconds between each invocation.
-
hold_time
¶ The length of time (in seconds) to wait after the device is activated, until executing the
when_held
handler. Ifhold_repeat
is True, this is also the length of time between invocations ofwhen_held
.
-
static
import_class
(module_name, class_name)¶ Imports a class given a module and a class name.
-
inactive_time
¶ The length of time (in seconds) that the device has been inactive for. When the device is active, this is
None
.
-
is_active
¶ Returns
True
if the device is currently active andFalse
otherwise. This property is usually derived fromvalue
. Unlikevalue
, this is always a boolean.
-
is_pressed
¶ Returns
True
if the device is currently active andFalse
otherwise. This property is usually derived fromvalue
. Unlikevalue
, this is always a boolean.
-
own_state
¶ Representation of an object state that will be used to determine the current state of an object.
-
pin
¶ The
Pin
that the device is connected to. This will beNone
if the device has been closed (see theclose()
method). When dealing with GPIO pins, querypin.number
to discover the GPIO pin (in BCM numbering) that the device is connected to.
-
pressed_time
¶ The length of time (in seconds) that the device has been active for. When the device is inactive, this is
None
.
-
print_config
()¶
-
print_state
()¶
-
save_config
(path)¶ Stores the set of parameters to recreate an object in a JSON file.
-
state
¶ Returns a dictionary with the state of the current object and all of its children.
-
value
¶ Returns 1 if the button is currently pressed, and 0 if it is not.
-
wait_for_active
(timeout=None)¶ Pause the script until the device is activated, or the timeout is reached.
Parameters: timeout (float or None) – Number of seconds to wait before proceeding. If this is None
(the default), then wait indefinitely until the device is active.
-
wait_for_inactive
(timeout=None)¶ Pause the script until the device is deactivated, or the timeout is reached.
Parameters: timeout (float or None) – Number of seconds to wait before proceeding. If this is None
(the default), then wait indefinitely until the device is inactive.
-
wait_for_press
(timeout=None)¶ Pause the script until the device is activated, or the timeout is reached.
Parameters: timeout (float or None) – Number of seconds to wait before proceeding. If this is None
(the default), then wait indefinitely until the device is active.
-
wait_for_release
(timeout=None)¶ Pause the script until the device is deactivated, or the timeout is reached.
Parameters: timeout (float or None) – Number of seconds to wait before proceeding. If this is None
(the default), then wait indefinitely until the device is inactive.
-
when_activated
¶ The function to run when the device changes state from inactive to active.
This can be set to a function which accepts no (mandatory) parameters, or a Python function which accepts a single mandatory parameter (with as many optional parameters as you like). If the function accepts a single mandatory parameter, the device that activated it will be passed as that parameter.
Set this property to
None
(the default) to disable the event.
-
when_deactivated
¶ The function to run when the device changes state from active to inactive.
This can be set to a function which accepts no (mandatory) parameters, or a Python function which accepts a single mandatory parameter (with as many optional parameters as you like). If the function accepts a single mandatory parameter, the device that deactivated it will be passed as that parameter.
Set this property to
None
(the default) to disable the event.
-
when_held
¶ The function to run when the device has remained active for
hold_time
seconds.This can be set to a function which accepts no (mandatory) parameters, or a Python function which accepts a single mandatory parameter (with as many optional parameters as you like). If the function accepts a single mandatory parameter, the device that activated will be passed as that parameter.
Set this property to
None
(the default) to disable the event.
-
when_pressed
¶ The function to run when the device changes state from inactive to active.
This can be set to a function which accepts no (mandatory) parameters, or a Python function which accepts a single mandatory parameter (with as many optional parameters as you like). If the function accepts a single mandatory parameter, the device that activated it will be passed as that parameter.
Set this property to
None
(the default) to disable the event.
-
when_released
¶ The function to run when the device changes state from active to inactive.
This can be set to a function which accepts no (mandatory) parameters, or a Python function which accepts a single mandatory parameter (with as many optional parameters as you like). If the function accepts a single mandatory parameter, the device that deactivated it will be passed as that parameter.
Set this property to
None
(the default) to disable the event.
Buzzer¶

Note
This is a Digital Component which connects to a Digital Port [D0-D7].
from time import sleep
from pitop import Buzzer
buzzer = Buzzer("D0")
buzzer.on() # Set buzzer sound on
print(buzzer.value) # Return 1 while the buzzer is on
sleep(2)
buzzer.off() # Set buzzer sound off
print(buzzer.value) # Return 0 while the buzzer is off
sleep(2)
buzzer.toggle() # Swap between on and off states
print(buzzer.value) # Return the current state of the buzzer
sleep(2)
buzzer.off()
-
class
pitop.pma.
Buzzer
(port_name, name='buzzer')[source]¶ Encapsulates the behaviour of a simple buzzer that can be turned on and off.
Parameters: - port_name (str) – The ID for the port to which this component is connected
- name (str) – Component name, defaults to buzzer. Used to access this component when added to a
pitop.Pitop
object.
-
active_high
¶ When
True
, thevalue
property isTrue
when the device’spin
is high. WhenFalse
thevalue
property isTrue
when the device’s pin is low (i.e. the value is inverted).This property can be set after construction; be warned that changing it will invert
value
(i.e. changing this property doesn’t change the device’s pin state - it just changes how that state is interpreted).
-
beep
(on_time=1, off_time=1, n=None, background=True)¶ Make the device turn on and off repeatedly.
Parameters: - on_time (float) – Number of seconds on. Defaults to 1 second.
- off_time (float) – Number of seconds off. Defaults to 1 second.
- n (int or None) – Number of times to blink;
None
(the default) means forever. - background (bool) – If
True
(the default), start a background thread to continue blinking and return immediately. IfFalse
, only return when the blink is finished (warning: the default value of n will result in this method never returning).
-
blink
(on_time=1, off_time=1, n=None, background=True)¶ Make the device turn on and off repeatedly.
Parameters: - on_time (float) – Number of seconds on. Defaults to 1 second.
- off_time (float) – Number of seconds off. Defaults to 1 second.
- n (int or None) – Number of times to blink;
None
(the default) means forever. - background (bool) – If
True
(the default), start a background thread to continue blinking and return immediately. IfFalse
, only return when the blink is finished (warning: the default value of n will result in this method never returning).
-
close
()[source]¶ Shut down the device and release all associated resources. This method can be called on an already closed device without raising an exception.
This method is primarily intended for interactive use at the command line. It disables the device and releases its pin(s) for use by another device.
You can attempt to do this simply by deleting an object, but unless you’ve cleaned up all references to the object this may not work (even if you’ve cleaned up all references, there’s still no guarantee the garbage collector will actually delete the object at that point). By contrast, the close method provides a means of ensuring that the object is shut down.
For example, if you have a buzzer connected to port D0, but then wish to attach an LED instead:
>>> from pitop import Buzzer, LED >>> bz = Buzzer("D0") >>> bz.on() >>> bz.off() >>> bz.close() >>> led = LED("D0") >>> led.blink()
Device
descendents can also be used as context managers using thewith
statement. For example:>>> from pitop import Buzzer, LED >>> with Buzzer("D0") as bz: ... bz.on() ... >>> with LED("D0") as led: ... led.on() ...
-
closed
¶ Returns
True
if the device is closed (see theclose()
method). Once a device is closed you can no longer use any other methods or properties to control or query the device.
-
config
¶ Returns a dictionary with the set of parameters that can be used to recreate an object.
-
classmethod
from_config
(config_dict)¶ Creates an instance of a Recreatable object with parameters in the provided dictionary.
-
classmethod
from_file
(path)¶ Creates an instance of an object using the JSON file from the provided path.
-
static
import_class
(module_name, class_name)¶ Imports a class given a module and a class name.
-
is_active
¶ Returns
True
if the device is currently active andFalse
otherwise. This property is usually derived fromvalue
. Unlikevalue
, this is always a boolean.
-
off
()¶ Turns the device off.
-
on
()¶ Turns the device on.
-
own_state
¶ Representation of an object state that will be used to determine the current state of an object.
-
pin
¶ The
Pin
that the device is connected to. This will beNone
if the device has been closed (see theclose()
method). When dealing with GPIO pins, querypin.number
to discover the GPIO pin (in BCM numbering) that the device is connected to.
-
print_config
()¶
-
print_state
()¶
-
save_config
(path)¶ Stores the set of parameters to recreate an object in a JSON file.
-
source_delay
¶ The delay (measured in seconds) in the loop used to read values from
source
. Defaults to 0.01 seconds which is generally sufficient to keep CPU usage to a minimum while providing adequate responsiveness.
-
state
¶ Returns a dictionary with the state of the current object and all of its children.
-
toggle
()¶ Reverse the state of the device. If it’s on, turn it off; if it’s off, turn it on.
-
value
¶ Returns 1 if the device is currently active and 0 otherwise. Setting this property changes the state of the device.
Encoder Motor¶
Note
This is a Motor Component which connects to a MotorEncoder Port [M0-M3].
from time import sleep
from pitop import BrakingType, EncoderMotor, ForwardDirection
# Setup the motor
motor = EncoderMotor("M0", ForwardDirection.COUNTER_CLOCKWISE)
motor.braking_type = BrakingType.COAST
# Move in both directions
rpm_speed = 100
for _ in range(4):
motor.set_target_rpm(rpm_speed)
sleep(2)
motor.set_target_rpm(-rpm_speed)
sleep(2)
motor.stop()
-
class
pitop.pma.
EncoderMotor
(port_name, forward_direction, braking_type=<BrakingType.BRAKE: 1>, wheel_diameter=0.075, name='encoder_motor')[source]¶ Represents a pi-top motor encoder component.
Note that pi-top motor encoders use a built-in closed-loop control system, that feeds the readings from an encoder sensor to an PID controller. This controller will actively modify the motor’s current to move at the desired speed or position, even if a load is applied to the shaft.
This internal controller is used when moving the motor through
set_target_rpm
orset_target_speed
methods, while using theset_power
method will make the motor work in open-loop, not using the controller.Note
Note that some methods allow to use distance and speed settings in meters and meters per second. These will only make sense when using a wheel attached to the shaft of the motor.
The conversions between angle, rotations and RPM used by the motor to meters and meters/second are performed considering the
wheel_diameter
parameter. This parameter defaults to the diameter of the wheel included with MMK. If a wheel of different dimmensions is attached to the motor, you’ll need to measure it’s diameter, in order for these methods to work properly.Parameters: - port_name (str) – The ID for the port to which this component is connected.
- forward_direction (ForwardDirection) – The type of rotation of the motor shaft that corresponds to forward motion.
- braking_type (BrakingType) – The braking type of the motor. Defaults to coast.
- wheel_diameter (int or float) – The diameter of the wheel attached to the motor.
- name (str) – Component name, defaults to encoder_motor. Used to access this component when added to a
pitop.Pitop
object.
-
backward
(target_speed, distance=0.0)[source]¶ Run the wheel backwards at the desired speed in meters per second.
This method is a simple interface to move the wheel that wraps a call to
set_target_speed
, specifying the back direction.If desired, a
distance
to travel can also be specified in meters, after which the motor will stop. Settingdistance
to 0 will set the motor to run indefinitely until stopped.Note
Note that for this method to move the wheel the expected
distance
, the correctwheel_circumference
value needs to be used.Parameters:
-
braking_type
¶ Returns the type of braking used by the motor when it’s stopping after a movement.
Setting this property will change the way the motor stops a movement:
BrakingType.COAST
will make the motor coast to a halt when stopped.BrakingType.BRAKE
will cause the motor to actively brake when stopped.
Parameters: braking_type (BrakingType) – The braking type of the motor.
-
current_rpm
¶ Returns the actual RPM currently being achieved at the output shaft, measured by the encoder sensor.
This value might differ from the target RPM set through
set_target_rpm
.
-
current_speed
¶ Returns the speed currently being achieved by the motor in meters per second.
This value may differ from the target speed set through
set_target_speed
.
-
distance
¶ Returns the distance the wheel has travelled in meters.
This value depends on the correct
wheel_circumference
value being set.
-
forward
(target_speed, distance=0.0)[source]¶ Run the wheel forward at the desired speed in meters per second.
This method is a simple interface to move the motor that wraps a call to
set_target_speed
, specifying the forward direction.If desired, a
distance
to travel can also be specified in meters, after which the motor will stop. Settingdistance
to 0 will set the motor to run indefinitely until stopped.Note
Note that for this method to move the wheel the expected
distance
, the correctwheel_circumference
value needs to be used.Parameters:
-
forward_direction
¶ Represents the forward direction setting used by the motor.
Setting this property will determine on which direction the motor will turn whenever a movement in a particular direction is requested.
Parameters: forward_direction (ForwardDirection) – The direction that corresponds to forward motion.
-
max_rpm
¶ Returns the approximate maximum RPM capable given the motor and gear ratio.
-
max_speed
¶ The approximate maximum speed possible for the wheel attached to the motor shaft, given the motor specs, gear ratio and wheel circumference.
This value depends on the correct
wheel_circumference
value being set.
-
own_state
¶ Representation of an object state that will be used to determine the current state of an object.
-
power
()[source]¶ Get the current power of the motor.
Returns a value from -1.0 to +1.0, assuming the user is controlling the motor using the
set_power
method (motor is in control mode 0). If this is not the case, returns None.
-
rotation_counter
¶ Returns the total or partial number of rotations performed by the motor shaft.
Rotations will increment when moving forward, and decrement when moving backward. This value is a float with many decimal points of accuracy, so can be used to monitor even very small turns of the output shaft.
-
set_power
(power, direction=<Direction.FORWARD: 1>)[source]¶ Turn the motor on at the power level provided, in the range -1.0 to.
+1.0, where:
- 1.0: motor will turn with full power in the
direction
provided as argument. - 0.0: motor will not move.
- -1.0: motor will turn with full power in the direction contrary to
direction
.
Warning
Setting a
power
value out of range will cause the method to raise an exception.Parameters: - 1.0: motor will turn with full power in the
-
set_target_rpm
(target_rpm, direction=<Direction.FORWARD: 1>, total_rotations=0.0)[source]¶ Run the motor at the specified
target_rpm
RPM.If desired, a number of full or partial rotations can also be set through the
total_rotations
parameter. Once reached, the motor will stop. Settingtotal_rotations
to 0 will set the motor to run indefinitely until stopped.If the desired RPM setting cannot be achieved,
torque_limited
will be set toTrue
and the motor will run at the maximum possible RPM it is capable of for the instantaneous torque. This means that if the torque lowers, then the RPM will continue to rise until it meets the desired level.Care needs to be taken here if you want to drive a vehicle forward in a straight line, as the motors are not guaranteed to spin at the same rate if they are torque-limited.
Warning
Setting a
target_rpm
higher than the maximum allowed will cause the method to throw an exception. To determine what the maximum possible target RPM for the motor is, use themax_rpm
method.Parameters:
-
set_target_speed
(target_speed, direction=<Direction.FORWARD: 1>, distance=0.0)[source]¶ Run the wheel at the specified target speed in meters per second.
If desired, a
distance
to travel can also be specified in meters, after which the motor will stop. Settingdistance
to 0 will set the motor to run indefinitely until stopped.Warning
Setting a
target_speed
higher than the maximum allowed will cause the method to throw an exception. To determine what the maximum possible target speed for the motor is, use themax_speed
method.Note
Note that for this method to move the wheel the expected
distance
, the correctwheel_diameter
value needs to be used.Parameters:
-
target_rpm
()[source]¶ Get the desired RPM of the motor output shaft, assuming the user is controlling the motor using
set_target_rpm
(motor is in control mode 1).If this is not the case, returns None.
-
torque_limited
¶ Check if the actual motor speed or RPM does not match the target speed or RPM.
Returns a boolean value,
True
if the motor is torque- limited andFalse
if it is not.
-
wheel_circumference
¶
-
wheel_diameter
¶ Represents the diameter of the wheel attached to the motor in meters.
This parameter is important if using library functions to measure speed or distance, as these rely on knowing the diameter of the wheel in order to function correctly. Use one of the predefined pi-top wheel and tyre types, or define your own wheel size.
Note
Note the following diameters:
- pi-top MMK Standard Wheel: 0.060.0m
- pi-top MMK Standard Wheel with Rubber Tyre: 0.065m
- pi-top MMK Standard Wheel with tank track: 0.070m
Parameters: wheel_diameter (int or float) – Wheel diameter in meters.
LED¶

Note
This is a Digital Component which connects to a Digital Port [D0-D7].
from time import sleep
from pitop import LED
led = LED("D2")
led.on()
print(led.is_lit)
sleep(1)
led.off()
print(led.is_lit)
sleep(1)
led.toggle()
print(led.is_lit)
sleep(1)
print(led.value) # Returns 1 is the led is on or 0 if the led is off
-
class
pitop.pma.
LED
(port_name, name='led', color=None)[source]¶ Encapsulates the behaviour of an LED.
An LED (Light Emitting Diode) is a simple light source that can be controlled directly.
Parameters: - port_name (str) – The ID for the port to which this component is connected
- name (str) – Component name, defaults to led. Used to access this component when added to a
pitop.Pitop
object.
-
active_high
¶ When
True
, thevalue
property isTrue
when the device’spin
is high. WhenFalse
thevalue
property isTrue
when the device’s pin is low (i.e. the value is inverted).This property can be set after construction; be warned that changing it will invert
value
(i.e. changing this property doesn’t change the device’s pin state - it just changes how that state is interpreted).
-
blink
(on_time=1, off_time=1, n=None, background=True)¶ Make the device turn on and off repeatedly.
Parameters: - on_time (float) – Number of seconds on. Defaults to 1 second.
- off_time (float) – Number of seconds off. Defaults to 1 second.
- n (int or None) – Number of times to blink;
None
(the default) means forever. - background (bool) – If
True
(the default), start a background thread to continue blinking and return immediately. IfFalse
, only return when the blink is finished (warning: the default value of n will result in this method never returning).
-
close
()[source]¶ Shut down the device and release all associated resources. This method can be called on an already closed device without raising an exception.
This method is primarily intended for interactive use at the command line. It disables the device and releases its pin(s) for use by another device.
You can attempt to do this simply by deleting an object, but unless you’ve cleaned up all references to the object this may not work (even if you’ve cleaned up all references, there’s still no guarantee the garbage collector will actually delete the object at that point). By contrast, the close method provides a means of ensuring that the object is shut down.
For example, if you have a buzzer connected to port D0, but then wish to attach an LED instead:
>>> from pitop import Buzzer, LED >>> bz = Buzzer("D0") >>> bz.on() >>> bz.off() >>> bz.close() >>> led = LED("D0") >>> led.blink()
Device
descendents can also be used as context managers using thewith
statement. For example:>>> from pitop import Buzzer, LED >>> with Buzzer("D0") as bz: ... bz.on() ... >>> with LED("D0") as led: ... led.on() ...
-
closed
¶ Returns
True
if the device is closed (see theclose()
method). Once a device is closed you can no longer use any other methods or properties to control or query the device.
-
config
¶ Returns a dictionary with the set of parameters that can be used to recreate an object.
-
classmethod
from_config
(config_dict)¶ Creates an instance of a Recreatable object with parameters in the provided dictionary.
-
classmethod
from_file
(path)¶ Creates an instance of an object using the JSON file from the provided path.
-
static
import_class
(module_name, class_name)¶ Imports a class given a module and a class name.
-
is_active
¶ Returns
True
if the device is currently active andFalse
otherwise. This property is usually derived fromvalue
. Unlikevalue
, this is always a boolean.
-
is_lit
¶ Returns
True
if the device is currently active andFalse
otherwise. This property is usually derived fromvalue
. Unlikevalue
, this is always a boolean.
-
off
()¶ Turns the device off.
-
on
()¶ Turns the device on.
-
own_state
¶ Representation of an object state that will be used to determine the current state of an object.
-
pin
¶ The
Pin
that the device is connected to. This will beNone
if the device has been closed (see theclose()
method). When dealing with GPIO pins, querypin.number
to discover the GPIO pin (in BCM numbering) that the device is connected to.
-
print_config
()¶
-
print_state
()¶
-
save_config
(path)¶ Stores the set of parameters to recreate an object in a JSON file.
-
source_delay
¶ The delay (measured in seconds) in the loop used to read values from
source
. Defaults to 0.01 seconds which is generally sufficient to keep CPU usage to a minimum while providing adequate responsiveness.
-
state
¶ Returns a dictionary with the state of the current object and all of its children.
-
toggle
()¶ Reverse the state of the device. If it’s on, turn it off; if it’s off, turn it on.
-
value
¶ Returns 1 if the device is currently active and 0 otherwise. Setting this property changes the state of the device.
Light Sensor¶

Note
This is a Analog Component which connects to a Analog Port [A0-A3].
from time import sleep
from pitop import LightSensor
light_sensor = LightSensor("A1")
while True:
# Returns a value depending on the amount of light
print(light_sensor.reading)
sleep(0.1)
-
class
pitop.pma.
LightSensor
(port_name, pin_number=1, name='light_sensor', number_of_samples=3)[source]¶ Encapsulates the behaviour of a light sensor module.
A simple analogue photo transistor is used to detect the intensity of the light striking the sensor. The component contains a photoresistor which detects light intensity. The resistance decreases as light intensity increases; thus the brighter the light, the higher the voltage.
Uses an Analog-to-Digital Converter (ADC) to turn the analog reading from the sensor into a digital value.
By default, the sensor uses 3 samples to report a
reading
, which takes around 0.5s. This can be changed by modifying the parameternumber_of_samples
in the constructor.Parameters: - port_name (str) – The ID for the port to which this component is connected
- number_of_samples (str) – Amount of sensor samples used to report a
reading
. Defaults to 3. - name (str) – Component name, defaults to light_sensor. Used to access this component when added to a
pitop.Pitop
object.
-
own_state
¶ Representation of an object state that will be used to determine the current state of an object.
-
reading
¶ Take a reading from the sensor.
Returns: A value representing the amount of light striking the sensor at the current time from 0 to 999. Return type: float
-
value
¶ Get a simple binary value based on a reading from the device.
Returns: 1 if the sensor is detecting any light, 0 otherwise Return type: integer
Potentiometer¶

Note
This is a Analog Component which connects to a Analog Port [A0-A3].
from time import sleep
from pitop import Potentiometer
potentiometer = Potentiometer("A3")
while True:
# Returns the current position of the Potentiometer
print(potentiometer.position)
sleep(0.1)
-
class
pitop.pma.
Potentiometer
(port_name, pin_number=1, name='potentiometer', number_of_samples=1)[source]¶ Encapsulates the behaviour of a potentiometer.
A potentiometer is a three-terminal resistor with a sliding or rotating contact that forms an adjustable voltage divider. The component is used for measuring the electric potential (voltage) between the two ‘end’ terminals. If only two of the terminals are used, one end and the wiper, it acts as a variable resistor or rheostat. Potentiometers are commonly used to control electrical devices such as volume controls on audio equipment.
Uses an Analog-to-Digital Converter (ADC) to turn the analog reading from the sensor into a digital value.
Parameters: - port_name (str) – The ID for the port to which this component is connected
- number_of_samples (str) – Amount of sensor samples used to report a
position
. Defaults to 1. - name (str) – Component name, defaults to potentiometer. Used to access this component when added to a
pitop.Pitop
object.
-
own_state
¶ Representation of an object state that will be used to determine the current state of an object.
-
position
¶ Get the current reading from the sensor.
Returns: A value representing the potential difference (voltage) from 0 to 999. Return type: float
-
value
¶ Get a simple binary value based on a reading from the device.
Returns: 1 if the sensor is detecting a potential difference (voltage), 0 otherwise Return type: integer
Servo Motor¶
Note
This is a Motor Component which connects to a ServoMotor Port [S0-S3].
from time import sleep
from pitop import ServoMotor, ServoMotorSetting
servo = ServoMotor("S0")
# Scan back and forward across a 180 degree angle range in 30 degree hops using default servo speed
for angle in range(90, -100, -30):
print("Setting angle to", angle)
servo.target_angle = angle
sleep(0.5)
# you can also set angle with a different speed than the default
servo_settings = ServoMotorSetting()
servo_settings.speed = 25
for angle in range(-90, 100, 30):
print("Setting angle to", angle)
servo_settings.angle = angle
servo.setting = servo_settings
sleep(0.5)
sleep(1)
# Scan back and forward displaying current angle and speed
STOP_ANGLE = 80
TARGET_SPEED = 40
print("Sweeping using speed ", -TARGET_SPEED)
servo.target_speed = -TARGET_SPEED
current_state = servo.setting
current_angle = current_state.angle
# sweep using the already set servo speed
servo.sweep()
while current_angle > -STOP_ANGLE:
current_state = servo.setting
current_angle = current_state.angle
current_speed = current_state.speed
print(f"current_angle: {current_angle} | current_speed: {current_speed}")
sleep(0.05)
print("Sweeping using speed ", TARGET_SPEED)
# you can also sweep specifying the speed when calling the sweep method
servo.sweep(speed=TARGET_SPEED)
while current_angle < STOP_ANGLE:
current_state = servo.setting
current_angle = current_state.angle
current_speed = current_state.speed
print(f"current_angle: {current_angle} | current_speed: {current_speed}")
sleep(0.05)
-
class
pitop.pma.
ServoMotor
(port_name, zero_point=0, name='servo')[source]¶ Represents a pi-top servo motor component.
Note that pi-top servo motors use an open-loop control system. As such, the output of the device (e.g. the angle and speed of the servo horn) cannot be measured directly. This means that you can set a target angle or speed for the servo, but you cannot read the current angle or speed.
Parameters: - port_name (str) – The ID for the port to which this component is connected.
- zero_point (int) – A user-defined offset from ‘true’ zero.
- name (str) – Component name, defaults to servo. Used to access this component when added to a
pitop.Pitop
object.
-
angle_range
¶ Returns a tuple with minimum and maximum possible angles where the servo horn can be moved to.
If
zero_point
is set to 0 (default), the angle range will be (-90, 90).
-
current_angle
¶ Returns the current angle that the servo motor is at.
Note
If you need synchronized angle and speed values, use
ServoMotor.state()
instead, this will return both current angle and current speed at the same time.Returns: float value of the current angle of the servo motor in degrees.
-
current_speed
¶ Returns the current speed the servo motor is at.
Note
If you need synchronized angle and speed values, use
ServoMotor.state()
instead, this will return both current angle and current speed at the same time.Returns: float value of the current speed of the servo motor in deg/s.
-
own_state
¶ Representation of an object state that will be used to determine the current state of an object.
-
setting
¶ Returns the current state of the servo motor, giving current angle and current speed.
Returns: :class:’ServoMotorSetting` object that has angle and speed attributes.
-
smooth_acceleration
¶ Gets whether or not the servo is configured to use a linear acceleration profile to ramp speed at start and end of cycle.
Returns: boolean value of the acceleration mode
-
sweep
(speed=None)[source]¶ Moves the servo horn from the current position to one of the servo motor limits (maximum/minimum possible angle), moving at the specified speed. The speed value must be a number from -100.0 to 100.0 deg/s.
The sweep direction is given by the speed.
Setting a
speed
value higher than zero will move the horn to the maximum angle (90 degrees by default), while a value less than zero will move it to the minimum angle (-90 degress by default).Warning
Using a
speed
out of the valid speed range will cause the method to raise an exception.Parameters: speed (int or float) – The target speed at which to move the servo horn, from -100 to 100 deg/s.
-
target_angle
¶ Returns the last target angle that has been set.
Returns: float value of the target angle of the servo motor in deg.
-
target_speed
¶ Returns the last target speed that has been set.
Returns: float value of the target speed of the servo motor in deg/s.
-
zero_point
¶ Represents the servo motor angle that the library treats as ‘zero’. This value can be anywhere in the range of -90 to +90.
For example, if the zero_point were set to be -30, then the valid range of values for setting the angle would be -60 to +120.
Warning
Setting a zero point out of the range of -90 to 90 will cause the method to raise an exception.
Sound Sensor¶

Note
This is a Analog Component which connects to a Analog Port [A0-A3].
from time import sleep
from pitop import SoundSensor
sound_sensor = SoundSensor("A2")
while True:
# Returns reading the amount of sound in the room
print(sound_sensor.reading)
sleep(0.1)
-
class
pitop.pma.
SoundSensor
(port_name, pin_number=1, name='sound_sensor', number_of_samples=1)[source]¶ Encapsulates the behaviour of a sound sensor.
A sound sensor component is typically a simple microphone that detects the vibrations of the air entering the sensor and produces an analog reading based on the amplitude of these vibrations.
Uses an Analog-to-Digital Converter (ADC) to turn the analog reading from the sensor into a digital value.
Parameters: - port_name (str) – The ID for the port to which this component is connected
- number_of_samples (str) – Amount of sensor samples used to report a
reading
. Defaults to 1. - name (str) – Component name, defaults to sound_sensor. Used to access this component when added to a
pitop.Pitop
object.
-
own_state
¶ Representation of an object state that will be used to determine the current state of an object.
-
reading
¶ Take a reading from the sensor. Uses a builtin peak detection system to retrieve the sound level.
Returns: A value representing the volume of sound detected by the sensor at the current time from 0 to 500. Return type: float
-
value
¶ Get a simple binary value based on a reading from the device.
Returns: 1 if the sensor is detecting any sound, 0 otherwise Return type: integer
Ultrasonic Sensor¶

Note
This is a Digital Component which connects to a Digital Port [D0-D7].
from time import sleep
from pitop import UltrasonicSensor
distance_sensor = UltrasonicSensor("D3", threshold_distance=0.2)
# Set up functions to print when an object crosses 'threshold_distance'
distance_sensor.when_in_range = lambda: print("in range")
distance_sensor.when_out_of_range = lambda: print("out of range")
while True:
# Print the distance (in meters) to an object in front of the sensor
print(distance_sensor.distance)
sleep(0.1)
-
class
pitop.pma.
UltrasonicSensor
(port_name, queue_len=5, max_distance=3, threshold_distance=0.3, partial=False, name='ultrasonic')[source]¶ -
close
()[source]¶ Shut down the device and release all associated resources. This method can be called on an already closed device without raising an exception.
This method is primarily intended for interactive use at the command line. It disables the device and releases its pin(s) for use by another device.
You can attempt to do this simply by deleting an object, but unless you’ve cleaned up all references to the object this may not work (even if you’ve cleaned up all references, there’s still no guarantee the garbage collector will actually delete the object at that point). By contrast, the close method provides a means of ensuring that the object is shut down.
For example, if you have a buzzer connected to port D0, but then wish to attach an LED instead:
>>> from pitop import Buzzer, LED >>> bz = Buzzer("D0") >>> bz.on() >>> bz.off() >>> bz.close() >>> led = LED("D0") >>> led.blink()
Device
descendents can also be used as context managers using thewith
statement. For example:>>> from pitop import Buzzer, LED >>> with Buzzer("D0") as bz: ... bz.on() ... >>> with LED("D0") as led: ... led.on() ...
-
distance
¶ Returns the current distance measured by the sensor in meters.
Note that this property will have a value between 0 and
max_distance
.
-
in_range
¶
-
max_distance
¶ The maximum distance that the sensor will measure in meters.
This value is specified in the constructor and is used to provide the scaling for the
value
attribute. Whendistance
is equal tomax_distance
,value
will be 1.
-
own_state
¶ Representation of an object state that will be used to determine the current state of an object.
-
pin
¶
-
threshold_distance
¶ The distance, measured in meters, that will trigger the
when_in_range
andwhen_out_of_range
events when crossed. This is simply a meter-scaled variant of the usualthreshold
attribute.
-
value
¶ Returns a value between 0, indicating that something is either touching the sensor or is sufficiently near that the sensor can’t tell the difference, and 1, indicating that something is at or beyond the specified max_distance.
-
when_in_range
¶
-
when_out_of_range
¶
-
API - pi-top Peripheral Devices¶
pi-topPROTO+¶

This module provides 2 classes - a simple way to use a pi-topPROTO+’s onboard ADC (analog-to-digital converter), and another to use it as a distance sensor.
These classes will work with original pi-top, pi-topCEED and pi-top [3]. pi-top [4] does not support the pi-topPROTO+’s modular rail connector, and so will not work.
Using the pi-topPROTO+ as a Distance Sensor¶
from time import sleep
from pitop.protoplus import DistanceSensor
ultrasonic = DistanceSensor()
while True:
print(ultrasonic.distance)
sleep(1)
Class Reference: pi-topPROTO+ Distance Sensor¶
-
class
pitop.protoplus.sensors.
DistanceSensor
(trigger_gpio_pin=23, echo_gpio_pin=27)[source]¶ Encapsulates the behaviour of a simple DistanceSensor that can be turned on and off.
Parameters: -
close
()[source]¶ Shut down the device and release all associated resources. This method can be called on an already closed device without raising an exception.
This method is primarily intended for interactive use at the command line. It disables the device and releases its pin(s) for use by another device.
You can attempt to do this simply by deleting an object, but unless you’ve cleaned up all references to the object this may not work (even if you’ve cleaned up all references, there’s still no guarantee the garbage collector will actually delete the object at that point). By contrast, the close method provides a means of ensuring that the object is shut down.
For example, if you have a buzzer connected to port D0, but then wish to attach an LED instead:
>>> from pitop import Buzzer, LED >>> bz = Buzzer("D0") >>> bz.on() >>> bz.off() >>> bz.close() >>> led = LED("D0") >>> led.blink()
Device
descendents can also be used as context managers using thewith
statement. For example:>>> from pitop import Buzzer, LED >>> with Buzzer("D0") as bz: ... bz.on() ... >>> with LED("D0") as led: ... led.on() ...
-
raw_distance
¶
-
Using the pi-topPROTO+’s onboard ADC¶
from time import sleep
from pitop.protoplus import ADCProbe
temp_sensor = ADCProbe()
while True:
print(temp_sensor.read_value(1))
sleep(0.5)
pi-topPULSE¶

This module provides a simple way to use a pi-topPULSE, and will work with any Raspberry Pi and/or pi-top.
The hardware representation of each color is 5 bits (i.e. only 32 different values). Without gamma correction, this would mean the actual color value changes only every 8th color intensity value. This module applies gamma correction, which means that pixels with seemingly different intensities actually have the same.
Using the pi-topPULSE’s microphone¶
from time import sleep
from pitop.pulse import ledmatrix, microphone
def set_bit_rate_to_unsigned_8():
print("Setting bit rate to 8...")
microphone.set_bit_rate_to_unsigned_8()
def set_bit_rate_to_signed_16():
print("Setting bit rate to 16...")
microphone.set_bit_rate_to_signed_16()
def set_sample_rate_to_16khz():
print("Setting sample rate to 16KHz...")
microphone.set_sample_rate_to_16khz()
def set_sample_rate_to_22khz():
print("Setting sample rate to 22KHz...")
microphone.set_sample_rate_to_22khz()
def pause(length):
ledmatrix.off()
sleep(length)
def record(record_time, output_file, pause_time=1):
print("Recording audio for " + str(record_time) + "s...")
ledmatrix.set_all(255, 0, 0)
ledmatrix.show()
microphone.record()
sleep(record_time)
microphone.stop()
ledmatrix.off()
microphone.save(output_file, True)
print("Saved to " + output_file)
print("")
pause(pause_time)
set_sample_rate_to_22khz()
set_bit_rate_to_unsigned_8()
record(5, "/tmp/test22-8.wav")
set_bit_rate_to_signed_16()
record(5, "/tmp/test22-16.wav")
set_sample_rate_to_16khz()
set_bit_rate_to_unsigned_8()
record(5, "/tmp/test16-8.wav")
set_bit_rate_to_signed_16()
record(5, "/tmp/test16-16.wav")
Using the pi-topPULSE’s LED matrix: Test colors¶
import time
from pitop.pulse import ledmatrix
def show_map(r, g, b):
for x in range(0, 7):
for y in range(0, 7):
z = (float(y) + 7.0 * float(x)) / 49.0
rr = int(z * r)
gg = int(z * g)
bb = int(z * b)
ledmatrix.set_pixel(x, y, rr, gg, bb)
ledmatrix.show()
ledmatrix.rotation(0)
ledmatrix.clear()
# Display 49 different color intensities
for r in range(0, 2):
for g in range(0, 2):
for b in range(2):
if r + g + b > 0:
rr = 255 * r
gg = 255 * g
bb = 255 * b
print(rr, gg, bb)
show_map(rr, gg, bb)
time.sleep(5)
ledmatrix.clear()
ledmatrix.show()
Using the pi-topPULSE’s LED matrix: Fancy Light Show!¶
import colorsys
import math
from pitop.pulse import ledmatrix
s_width, s_height = ledmatrix.get_shape()
# twisty swirly goodness
def swirl(x, y, step):
x -= s_width / 2
y -= s_height / 2
dist = math.sqrt(pow(x, 2) + pow(y, 2)) / 2.0
angle = (step / 10.0) + (dist * 1.5)
s = math.sin(angle)
c = math.cos(angle)
xs = x * c - y * s
ys = x * s + y * c
r = abs(xs + ys)
r = r * 64.0
r -= 20
return (r, r + (s * 130), r + (c * 130))
# roto-zooming checker board
def checker(x, y, step):
x -= s_width / 2
y -= s_height / 2
angle = step / 10.0
s = math.sin(angle)
c = math.cos(angle)
xs = x * c - y * s
ys = x * s + y * c
xs -= math.sin(step / 200.0) * 40.0
ys -= math.cos(step / 200.0) * 40.0
scale = step % 20
scale /= 20
scale = (math.sin(step / 50.0) / 8.0) + 0.25
xs *= scale
ys *= scale
xo = abs(xs) - int(abs(xs))
yo = abs(ys) - int(abs(ys))
val = (
0
if (math.floor(xs) + math.floor(ys)) % 2
else 1
if xo > 0.1 and yo > 0.1
else 0.5
)
r, g, b = colorsys.hsv_to_rgb((step % 255) / 255.0, 1, val)
return (r * 255, g * 255, b * 255)
# weeee waaaah
def blues_and_twos(x, y, step):
x -= s_width / 2
y -= s_height / 2
scale = math.sin(step / 6.0) / 1.5
r = math.sin((x * scale) / 1.0) + math.cos((y * scale) / 1.0)
b = math.sin(x * scale / 2.0) + math.cos(y * scale / 2.0)
g = r - 0.8
g = 0 if g < 0 else g
b -= r
b /= 1.4
return (r * 255, (b + g) * 255, g * 255)
# rainbow search spotlights
def rainbow_search(x, y, step):
xs = math.sin((step) / 100.0) * 20.0
ys = math.cos((step) / 100.0) * 20.0
scale = ((math.sin(step / 60.0) + 1.0) / 5.0) + 0.2
r = math.sin((x + xs) * scale) + math.cos((y + xs) * scale)
g = math.sin((x + xs) * scale) + math.cos((y + ys) * scale)
b = math.sin((x + ys) * scale) + math.cos((y + ys) * scale)
return (r * 255, g * 255, b * 255)
# zoom tunnel
def tunnel(x, y, step):
speed = step / 100.0
x -= s_width / 2
y -= s_height / 2
xo = math.sin(step / 27.0) * 2
yo = math.cos(step / 18.0) * 2
x += xo
y += yo
if y == 0:
if x < 0:
angle = -(math.pi / 2)
else:
angle = math.pi / 2
else:
angle = math.atan(x / y)
if y > 0:
angle += math.pi
angle /= 2 * math.pi # convert angle to 0...1 range
shade = math.sqrt(math.pow(x, 2) + math.pow(y, 2)) / 2.1
shade = 1 if shade > 1 else shade
angle += speed
depth = speed + (math.sqrt(math.pow(x, 2) + math.pow(y, 2)) / 10)
col1 = colorsys.hsv_to_rgb((step % 255) / 255.0, 1, 0.8)
col2 = colorsys.hsv_to_rgb((step % 255) / 255.0, 1, 0.3)
col = col1 if int(abs(angle * 6.0)) % 2 == 0 else col2
td = 0.3 if int(abs(depth * 3.0)) % 2 == 0 else 0
col = (col[0] + td, col[1] + td, col[2] + td)
col = (col[0] * shade, col[1] * shade, col[2] * shade)
return (col[0] * 255, col[1] * 255, col[2] * 255)
effects = [tunnel, rainbow_search, checker, swirl]
step = 0
while True:
for i in range(500):
for y in range(s_height):
for x in range(s_width):
r, g, b = effects[0](x, y, step)
if i > 400:
r2, g2, b2 = effects[-1](x, y, step)
ratio = (500.00 - i) / 100.0
r = r * ratio + r2 * (1.0 - ratio)
g = g * ratio + g2 * (1.0 - ratio)
b = b * ratio + b2 * (1.0 - ratio)
r = int(max(0, min(255, r)))
g = int(max(0, min(255, g)))
b = int(max(0, min(255, b)))
ledmatrix.set_pixel(x, y, r, g, b)
step += 1
ledmatrix.show()
effect = effects.pop()
effects.insert(0, effect)
Using the pi-topPULSE’s LED matrix: Showing CPU temperature¶
import time
from pitop.pulse import ledmatrix
def getCpuTemperature():
tempFile = open("/sys/class/thermal/thermal_zone0/temp")
cpu_temp = tempFile.read()
tempFile.close()
return int(int(cpu_temp) / 1000)
OFFSET_LEFT = 0
OFFSET_TOP = 2
# fmt: off
NUMS = [1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, # 0
0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, # 1
1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, # 2
1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, # 3
1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, # 4
1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, # 5
1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, # 6
1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, # 7
1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, # 8
1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1] # 9
# fmt: on
# Displays a single digit (0-9)
def show_digit(val, xd, yd, r, g, b):
offset = val * 15
for p in range(offset, offset + 15):
xt = p % 3
yt = (p - offset) // 3
ledmatrix.set_pixel(xt + xd, 7 - yt - yd, r * NUMS[p], g * NUMS[p], b * NUMS[p])
ledmatrix.show()
# Displays a two-digits positive number (0-99)
def show_number(val, r, g, b):
abs_val = abs(val)
tens = abs_val // 10
units = abs_val % 10
if abs_val > 9:
show_digit(tens, OFFSET_LEFT, OFFSET_TOP, r, g, b)
show_digit(units, OFFSET_LEFT + 4, OFFSET_TOP, r, g, b)
###########################################################
# MAIN
###########################################################
ledmatrix.rotation(0)
ledmatrix.clear()
lastTemperature = -1
try:
while True:
temperature = getCpuTemperature()
if temperature != lastTemperature:
if temperature < 60:
show_number(temperature, 0, 255, 0)
elif temperature < 70:
show_number(temperature, 255, 255, 0)
else:
show_number(temperature, 255, 0, 0)
lastemperature = temperature
time.sleep(2)
except KeyboardInterrupt:
ledmatrix.clear()
ledmatrix.show()
Using the pi-topPULSE’s LED matrix: Showing CPU usage¶
import time
from pitop.pulse import ledmatrix
last_work = [0, 0, 0, 0]
last_idle = [0, 0, 0, 0]
def get_cpu_rates():
global last_work, last_idle
rate = [0, 0, 0, 0]
f = open("/proc/stat", "r")
line = ""
for i in range(0, 4):
while not "cpu" + str(i) in line:
line = f.readline()
# print(line)
splitline = line.split()
work = int(splitline[1]) + int(splitline[2]) + int(splitline[3])
idle = int(splitline[4])
diff_work = work - last_work[i]
diff_idle = idle - last_idle[i]
rate[i] = float(diff_work) / float(diff_idle + diff_work)
last_work[i] = work
last_idle[i] = idle
f.close()
return rate
ledmatrix.rotation(0)
try:
while True:
rate = get_cpu_rates()
ledmatrix.clear()
for i in range(0, 4):
level = int(6.99 * rate[i])
if level < 4:
r = 0
g = 255
b = 0
elif level < 6:
r = 255
g = 255
b = 6
else:
r = 255
g = 0
b = 0
for y in range(0, level + 1):
ledmatrix.set_pixel(2 * i, y, r, g, b)
ledmatrix.show()
time.sleep(1)
except KeyboardInterrupt:
ledmatrix.clear()
ledmatrix.show()
Module Reference: pi-topPULSE Configuration¶
-
pitop.pulse.configuration.
microphone_sample_rate_is_16khz
()[source]¶ Get whether the microphone is set to record at a sample rate of 16,000Hz.
-
pitop.pulse.configuration.
microphone_sample_rate_is_22khz
()[source]¶ Get whether the microphone is set to record at a sample rate of 22,050Hz.
-
pitop.pulse.configuration.
set_microphone_sample_rate_to_16khz
()[source]¶ Set the appropriate I2C bits to enable 16,000Hz recording on the microphone.
Module Reference: pi-topPULSE LED Matrix¶
-
pitop.pulse.ledmatrix.
brightness
(new_brightness)[source]¶ Set the display brightness between 0.0 and 1.0.
Parameters: new_brightness – Brightness from 0.0 to 1.0 (default 1.0)
-
pitop.pulse.ledmatrix.
get_brightness
()[source]¶ Get the display brightness value.
Returns a float between 0.0 and 1.0.
-
pitop.pulse.ledmatrix.
get_pixel
(x, y)[source]¶ Get the RGB value of a single pixel.
Parameters: - x – Horizontal position from 0 to 7
- y – Veritcal position from 0 to 7
-
pitop.pulse.ledmatrix.
rotation
(new_rotation=0)[source]¶ Set the display rotation.
Parameters: new_rotation – Specify the rotation in degrees: 0, 90, 180 or 270
-
pitop.pulse.ledmatrix.
run_tests
()[source]¶ Runs a series of tests to check the LED board is working as expected.
-
pitop.pulse.ledmatrix.
set_pixel
(x, y, r, g, b)[source]¶ Set a single pixel to RGB color.
Parameters: - x – Horizontal position from 0 to 7
- y – Veritcal position from 0 to 7
- r – Amount of red from 0 to 255
- g – Amount of green from 0 to 255
- b – Amount of blue from 0 to 255
Module Reference: pi-topPULSE Microphone¶
-
pitop.pulse.microphone.
is_recording
()[source]¶ Returns recording state of the pi-topPULSE microphone.
-
pitop.pulse.microphone.
set_bit_rate_to_signed_16
()[source]¶ Set bitrate to double that of device default by scaling the signal.
-
pitop.pulse.microphone.
set_sample_rate_to_16khz
()[source]¶ Set the appropriate I2C bits to enable 16,000Hz recording on the microphone.
Advanced: EEPROM¶
The pi-topPULSE contains an EEPROM which was programmed using this settings file. during factory production.
See the Raspberry Pi Foundation’s HAT Github repository for more information.
API - System Peripheral Devices¶
The pi-top Python SDK provides classes which represent devices, including some that can be used by generic devices, such as USB cameras. These classes are intended to simplify using these common system peripheral devices.
USB Camera¶
This class provides an easy way to:
- save image and video files
- directly access camera frames
- process frames in the background (via callback)
It is easy to make use of some pre-written video processors, such as motion detection.
It is also possible to make use of this class to read frames from a directory of images, removing the need for a stream of images from physical hardware. This can be useful for testing, or simulating a real camera.
from time import sleep
from pitop import Camera
# Record a 10s video to ~/Camera/
cam = Camera()
cam.start_video_capture()
sleep(10)
cam.stop_video_capture()
By default, camera frames are of PIL.Image.Image
type (using the Pillow module),
which provides a standardized way of working with the image.
These Image objects use raw, RGB-ordered pixels.
It is also possible to use OpenCV standard format, if desired. This may be useful if you are
intending to do your own image processing with OpenCV. The OpenCV format uses raw, BGR-ordered
pixels in a NumPy numpy.ndarray
object. This can be done by setting the camera’s format
property to “OpenCV”:
from pitop import Camera
c = Camera()
c.format = "OpenCV"
This can be also be done by passing the format to the camera’s constructor:
from pitop import Camera
c = Camera(format="OpenCV")
Using a USB Camera to Access Image Data¶
from pitop import Camera
cam = Camera()
while True:
image = cam.get_frame()
print(image.getpixel((0, 0)))
Using a USB Camera to Capture Video¶
from time import sleep
from pitop import Camera
# Record a 10s video to ~/Camera/
cam = Camera()
cam.start_video_capture()
sleep(10)
cam.stop_video_capture()
Adding Motion Detection to a USB Camera¶
from datetime import datetime
from time import localtime, sleep, strftime
from pitop import Camera
# Example code for Camera
# Records videos of any motion captured by the camera
cam = Camera()
last_motion_detected = None
def motion_detected():
global last_motion_detected
last_motion_detected = datetime.now().timestamp()
if cam.is_recording() is False:
print("Motion detected! Starting recording...")
output_file_name = f"/home/pi/Desktop/My Motion Recording {strftime('%Y-%m-%d %H:%M:%S', localtime(last_motion_detected))}.avi"
cam.start_video_capture(output_file_name=output_file_name)
while (datetime.now().timestamp() - last_motion_detected) < 3:
sleep(1)
cam.stop_video_capture()
print(f"Recording completed - saved to {output_file_name}")
print("Motion detector starting...")
cam.start_detecting_motion(
callback_on_motion=motion_detected, moving_object_minimum_area=350
)
sleep(60)
cam.stop_detecting_motion()
print("Motion detector stopped")
Processing Camera Frame¶
from PIL import ImageDraw
from pitop import Camera
cam = Camera()
def draw_red_cross_over_image(im):
# Use Pillow to draw a red cross over the image
draw = ImageDraw.Draw(im)
draw.line((0, 0) + im.size, fill=128, width=5)
draw.line((0, im.size[1], im.size[0], 0), fill=128, width=5)
return im
im = draw_red_cross_over_image(cam.get_frame())
im.show()
Processing Camera Frame Stream with OpenCV (Convert to grayscale)¶
from time import sleep
import cv2
from pitop import Camera
cam = Camera(format="OpenCV")
def show_gray_image(image):
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
cv2.imshow("frame", gray)
cv2.waitKey(1) # Necessary to show image
# Use callback function for 60s
cam.on_frame = show_gray_image
sleep(60)
# Use get_frame indefinitely
try:
while True:
show_gray_image(cam.get_frame())
except KeyboardInterrupt:
cv2.destroyAllWindows()
Ball Color Detection with OpenCV¶
from signal import pause
import cv2
from pitop.camera import Camera
from pitop.processing.algorithms import BallDetector
def process_frame(frame):
detected_balls = ball_detector(frame, color=["red", "green", "blue"])
red_ball = detected_balls.red
if red_ball.found:
print(f"Red ball center: {red_ball.center}")
print(f"Red ball radius: {red_ball.radius}")
print(f"Red ball angle: {red_ball.angle}")
print()
green_ball = detected_balls.green
if green_ball.found:
print(f"Green ball center: {green_ball.center}")
print(f"Green ball radius: {green_ball.radius}")
print(f"Green ball angle: {green_ball.angle}")
print()
blue_ball = detected_balls.blue
if blue_ball.found:
print(f"Blue ball center: {blue_ball.center}")
print(f"Blue ball radius: {blue_ball.radius}")
print(f"Blue ball angle: {blue_ball.angle}")
print()
cv2.imshow("Image", detected_balls.robot_view)
cv2.waitKey(1)
ball_detector = BallDetector()
camera = Camera(resolution=(640, 480))
camera.on_frame = process_frame
pause()
Class Reference: USB Camera¶
-
class
pitop.camera.
Camera
(index=None, resolution=(640, 480), camera_type=<CameraTypes.USB_CAMERA: 0>, path_to_images='', format='PIL', flip_top_bottom: bool = False, flip_left_right: bool = False, rotate_angle=0, name='camera')[source]¶ Provides a variety of high-level functionality for using the PMA USB Camera, including capturing images and video, and processing image data from the camera.
Parameters: index (int) – ID of the video capturing device to open. Passing None will cause the backend to autodetect the available video capture devices and attempt to use them. -
capture_image
(output_file_name='')[source]¶ Capture a single frame image to file.
Note
If no
output_file_name
argument is provided, images will be stored in ~/Camera.Parameters: output_file_name (str) – The filename into which to write the image.
-
current_frame
(format=None)[source]¶ Returns the latest frame captured by the camera. This method is non- blocking and can return the same frame multiple times.
By default the returned image is formatted as a
PIL.Image.Image
.Parameters: format (string) – DEPRECATED. Set ‘camera.format’ directly, and call this function directly instead.
-
format
¶
-
classmethod
from_file_system
(path_to_images: str)[source]¶ Alternative classmethod to create an instance of a
Camera
object using aFileSystemCamera
-
classmethod
from_usb
(index=None)[source]¶ Alternative classmethod to create an instance of a
Camera
object using aUsbCamera
-
get_frame
(format=None)[source]¶ Returns the next frame captured by the camera. This method blocks until a new frame is available.
Parameters: format (string) – DEPRECATED. Set ‘camera.format’ directly, and call this function directly instead.
-
own_state
¶ Representation of an object state that will be used to determine the current state of an object.
-
start_detecting_motion
(callback_on_motion, moving_object_minimum_area=300)[source]¶ Begin processing image data from the camera, attempting to detect motion. When motion is detected, call the function passed in.
Warning
The callback function can take either no arguments or only one, which will be used to provide the image back to the user when motion is detected. If a callback with another signature is received, the method will raise an exception.
Parameters: - callback_on_motion (function) – A callback function that will be called when motion is detected.
- moving_object_minimum_area (int) – The sensitivity of the motion detection, measured as the area of pixels changing between frames that constitutes motion.
-
start_handling_frames
(callback_on_frame, frame_interval=1, format=None)[source]¶ Begin calling the passed callback with each new frame, allowing for custom processing.
Warning
The callback function can take either no arguments or only one, which will be used to provide the image back to the user. If a callback with another signature is received, the method will raise an exception.
Parameters: - callback_on_frame (function) – A callback function that will be called every
frame_interval
camera frames. - frame_interval (int) – The callback will run every frame_interval frames, decreasing the frame rate of processing. Defaults to 1.
- format (string) – DEPRECATED. Set ‘camera.format’ directly, and call this function directly instead.
- callback_on_frame (function) – A callback function that will be called every
-
start_video_capture
(output_file_name='', fps=20.0, resolution=None)[source]¶ Begin capturing video from the camera.
Note
If no
output_file_name
argument is provided, video will be stored in ~/Camera.Parameters:
-
stop_detecting_motion
()[source]¶ Stop running the motion detection processing.
Does nothing unless
start_detecting_motion
has been called.
-
stop_handling_frames
()[source]¶ Stops handling camera frames.
Does nothing unless
start_handling_frames
has been called.
-
stop_video_capture
()[source]¶ Stop capturing video from the camera.
Does nothing unless
start_video_capture
has been called.
-
Keyboard Button¶
This class makes it easy to handle a keyboard button in the same way as a GPIO-based button.
You can listen for any standard keyboard key input. For example, using a
or A
will provide the ability to ‘listen’ for the A-key being pressed - with or without shift.
Warning
This class depends on pynput, which interfaces with Xorg to handle key press events. This means that this component cannot be used via SSH, or in a headless environment (that is, without a desktop environment).
Note
The DISPLAY environment variable is required to be set in order for this component to work.
Note
If your code is being run from a terminal window, then the key presses will be captured in the terminal output. This can cause confusion and issues around reading output.
from time import sleep
from pitop import KeyboardButton
def on_up_pressed():
print("up pressed")
def on_up_released():
print("up released")
def on_down_pressed():
print("down pressed")
def on_down_released():
print("down released")
def on_left_pressed():
print("left pressed")
def on_left_released():
print("left released")
def on_right_pressed():
print("right pressed")
def on_right_released():
print("right released")
keyboard_btn_up = KeyboardButton("up")
keyboard_btn_down = KeyboardButton("down")
keyboard_btn_left = KeyboardButton("left")
keyboard_btn_right = KeyboardButton("right")
keyboard_btn_uppercase_z = KeyboardButton("Z")
# Methods will be called when key is pressed:
keyboard_btn_up.when_pressed = on_up_pressed
keyboard_btn_up.when_released = on_up_released
keyboard_btn_down.when_pressed = on_down_pressed
keyboard_btn_down.when_released = on_down_released
keyboard_btn_left.when_pressed = on_left_pressed
keyboard_btn_left.when_released = on_left_released
keyboard_btn_right.when_pressed = on_right_pressed
keyboard_btn_right.when_released = on_right_released
# Or alternatively you can "poll" for key presses:
while True:
if keyboard_btn_uppercase_z.is_pressed is True:
print("Z pressed!")
sleep(0.1)
Class Reference: KeyboardButton¶
-
class
pitop.keyboard.
KeyboardButton
(key)[source]¶ -
-
when_pressed
¶ Get or set the ‘when pressed’ button state callback function. When set, this callback function will be invoked when this event happens.
Parameters: callback (Function) – Callback function to run when a button is pressed.
-
when_released
¶ Get or set the ‘when released’ button state callback function. When set, this callback function will be invoked when this event happens.
Parameters: callback (Function) – Callback function to run when a button is released.
-
Special Key Names¶
You can listen for the following special keys by passing their names when creating an instance of KeyboardButton.
Identifier | Description |
---|---|
alt |
A generic Alt key. This is a modifier. |
alt_l |
The left Alt key. This is a modifier. |
alt_r |
The right Alt key. This is a modifier. |
alt_gr |
The AltGr key. This is a modifier. |
backspace |
The Backspace key. |
caps_lock |
The CapsLock key. |
cmd |
A generic command button. |
cmd_l |
The left command button. On PC keyboards, this corresponds to the Super key or Windows key, and on Mac keyboards it corresponds to the Command key. This may be a modifier. |
cmd_r |
The right command button. On PC keyboards, this corresponds to the Super key or Windows key, and on Mac keyboards it corresponds to the Command key. This may be a modifier. |
ctrl |
A generic Ctrl key. This is a modifier. |
ctrl_l |
The left Ctrl key. This is a modifier. |
ctrl_r |
The right Ctrl key. This is a modifier. |
delete |
The Delete key. |
down |
A down arrow key. |
up |
An up arrow key. |
left |
A left arrow key. |
right |
A right arrow key. |
end |
The End key. |
enter |
The Enter or Return key. |
esc |
The Esc key. |
home |
The Home key. |
page_down |
The PageDown key. |
page_up |
The PageUp key. |
shift |
A generic Shift key. This is a modifier. |
shift_l |
The left Shift key. This is a modifier. |
shift_r |
The right Shift key. This is a modifier. |
space |
The Space key. |
tab |
The Tab key. |
insert |
The Insert key. This may be undefined for some platforms. |
menu |
The Menu key. This may be undefined for some platforms. |
num_lock |
The NumLock key. This may be undefined for some platforms. |
pause |
The Pause/Break key. This may be undefined for some platforms. |
print_screen |
The PrintScreen key. This may be undefined for some platforms. |
scroll_lock |
The ScrollLock key. This may be undefined for some platforms. |
f1 |
The F1 key |
f2 |
The F2 key |
f3 |
The F3 key |
f4 |
The F4 key |
f5 |
The F5 key |
f6 |
The F6 key |
f7 |
The F7 key |
f8 |
The F8 key |
f9 |
The F9 key |
f10 |
The F10 key |
f11 |
The F11 key |
f12 |
The F12 key |
f13 |
The F13 key |
f14 |
The F14 key |
f15 |
The F15 key |
f16 |
The F16 key |
f17 |
The F17 key |
f18 |
The F18 key |
f19 |
The F19 key |
f20 |
The F20 key |
Command-Line Tools (CLI)¶
‘pi-top’ Command¶
Utility to interact with pi-top hardware.
pi-top [-h] {battery,devices,display,support,imu,oled} ...
Where:
-h, --help | Show a help message and exits |
- {battery,devices,display,help,imu,oled}
- battery:
- Get battery information from a pi-top
- devices:
- Get information about device and attached pi-top hardware
- display:
- Communicate and control the device’s display
- support:
- Find support resources
- imu:
- Expansion Plate IMU utilities
- oled:
- Quickly display text in pi-top [4]’s miniscreen OLED display
pi-top battery¶
If the pi-top device has an internal battery, it will report its status.
pi-top battery [-h] [-s] [-c] [-t] [-w] [-v]
Where:
-h, --help | Show a help message and exits |
-s, --charging-state | |
Optional. Return the charging state of the battery as an number, where:
| |
-c, --capacity | Optional. Get battery capacity percentage % |
-t, --time-remaining | |
Optional. Get the time (in minutes) to full or time to empty based on the charging state | |
-w, --wattage | Optional. Get the wattage (mAh) of the battery |
-v, --verbose | If no argument is provided, this option will be used by default. Report all the information available about the battery (charging state, capacity, time remaining and wattage) |
Example:
pi@pi-top:~ $ pi-top battery
Charging State: 0
Capacity: 42
Time Remaining: 104
Wattage: -41
pi-top display¶
This command provides a way to control different display settings on pi-top devices with a built-in screen.
pi-top display [-h] {brightness,backlight,timeout}
Where:
-h, --help | Show a help message and exits |
- brightness
- Control display brightness
- backlight
- Control display backlight
- timeout
- Set the timeout before the screen blanks in seconds (0 to disable)
pi-top display brightness¶
Request or change the value of the display’s brightness.
Note
This only works for the original pi-top, pi-topCEED and pi-top [3]. The pi-top [4] Full HD Touch Display uses hardware buttons to control the brightness, and is not controllable via this SDK.
pi-top display brightness [-h] [-v] [-i] [-d]
[brightness_value]
Where:
-h, --help | Show a help message and exits |
-v, --verbose | Increase verbosity of output |
-i, --increment_brightness | |
Increment screen brightness level | |
-d, --decrement_brightness | |
Decrement screen brightness level |
- brightness_value
- Set screen brightness level; [1-10] on pi-top [1] and pi-topCEED, [1-16] for pi-top [3]
Using pi-top display brightness without arguments will return the current brightness value.
Note
The brightness_value range differs for different devices: for pi-top [3] is from 0-16; pi-top [1] and CEED is 0-10.
Example:
pi@pi-top:~ $ pi-top display brightness
16
pi-top display backlight¶
Using pi-top display backlight without arguments will return the current backlight status.
pi-top display backlight [-h] [-v] [{0,1}]
Where:
-h, --help | Show a help message and exits |
-v, --verbose | Increase verbosity of output |
- {0,1}
- Set the screen backlight state [0-1]
pi-top display blank_time¶
Set the time before the screen goes blank on inactivity periods.
Using pi-top display blank_time without arguments will return the screen’s timeout value.
pi-top display timeout [-h] [-v] [timeout_value]
Where:
-h, --help | Show a help message and exits |
-v, --verbose | Increase verbosity of output |
- timeout_value
- Timeout value in seconds. Set to 0 to disable.
pi-top devices¶
Finds useful information about the system and the attached devices that are being managed by pi-topd.
Running pi-top devices on its own will report back the current brightness value.
pi-top devices [-h] [--quiet] [--name-only] {hub,peripherals}
Where:
-h, --help | Show a help message and exits |
--quiet, -q | Display only the connected devices |
--name-only, -n | |
Display only the name of the devices, without further information |
- hub
- Get the name of the active pi-top device
- peripherals
- Get information about attached pi-top peripherals
Example:
pi@pi-top:~ $ pi-top devices HUB =================================================== pi-top [4] (v5.4) PERIPHERALS =========================================== [ ✓ ] pi-top [4] Expansion Plate (v21.5) [ ] pi-top Touchscreen [ ] pi-top Keyboard [ ] pi-topPULSE [ ] pi-topSPEAKER (v1) - Left channel [ ] pi-topSPEAKER (v1) - Right channel [ ] pi-topSPEAKER (v1) - Mono [ ] pi-topSPEAKER (v2)
pi@pi-top:~ $ pt devices peripherals [ ✓ ] pi-top [4] Expansion Plate (v21.5) [ ] pi-top Touchscreen [ ] pi-top Keyboard [ ] pi-topPULSE [ ] pi-topSPEAKER (v1) - Left channel [ ] pi-topSPEAKER (v1) - Right channel [ ] pi-topSPEAKER (v1) - Mono [ ] pi-topSPEAKER (v2)
pi@pi-top:~ $ pt devices hub –name-only pi-top [4]
pi-top imu¶
Utility to calibrate the IMU included in the Expansion Plate.
pi-top imu calibrate [-h] [-p PATH]
Where:
-h, --help | Show a help message and exits |
-p PATH, --path PATH | |
Directory for storing calibration graph data |
Example:
pi-top imu calibrate --path /tmp
pi-top oled¶
Configure and display text/images directly onto pi-top [4]’s miniscreen OLED display.
pi-top oled [-h] {display,spi}
Where:
-h, --help | Show a help message and exits |
- display
- Display text and images into the OLED
- spi
- Control the SPI bus used by OLED
pi-top oled display¶
Display text and images directly onto pi-top [4]’s miniscreen OLED display.
pi-top oled display [-h] [--timeout TIMEOUT] [--font-size FONT_SIZE] text
Where:
-h, --help | Show a help message and exits |
-t, --timeout TIMEOUT | |
set the timeout in seconds | |
--font-size FONT_SIZE | |
set the font size |
- text
- set the text to write to screen
Example:
pi@pi-top:~ $ pi-top oled display "hey!" -t 5
pi-top oled spi¶
Control the SPI bus used by the OLED. When using pi-top oled spi without arguments, the SPI bus currently used by the OLED will be returned.
pi-top oled spi [-h] {0,1}
Where:
-h, --help | Show a help message and exits |
- {0,1}
- Optional. Set the SPI bus to be used by OLED. Valid options: 0 or 1
Example:
pi@pi-top:~ $ pi-top oled spi
1
pi@pi-top:~ $ pi-top oled spi 0
pi@pi-top:~ $ pi-top oled spi
0
pi-top support¶
Find information about support topics for your device.
pi-top support [-h] {links,health_check} ...
Where:
-h, --help | Show a help message and exits |
- {links,health_check}
- Subcommands, please refer to the next sections.
pi-top support links¶
Find resources to learn how to use your device and get help if needed.
pi-top support links [-h] {docs,help}
Where:
-h, --help | Show a help message and exits |
- {docs,help}
docs: Print links to pi-top documentation
help: Print links to places where to look for help
Example:
$ pi-top support links docs
===============================================
DOCS
===============================================
[ ✓ ] pi-top Python SDK documentation: online version, recommended
https://docs.pi-top.com/python-sdk/
[ ✓ ] pi-top Python SDK documentation: offline version
/usr/share/doc/python3-pitop/html/index.html
pi@pi-top:~ $ pi-top support links
===============================================
DOCS
===============================================
[ ✓ ] pi-top Python SDK documentation: online version, recommended
https://docs.pi-top.com/python-sdk/
[ ✓ ] pi-top Python SDK documentation: offline version
/usr/share/doc/python3-pitop/html/index.html
===============================================
OTHER
===============================================
[ ✓ ] Knowledge Base: Find answers to commonly asked questions
https://knowledgebase.pi-top.com/
[ ✓ ] Forum: Discuss and search through support topics.
https://forum.pi-top.com/
pi-top support health_check¶
Perform a system wide check to help troubleshooting any problems with pi-top software and hardware.
pi-top support health_check
🧪 Labs - Experimental APIs ⚠️¶
Note
The pi-top Python SDK Labs are a set of classes which are being provided as experiments in exciting new ways to interact with your device.
Warning
Everything in Labs is subject to change - so use at your own risk!
Web¶
This Web API has been created with the goal of giving users the ability to easily create a web application that runs directly on the pi-top that can easily offer a dynamic, interactive interface for controlling the pi-top.
The Web API provides a selection of web server interfaces, as well as a selection of prebuilt features known as Blueprints to be used with these servers.
For examples of how to use this, check out the labs examples directory on GitHub.
Servers¶
For simple static web apps or ground-up customisation, use WebServer.
If you would like a ‘batteries included’ WebServer that makes it easy to interact with your pi-top, use WebController.
For a quick way to control your pi-top [4] Robotics Kit, use RoverWebController, which offers a preconfigured but customisable WebController for rover-style robots.
WebServer¶
The WebServer class is used to create a zero-config server that can:
- serve static files and templates
- handle requests
- handle WebSocket connections
WebServer is a preconfigured gevent WSGIServer, due to this it can be started and stopped just like a gevent BaseServer:
from pitop.labs import WebServer
server = WebServer()
# start server in the background
server.start()
# stop server that has been started in the background
server.stop()
# start server and wait until interrupted
server.serve_forever()
WebServer serves static files and templates found in the working directory
automatically. The entrypoint file is always index.html
. All html files
found are considered to be Jinja templates, this means that if you have a
file layout.html
in the same directory as your WebServer:
<html>
<head>
<title>My Web App</title>
</head>
<body>
{% block body %}
{% endblock %}
</body>
</html>
It is possible to use it as template for other html files. For example
index.html
can extend layout.html
:
{% extends 'layout.html' %}
{% block body %}
<h1>My Custom Body</h1>
{% endblock %}
To add routes you can use the underlying Flask app’s route decorator:
from pitop.labs import WebServer
server = WebServer()
@server.app.route('/ping')
def ping():
return 'pong'
server.serve_forever()
WebSocket routes can be added by using the route decorator provided by Flask Sockets:
from pitop.labs import WebServer
server = WebServer()
@server.sockets.route('/ws')
def ws(socket):
while not socket.closed:
message = socket.receive()
socket.send(message)
server.serve_forever()
The server port
defaults to 8070 but can be customised:
from pitop.labs import WebServer
server = WebServer(port=8071)
It is also possible to customise the Flask app by passing your own into the
app
keyword argument:
from pitop.labs import WebServer
from flask import Flask
server = WebServer(app=Flask(__name__))
WebServer is fully compatible with Flask blueprints, which can be passed to
the blueprints
keyword argument:
from pitop.labs import WebServer
from flask import Blueprint
WebServer(blueprints=[
Blueprint('custom', __name__)
])
We provide a number of premade blueprints:
By default WebServer uses the BaseBlueprint
Warning
When using WebServer in a multithreaded project you must use gevent threading. This is because using Python standard library threading while using a gevent server can result in unexpected behaviour, or may not work at all. See the dashboard example for a basic idea of how gevent threading can be used.
WebController¶
The WebController class is subclass of WebServer that uses the ControllerBlueprint. It exists as a convenience class so that blueprints are not required to be able to build simple web controllers.
from pitop import Camera
from pitop.labs import WebController
camera = Camera()
def on_dinner_change(data):
print(f'dinner is now {data}')
server = WebController(
get_frame=camera.get_frame,
message_handlers={'dinner_changed': on_dinner_change}
)
server.serve_forever()
See the ControllerBlueprint reference for more detail.
RoverWebController¶
The RoverWebController class is subclass of WebServer that uses the RoverControllerBlueprint. It exists as a convenience class so that blueprints are not required to build simple rover web controllers.
from pitop import Pitop, Camera, DriveController, PanTiltController
from pitop.labs import RoverWebController
rover = Pitop()
rover.add_component(Camera())
rover.add_component(DriveController())
rover.add_component(PanTiltController())
server = RoverWebController(
get_frame=rover.camera.get_frame,
drive=rover.drive,
pan_tilt=rover.pan_tilt
)
server.serve_forever()
See the RoverControllerBlueprint reference for more detail.
Blueprints¶
BaseBlueprint¶
BaseBlueprint provides a layout and styles that are the base of the
templates found in other blueprints. It adds a base.html
template which
has the following structure:
<html>
<head>
<title>{% block title %}{% endblock %}</title>
{% block head %}
<link rel="stylesheet" href="/base/index.css"></link>
{% endblock %}
</head>
<body>
{% block body %}
<header> {% block header %}{% endblock %} </header>
<main> {% block main %}{% endblock %} </main>
<footer> {% block footer %}{% endblock %} </footer>
{% endblock %}
</body>
</html>
The base.html
adds some basic styles and variables to the page by
linking the index.css
static file.
:root {
--background-color: #00B2A2
}
body {
background-color: var(--background-color);
margin: 0;
padding: 0;
}
Adding the BaseBlueprint to a WebServer is done as follows:
from pitop.labs import WebServer, BaseBlueprint
server = WebServer(blueprints=[
BaseBlueprint()
])
server.serve_forever()
Note: WebServer uses BaseBlueprint by default, so the above is only necessary if you are using BaseBlueprint with other blueprints.
Then you are able to extend the base.html
in your other html files:
{% extends 'base.html' %}
{% block title %}Custom Page{% endblock %}
{% block head %}
<!-- call super() to add index.css -->
{{ super() }}
<link rel="styles" href="custom-styles.css"></link>
{% endblock %}
{% block header %}
<img src="logo.png"></img>
{% endblock %}
{% block main %}
<section>Section One</section>
<section>Section Two</section>
{% endblock %}
{% block footer %}
Contact Info: 123456789
{% endblock %}
If you want to use the static files provided without extending the
base.html
template you can do so by adding them to the page yourself:
<html>
<head>
<link rel="stylesheet" href="/base/index.css"></link>
</head>
<body>
</body>
</html>
WebComponentsBlueprint¶
WebComponentsBlueprint provides a set of Web Components for adding complex elements to the page.
Adding the WebComponentsBlueprint to a WebServer is done as follows:
from pitop.labs import WebServer, WebComponentsBlueprint
server = WebServer(blueprints=[
WebComponentsBlueprint()
])
server.serve_forever()
To add the components to the page WebComponentsBlueprint provides a setup template
setup-components.html
that can be included in the head
of your
page
<head>
{% include "setup-webcomponents.html" %}
</head>
Currently the only component included is the joystick-component
, which
acts a wrapper around nippleJS.
<joystick-component
mode="static"
size="200"
position="relative"
positionTop="100"
positionLeft="100"
positionRight=""
positionBottom=""
onmove="console.log(data)"
onend="console.log(data)"
></joystick-component>
To add the joystick-component to the page without using templates you can add it
to the page by adding the nipplejs.min.js
and
joystick-component.js
scripts to the head
of your page:
<head>
<script type="text/javascript" src="/webcomponents/vendor/nipplejs.min.js"></script>
<script type="text/javascript" src="/webcomponents/joystick-component.js"></script>
</head>
MessagingBlueprint¶
MessagingBlueprint is used to communicate between your python code and the page.
Adding the MessagingBlueprint to a WebServer is done as follows:
from pitop.labs import WebServer, MessagingBlueprint
server = WebServer(blueprints=[
MessagingBlueprint()
])
server.serve_forever()
To add messaging to the page MessagingBlueprint provides a setup template
setup-messaging.html
that can be included in the head
of your
page:
<head>
{% include "setup-messaging.html" %}
</head>
This adds a JavaScript function publish
to the page, which you can use
to send JavaScript Objects to your WebServer. The messages must have a type
,
and can optionally have some data
.
<select
id="dinner-select"
onchange="publish({ type: 'dinner_changed', data: this.value })"
>
<option value="tacos">Tacos</option>
<option value="spaghetti">Spaghetti</option>
</select>
To receive the messages sent by publish
you can pass a
message_handlers
dictionary to MessagingBlueprint. The keys of
message_handlers
correspond to the type
of the message and the
value must be a function that handles the message, a ‘message handler’. The
message handler is passed the message’s data
value as it’s first
argument.
from pitop.labs import WebServer, MessagingBlueprint
def on_dinner_change(data):
print(f'dinner is now {data}')
messaging = MessagingBlueprint(message_handlers={
'dinner_changed': on_dinner_change
})
server = WebServer(blueprints=[messaging])
server.serve_forever()
The second argument of a message handler is a send
function which
can send a message back to the page:
def on_dinner_change(data, send):
print(f'dinner is now {data}')
send({ 'type': 'dinner_received' })
To receive messages sent from a message handler the MessagingBlueprint also adds
a JavaScript function subscribe
to the page:
<script>
subscribe((message) => {
if (message.type === 'dinner_received') {
console.log('Dinner Received!')
}
})
</script>
Another way of sending messages to the page is to use the MessagingBlueprint’s
broadcast
method:
from pitop import Button
from pitop.labs import WebServer, MessagingBlueprint
button = Button('D1')
def on_dinner_change(data):
print(f'dinner is now {data}')
messaging = MessagingBlueprint(message_handlers={
'dinner_changed': on_dinner_change
})
def reset():
messaging.broadcast({ 'type': 'reset' })
button.on_press = reset
server = WebServer(blueprints=[messaging])
server.serve_forever()
This is received by the same subscribe function as before:
<script>
subscribe((message) => {
if (message.type === 'reset') {
console.log('Reset')
}
})
</script>
There is one difference between broadcast
and send
:
broadcast
sends the message to every client whereas send
only
responds to the client that sent the message being handled.
VideoBlueprint¶
VideoBlueprint adds the ability to add a video feed from your python code to the page.
Adding the VideoBlueprint to a WebServer is done as follows:
from pitop import Camera
from pitop.labs import WebServer, VideoBlueprint
camera = Camera()
server = WebServer(blueprints=[
VideoBlueprint(get_frame=camera.get_frame)
])
server.serve_forever()
To add video styles to the page VideoBlueprint provides a setup template
setup-video.html
that can be included in the head
of your
page:
<head>
{% include "setup-video.html" %}
</head>
This adds a set of classes that can be used to style your video:
.background-video {
height: 100vh;
position: fixed;
top: 0;
left: 50%;
transform: translateX(-50%);
z-index: -1;
}
In order to render the video on the page you must use an img
tag with
the src
attribute of video.mjpg
:
<body>
<img src="video.mjpg" class="background-video"></img>
</body>
It is also possible to add multiple VideoBlueprints to a WebServer:
from pitop import Camera
from pitop.labs import WebServer, VideoBlueprint
camera_one = Camera(index=0)
camera_two = Camera(index=1)
server = WebServer(blueprints=[
VideoBlueprint(name="video-one", get_frame=camera_one.get_frame),
VideoBlueprint(name="video-two", get_frame=camera_two.get_frame)
])
server.serve_forever()
This makes it possible to to add multiple video feeds to the page, where the
src
attribute uses the name of the VideoBlueprint with a .mjpg
extension:
<body>
<img src="video-one.mjpg"></img>
<img src="video-two.mjpg"></img>
</body>
If you want to use the static files on your page without using templates you can do so by adding them to the page yourself:
<head>
<link rel="stylesheet" href="/video/styles.css"></link>
</head>
ControllerBlueprint¶
ControllerBlueprint combines blueprints that are useful in creating web apps that interact with your pi-top. The blueprints it combines are the BaseBlueprint, WebComponentsBlueprint, MessagingBlueprint and VideoBlueprint.
from pitop import Camera
from pitop.labs import WebServer, ControllerBlueprint
camera = Camera()
def on_dinner_change(data):
print(f'dinner is now {data}')
server = WebServer(blueprints=[
ControllerBlueprint(
get_frame=camera.get_frame,
message_handlers={'dinner_changed': on_dinner_change}
)
])
server.serve_forever()
To simplify setup ControllerBlueprint provides a base-controller.html
template which includes all the setup snippets for it’s children blueprints:
{% extends "base.html" %}
{% block title %}
Web Controller
{% endblock %}
{% block head %}
{{ super() }}
{% include "setup-video.html" %}
{% include "setup-messaging.html" %}
{% include "setup-webcomponents.html" %}
{% endblock %}
base-controller.html
extends base.html
, this means you can use
blocks defined in base.html
when extending base-controller.html
:
{% extends "base-controller.html" %}
{% block title %}My WebController{% endblock %}
{% block head %}
<!-- call super() to setup blueprints -->
{{ super() }}
<link rel="stylesheet" href="custom-styles.css"></link>
{% endblock %}
{% block main %}
<h1>Video</h1>
<img src="video.mjpg"></img>
{% endblock %}
RoverControllerBlueprint¶
RoverControllerBlueprint uses the ControllerBlueprint to create a premade web controller specifically built for rover projects.
from pitop import Pitop, Camera
from pitop.labs import WebServer, RoverControllerBlueprint
rover = Pitop()
rover.add_component(Camera())
rover.add_component(DriveController())
rover.add_component(PanTiltController())
server = WebServer(blueprints=[
RoverControllerBlueprint(
get_frame=rover.camera.get_frame,
drive=rover.drive,
pan_tilt=rover.pan_tilt
)
])
server.serve_forever()
RoverControllerBlueprint provides a page template base-rover.html
which
has a background video and two joysticks:

By default the right joystick is used to drive the rover around and the left
joystick controls the pan tilt mechanism. The drive
keyword argument is
required, but the pan_tilt
keyword argument is optional; if it is not
passed the left joystick is not rendered.
It is possible to customise the page by extending the base-rover.html
template:
{% extends "base-rover.html" %}
{% block title %}My Rover Controller{% endblock %}
{% block main %}
<!-- call super() to keep video and joysticks -->
{{ super() }}
<button onclick="publish({ type: 'clicked' })"></button>
{% endblock %}
It is also possible to customise the message handlers used by the RoverControllerBlueprint, for example to swap the joysticks so the left drives the rover and the right controls pan tilt:
from pitop import Camera, DriveController, PanTiltController, Pitop
from pitop.labs import RoverWebController
from pitop.labs.web.blueprints.rover import drive_handler, pan_tilt_handler
rover = Pitop()
rover.add_component(DriveController())
rover.add_component(PanTiltController())
rover.add_component(Camera())
rover_controller = RoverWebController(
get_frame=rover.camera.get_frame,
message_handlers={
"left_joystick": lambda data: drive_handler(rover.drive, data),
"right_joystick": lambda data: pan_tilt_handler(rover.pan_tilt, data),
},
)
rover_controller.serve_forever()
Note that when left_joystick
or right_joystick
are in
message_handlers
the pan_tilt
and drive
arguments do not
need to be passed respectively.
More Information¶
Frequently Asked Questions¶
How does this SDK work?¶
What is PMA?¶
I keep getting an Exception - what is the problem?¶
Where did this SDK come from?¶
Note: epoch version
I was using an older version of the Python libraries. How can I update to use this SDK?¶
Check out the Python SDK Migration GitHub repository for more information about this.
You may also find it helpful to check out the examples to see how to use the new components.
API Changes¶
This section aims to outline key changes made between versions, to support upgrading.
Contributing¶
Check out the Contributing to pi-topOS article in the pi-top knowledge base to learn how to contribute.
References¶
Requirements¶
The following Debian packages are required for this library to work:
Package Name | Usage |
---|---|
alsa-utils |
Used for configuring the system audio; such as setting the correct audio card when connecting a pi-topSPEAKER. |
coreutils |
Used to perform basic OS operations and commands; such as ls and chmod |
fonts-droid-fallback |
Minimum essential font used by the OLED screen. |
i2c-tools |
Communicate with pi-top I2C devices. |
pi-topd |
Allows communication with pi-top’s hub; such as getting battery state.
This package installs a systemd service that needs to be running for this library to work properly |
raspi-config |
Required to communicate and set parameters to the Raspberry Pi. |
License¶
Copyright 2020 CEED Ltd.
Licensed under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
Version 2.0, January 2004 http://www.apache.org/licenses/
For an alphabetized list of terms used in this SDK with links, check out the Index.