Raspberry Pi Physical Computing Guide
Raspberry Pi Physical Computing Guide
Introduction
Physical computing is creating interactive systems that sense and respond to the analog world. While this
field has traditionally focused on direct sensor readings and programmed responses, we're entering an
exciting new era where Large Language Models (LLMs) can add sophisticated decision-making and natural
language interaction to physical computing projects.
In the Small Language Models (SLM) chapter, we learned how it is possible to run an LLM (or, more precisely,
an SLM) in a Single Board Computer (SBC) like the Raspberry Pi. This tutorial will guide us through setting up
a Raspberry Pi for physical computing, with an eye toward future AI integration. We'll cover:
We will also use a Jupyter notebook (programmed in Python) to interact with sensors and actuators—an
important and necessary first step toward the goal of integrating the Raspi with an SLM. The combination of
Raspberry Pi's versatility and the power of SLMs opens up exciting possibilities for creating more intelligent
and responsive physical computing systems.
Raspberry Pi Imager is a tool for downloading and writing images on macOS, Windows, and Linux. It
includes many popular operating system images for Raspberry Pi. We will also use the Imager to
preconfigure credentials and remote access settings.
We should also define the options, such as the hostname, username, password, LAN configuration (on
GENERAL TAB), and, more importantly, SSH Enable on the SERVICES tab.
Using the Secure Shell (SSH) protocol, you can access the terminal of a Raspberry Pi remotely from
another computer on the same network.
ssh mjrovai@[Link]
You should replace mjrovai with your username and rpi-5 with the hostname chosen during set-u
mjrovai@rpi-5:~ $
Note: ssh <username>@<hostname>.local sometimes does not work. In those cases, try: ssh
<username>@<ip address>
It is a good practice to update the system regularly. For that, you should run:
Pip is a tool for installing external Python modules on a Raspberry Pi. However, it has not been enabled in
recent OS versions. To allow it, you should run the command (only once):
sudo rm /usr/lib/python3.11/EXTERNALLY-MANAGED
Do not simply pull the power cord when you want to turn off your Raspberry Pi. The Raspi may still be
writing data to the SD card, in which case merely powering down may result in data loss or, even worse, a
corrupted SD card.
Pin Numbering
It is essential to mention that GPIO Zero Library uses Broadcom (BCM) pin numbering for the GPIO pins, as
opposed to physical (board) numbering. Any pin marked “GPIO ” in the following diagrams can be used as a
PIN. For example, if an LED were attached to “GPIO13,” you would specify the PIN as 18 rather than 33 (the
physical one).
Physical Pin 6 (GND) to GND Breadboard Power Grid (Blue -), using a black jumper
Physical Pin 1 (3.3V) to +VCC Breadboard Power Grid (Red +), using a red jumper
Now, let's connect an LED (red) using the physical pin 13 (GPIO13) connected to the LED cathode (longer LED
leg). Connect the LED anode to the breadboard GND using a 330 ohms resistor to reduce the current
drained from the Raspi, as shown below:
We can use any text editor (such as Nano ) to create and run the script. Save the file, for example, as
led_test.py , and then execute it using at the terminal:
python led_test.py
Now, let's blink the LED (the actual "Hello world ") when talking about physical computing. To do that, we
must also import another library, which is time . We will need it to define how long the LED will be ON and
OFF. In our case below, the LED will blink at a 1-second time.
ledRed = LED(13)
ledYlw = LED(19)
ledGrn = LED(26)
[Link]()
[Link]()
[Link]()
[Link]()
[Link]()
[Link]()
[Link]()
[Link]()
[Link]()
Button
The simple way to read an external command is using a push-button, and the GPIO Zero Library provides a
easy way to include it on our project. We do not need to think about Pull-up or Pull-down resistors, etc. In
terms of HW, the only thing to do is to connect one leg of our push-button to any one of RPi GPIOs and the
other one to GND as shown in the diagram:
Note that we will keep using GPIO Zero for pins, buttons and LEDs.
Enable Interfaces
The script will automatically enable I2C and SPI. You can run the following command to verify:
ls /dev/i2c* /dev/spi*
Blinka Test
Create a new file called blinka_test.py with nano or your favorite text editor and put the following in:
import board
import digitalio
import busio
print("Hello, blinka!")
python blinka_test.py
Overview
The low-cost DHT temperature and humidity sensors are elementary and slow but great for logging basic
data. They consist of a capacitive humidity sensor and a thermistor. A bare chip inside performs the analog-
to-digital conversion and spits out a digital signal with the temperature and humidity. The digital signal is
relatively easy to read using any microcontroller.
Once we use the sensor at distances less than 20m, a 4K7 ohm resistor should be connected between the
Data and VCC pins. The DHT22 output data pin will be connected to Raspberry GPIO 16. Check the
electrical diagram, connecting the sensor to RPi pins as below:
Do not forget to Install the 4K7 ohm resistor between the VCC and Data pins.
Once the sensor is connected, we must also install its library on our Raspi. For that we should first, install
the Adafruit CircuitPython library, what we have already done and them the Adafruit_CircuitPython_DHT.
cd Documents
Create a new Python script as below and name it, for example dht_test.py :
import time
import board
import adafruit_dht
dhtDevice = adafruit_dht.DHT22(board.D16)
while True:
try:
# Print the values to the serial port
temperature_c = [Link]
temperature_f = temperature_c * (9 / 5) + 32
humidity = [Link]
print(
"Temp: {:.1f} F / {:.1f} C Humidity: {}% ".format(
temperature_f, temperature_c, humidity
)
)
Environmental sensing has become increasingly important in various industries, from weather forecasting
to indoor navigation and consumer electronics. At the forefront of this technological advancement are
sensors like the BMP280 and BMP180 (deprected), which excel in measuring temperature and barometric
pressure with exceptional precision and reliability.
As its predecessor, the BMP180, the BMP280 is an absolute barometric pressure sensor, which is especially
feasible for mobile applications. Its diminutive dimensions and low power consumption allow for its
implementation in battery-powered devices such as mobile phones, GPS modules, or watches. The BMP280
is based on Bosch’s proven piezo-resistive pressure sensor technology featuring high accuracy and linearity
as well as long-term stability and high EMC robustness. Numerous device operation options guarantee the
highest flexibility. The device is optimized for power consumption, resolution, and filter performance.
Technical data
Average current consumption (1 Hz dt refresh rate) 2.74 μA, typical (ultra-low power mode)
Temperature coefficient offset (+25°…+40°C @900hPa) 1.5 Pa/K, equiv. to 12.6 cm/K
BMP280 Sensor Installation: Follow the diagram and make the connections:
Go to RPi Configuration and confirm that I2C interface is enabled. If not, enable it.
If everything has been installed and connected correctly, you can turn on your Rapspi and start interpreting
the BMP180's information about the environment.
The first thing to do is to check if the Raspi sees your BMP280. Try the following in a terminal:
In my case, the bus address is 0x76, so we should define it during the library installation.
Once the sensor is connected, we must install its library on our Raspi. For that, we should install the
Adafruit_CircuitPython_BMP280.
Create a new Python script as below and name it, for example, bmp280_test.py :
import time
import board
import adafruit_bmp280
i2c = board.I2C()
bmp280 = adafruit_bmp280.Adafruit_BMP280_I2C(i2c, address = 0x76)
bmp280.sea_level_pressure = 1013.25
while True:
print("\nTemperature: %0.1f C" % [Link])
print("Pressure: %0.1f hPa" % [Link])
print("Altitude = %0.2f meters" % [Link])
[Link](2)
python [Link]
Note that that pressure is presented in hPa. See the next section to understand this unit better.
The BMP280 (and its predecessor, the BMP180) was designed to measure atmospheric pressure accurately.
Atmospheric pressure varies with both weather and altitude.
Atmospheric pressure is a force that the air around you exerts on everything. The weight of the gasses in
the atmosphere creates atmospheric pressure. A standard unit of pressure is "pounds per square inch" or
psi. We will use the international notation, newtons per square meter, called pascals (Pa).
This weight, pressing down on the footprint of that column, creates the atmospheric pressure that we can
measure with sensors like the BMP280. Because that cm-wide column of air weighs about 1 kg, the average
sea level pressure is about 101,325 pascals, or better, 1013.25 hPa (1 hPa is also known as milibar - mbar).
This will drop about 4% for every 300 meters you ascend. The higher you get, the less pressure you’ll see
because the column to the top of the atmosphere is much shorter and weighs less. This is useful because
you can determine your altitude by measuring the pressure and doing math.
The air pressure at 3, 810 meters is only half of that at sea level.
The BMP280 outputs absolute pressure in hPa ( mbar ). One pascal is a very small amount of pressure,
approximately the amount that a sheet of paper will exert resting on a table. You will more often see
measurements in hectopascals (1 hPa = 100 Pa). The library used here provides outputs floating-point
values in hPa, which also happens to equal one millibar (mbar).
Temperature Effects
Because temperature affects the density of a gas, density affects the mass of a gas, and mass affects the
pressure (whew), atmospheric pressure will change dramatically with temperature. Pilots know this as
“density altitude”, which makes it easier to take off on a cold day than a hot one because the air is denser
and has a more significant aerodynamic effect. To compensate for temperature, the BMP280 includes a
rather good temperature sensor and a pressure sensor.
To perform a pressure reading, you first take a temperature reading, then combine that with a raw pressure
reading to come up with a final temperature-compensated pressure measurement. (The library makes all of
this very easy.)
If your application requires measuring absolute pressure, all you have to do is get a temperature reading,
then perform a pressure reading (see the test script for details). The final pressure reading will be in hPa =
mbar. You can convert this to a different unit using the above conversion factors.
Note that the absolute pressure of the atmosphere will vary with both your altitude and the current
weather patterns, both of which are useful things to measure.
Weather Observations
The atmospheric pressure at any given location on Earth (or anywhere with an atmosphere) isn’t constant.
The complex interaction between the earth’s spin, axis tilt, and many other factors result in moving areas of
higher and lower pressure, which in turn cause the variations in weather we see every day. By watching for
changes in pressure, you can predict short-term changes in the weather. For example, dropping pressure
usually means wet weather or a storm is approaching (a low-pressure system is moving in). Rising pressure
usually means clear weather is coming (a high-pressure system is moving through). But remember that
atmospheric pressure also varies with altitude. The absolute pressure in my home, Lo Barnechea in Chile
(altitude 960m), will always be lower than the absolute pressure in San Francisco (less than 2 meters, almost
sea level). If weather stations just reported their absolute pressure, it would be challenging to compare
pressure measurements from one location to another (and large-scale weather predictions depend on
measurements from as many stations as possible).
To solve this problem, weather stations always remove the effects of altitude from their reported pressure
readings by mathematically adding the equivalent fixed pressure to make it appear as if the reading was
taken at sea level. When you do this, a higher reading in San Francisco than in Lo Barnechea will always be
because of weather patterns and not because of altitude.
Having the absolute pressure in Pa, you check the sea level pressure using the Calculator.
Or calculating in Python, where altitude is real altitude in meters where the sensor is located.
Determining Altitude
Since pressure varies with altitude, you can use a pressure sensor to measure altitude (with a few caveats).
The average pressure of the atmosphere at sea level is 1013.25 hPa (or mbar). This drops off to zero as you
climb towards the vacuum of space. Because the curve of this drop-off is well understood, you can compute
the altitude difference between two pressure measurements (p and p0) by using a specific equation. The
BMP280 gives the measured altitude using [Link] .
In this section, we will learn how to install Jupyter Notebook on a Raspberry Pi. Then, we will read sensors
and act on actuators directly on the Pi.
To install Jupyter on your Raspberry (that will run with Python 3), open Terminal and enter the following
commands:
nano ~/.jupyter/jupyter_notebook_config.py
Now, on the Raspi terminal, start the Jupyter notebook server with the command:
You will need the Token; you can copy it from the terminal as shown above.
http:localhost:8888
The first time you connect, you'll need the token that appears in the Pi terminal when you start the
notebook server.
To stop the server and close the "kernels" (the Jupyter notebooks), press [Ctrl] + [C].
Initialization
Import libraries, instantiate and initialize sensors/actuators
# time library
import time
import datetime
# LEDs
from gpiozero import LED
ledRed = LED(13)
[Link]()
[Link]()
[Link]()
# Push-Button
from gpiozero import Button
button = Button(20)
[Link]()
[Link]()
[Link]()
[Link]()
[Link]()
[Link]()
getGpioStatus()
PrintGpioStatus()
temp = [Link]
pres = [Link]
alt = [Link]
presSeaLevel = pres / pow(1.0 - real_altitude/44330.0, 5.255)
Entering the BMP280 real altitude where it is located, run the code:
bmp280GetData(960)
Temperature of 26.9 oC
Now, we will generate a unique function to get the BMP280 and the DHT data, including a timestamp:
tempDHT = [Link]
humDHT = [Link]
Runing them:
Results:
Widgets
pywidgets, also known as jupyter-widgets or simply widgets, are interactive HTML widgets for Jupyter
notebooks and the IPython kernel. Notebooks come alive when interactive widgets are used. We can gain
control of our data and visualize changes in them.
Widgets are eventful Python objects that have a representation in the browser, often as a control like a
slider, text box, etc. We can use widgets to build interactive GUIs for our project.
In this lab, for example, we will use a slide bar to control the state of actuators in real time, such as by
turning on or off the LEDs. Widgets are great for adding more dynamic behavior to Jupyter Notebooks.
Installation
To use Widgets, we must install the Ipywidgets library using the commands:
# widget library
from ipywidgets import interactive
import ipywidgets as widgetsfrom
[Link] import display
and running the below line, we can control the LEDs in real-time:
Installation
Let's create a simple SLM test in the Jupyter notebook that checks if the model loads and measures
inference time. The model used here is the TinyLLama 1.1B . We will ask a very simple question:
import time
from transformers import pipeline
import torch
# Test prompt
test_prompt = "The weather today is"
# Memory usage
if device == "cuda":
print(f"\nGPU Memory allocated: {[Link].memory_allocated()/1024**2:.2f} MB")
print(f"GPU Memory cached: {[Link].memory_reserved()/1024**2:.2f} MB")
As we can see, the SLM works, but the latency is very high (+3 minutes). It is OK because this particular test
is on a Raspberry Pi 4. With a Raspberry Pi 5, the result would be better.:
The Raspi uses around 1GB of memory and all four cores to process the answer.
import time
import datetime
import board
import adafruit_dht
import adafruit_bmp280
from gpiozero import LED, Button
from transformers import pipeline
Initialize sensors
DHT22Sensor = adafruit_dht.DHT22(board.D16)
i2c = board.I2C()
bmp280Sensor = adafruit_bmp280.Adafruit_BMP280_I2C(i2c, address=0x76)
bmp280Sensor.sea_level_pressure = 1013.25
generator = pipeline('text-generation',
model='TinyLlama/TinyLlama-1.1B-intermediate-step-1431k-3T',
device='cpu')
Support Functions
Now, let's create support functions to readings from all sensors and control the LEDs:
def get_sensor_data():
"""Get current readings from all sensors"""
try:
temp_dht = [Link]
humidity = [Link]
temp_bmp = [Link]
pressure = [Link]
return {
'temperature_dht': round(temp_dht, 1) if temp_dht else None,
'humidity': round(humidity, 1) if humidity else None,
'temperature_bmp': round(temp_bmp, 1),
'pressure': round(pressure, 1)
}
except RuntimeError:
return None
def process_conditions(sensor_data):
"""Process sensor data and control LEDs based on conditions"""
if not sensor_data:
control_leds(red=True) # Error condition
return
temp = sensor_data['temperature_dht']
humidity = sensor_data['humidity']
So far, the LEDs reaction is only based on logic, but let's also use the SLM to "analyse" the sensors condition,
generating a response based on that:
def generate_response(sensor_data):
"""Generate response based on sensor data using SLM"""
if not sensor_data:
return "Unable to read sensor data"
return response
Main Function
And now, let's create a main() function to wait for the user to, for example, press a button and so,
capturing the data generated by the sensors, delivering some observation or recommendation from the
SLM:
def main_loop():
"""Main program loop"""
print("Starting Physical Computing with SLM Integration...")
print("Press the button to get a reading and SLM response.")
try:
while True:
if button.is_pressed:
# Get sensor readings
sensor_data = get_sensor_data()
if sensor_data:
# Get SLM response
response = generate_response(sensor_data)
except KeyboardInterrupt:
print("\nShutting down...")
control_leds(False, False, False) # Turn off all LEDs
Test Result
The sensors are read after the user presses the button to trigger a reading, and LEDs are controlled based
on conditions. Sensor data is formatted into a prompt for the SLM to generate a response analyzing the
current conditions. The Results are displayed in the terminal with the LED indicators showing
This simple code integrates a Small Language Model (TinyLlama model (1.1B parameters) with our physical
computing setup, providing raw sensor data and intelligent responses from the SLM about the
environmental conditions.
From user prompt, provide the the status of LEDS, Button or an specific sensor data.
Log data and responses to a file. Provide historical information by user request
Other Models
We can use other SLMs in a Raspberry Pi that have distinct ways of handling them. For example, a lot of
modern models use GGUF formats, and to use them, we need to install llama-cpp-python, which is
designed to work with GGUF models.
Also, Ollama is a great way to download and test SLMs in the Raspberry Pi.
Conclusion
Key Achievements
Throughout this tutorial, we've successfully:
Technical Insights
Hardware Integration
The combination of digital (DHT22) and I2C (BMP280) sensors demonstrated different communication
protocols and their implementations. This multi-sensor approach provides redundancy and comprehensive
environmental monitoring capabilities. The LED actuators and push-button interface created a responsive
and interactive system that bridges the digital and physical worlds.
Software Architecture
AI Integration Learnings
Small Language Models can effectively run on edge devices like Raspberry Pi
The system can provide human-readable insights from complex sensor data
Practical Applications
This project serves as a foundation for numerous real-world applications:
1. Resource Constraints:
Optimized SLM inference for Raspberry Pi capabilities
2. Data Integration:
3. AI Integration:
Future Enhancements
The system can be extended in several directions:
1. Hardware Expansions:
Additional sensor types (air quality, light, motion)
2. Software Improvements:
3. AI Capabilities:
Final Thoughts
This tutorial demonstrates that integrating physical computing with AI is not just feasible but practical on
accessible hardware like the Raspberry Pi. The combination of sensors, actuators, and AI creates a powerful
platform for developing intelligent environmental monitoring and control systems.
While the current implementation focuses on environmental monitoring, the principles and techniques can
be adapted to various applications. The modular nature of hardware and software components allows for
customization and expansion based on specific needs.
Remember that this is just the beginning - the foundation we've built can be extended in countless ways to
create more sophisticated and capable systems. The key is to build upon these basics while maintaining the
balance between functionality, reliability, and resource usage.