Examples · Internet of Things
A sensor that speaks
A reading from the sensors becomes a sentence, and the sentence becomes an MP3 that a Raspberry Pi plays in the classroom. A model makes the sentence friendlier; your code checks its numbers.
Intermediate · 45 min
The idea
A display needs someone looking at it. A voice reaches everyone in the room: “Lab 2: the air is stale, please open a window.”
The program takes a reading, writes a sentence, and turns it into an MP3:
- a template writes the sentence: the numbers exactly as they are, the same way every time;
- a model writes a friendlier one, and your code keeps it only if every number is still there;
- a voice reads it into
reading.mp3.
What you need
A key and the openai library, as in Your first API call in Python. On the Raspberry Pi, a speaker or headphones.
The program
Save it as speak_reading.py:
import json
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.ai.itsincom.org/v1",
api_key=os.environ["API_KEY"],
)
reading = {"room": "Lab 2", "temperature": 27.4, "humidity": 61, "co2": 1450} # from your sensors
# 1. A template: the numbers exactly as they are, the same way every time
sentence = (f"{reading['room']}: the temperature is {reading['temperature']} degrees, "
f"humidity {reading['humidity']} percent, carbon dioxide {reading['co2']} parts per million.")
if reading["co2"] > 1000:
sentence += " The air is stale: please open a window."
# 2. A friendlier sentence from a model, kept only if every number is still there
answer = client.chat.completions.create(
model="gemma-4-26b",
temperature=0.7,
messages=[
{"role": "system", "content": "You are the voice of the air monitor of a classroom. From the reading, "
"write one or two short sentences to be read aloud, in plain words, with "
"no symbols and no emoji. Say the temperature, the humidity and the CO2, "
"each number exactly as in the reading. If the CO2 is over 1000, ask to "
"open a window."},
{"role": "user", "content": json.dumps(reading)},
],
)
friendly = answer.choices[0].message.content.strip()
numbers = [str(reading[key]) for key in ("temperature", "humidity", "co2")]
if all(number in friendly for number in numbers):
sentence = friendly
else:
print("A number is missing or changed, so the template speaks. The model wrote:", friendly)
# 3. The sentence becomes an MP3
speech = client.audio.speech.create(model="tts-1", voice="amy", input=sentence, response_format="mp3")
speech.write_to_file("reading.mp3")
print(sentence)
print("reading.mp3 is ready")
Run it with python3 speak_reading.py. It takes a few seconds.
What you should see
The temperature is 27.4 degrees, the humidity is 61 percent, and the CO2 is 1450. Please open a window.
reading.mp3 is ready
The model writes a different sentence every time. When a number goes missing, the template speaks instead:
A number is missing or changed, so the template speaks. The model wrote: The CO2 level is 1450. Please open a window.
Lab 2: the temperature is 27.4 degrees, humidity 61 percent, carbon dioxide 1450 parts per million. The air is stale: please open a window.
reading.mp3 is ready
This happened to us with a vaguer system message, which did not name the three values. That is the reason for the check: a model can leave out or change a number, a template cannot.
On the Raspberry Pi
Install a player once, then play the file:
sudo apt install mpg123
mpg123 reading.mp3
From your Python program, the same with subprocess.run(["mpg123", "-q", "reading.mp3"]).
Good to know
- Speak when something changes, not at every reading: a voice every minute is noise. For example, once when the CO2 goes over 1000, and once when it is back under 800.
- Make the fixed messages once. “Please open a window” never changes: make its MP3 once, keep it on the Pi and play it whenever you need it. No network, no waiting, no credits.
- No network, no voice. Every new sentence is made by the school's machines. For alarms, keep a ready-made MP3 or a beep on the Pi.
- The check is on the numbers only. A model can still get something wrong in words: for anything that matters, the template is the safer choice.
Exercise
- Read the values from real sensors instead of the dictionary: a DHT22 for temperature and humidity, an SCD30 or SCD41 for the CO2.
- The check is simple: “61” is also inside “1610”. Write a stricter one:
re.findall(r"\d+(?:\.\d+)?", friendly)gives you the numbers in the sentence. - Make the Pi speak only when the CO2 goes over 1000, and once more when it is back under 800.
- An Italian version:
voice="paola", and ask the model to write in Italian. In Italian 27.4 is written «27,4»: does your check still pass?
