Raspberry Pi + Text-to-Speech: Speaking Sensor Readings Aloud
Intermediate1/8/2026- Author: IoTSpark Maker

Raspberry Pi + Text-to-Speech: Speaking Sensor Readings Aloud

Read temperature/humidity from a DHT22 sensor and speak it aloud through an I2S speaker on a Raspberry Pi 4, using pyttsx3/espeak-ng for fully offline speech synthesis.

Raspberry PiText-to-SpeechTTSDHT22PythonVoiceOffline
0 steps3 components

This project builds a voice-announcement (Text-to-Speech) system on a Raspberry Pi 4: periodically reading temperature and humidity from a DHT22 sensor, then speaking them through a speaker as a natural sentence (e.g. "The current temperature is 28 degrees C, humidity 65 percent") instead of just showing numbers on a screen. TTS runs fully offline using the espeak-ng/pyttsx3 engine — no cloud API required.

This is a "mirror image" of the STT lessons elsewhere in this series: instead of speech → text (Whisper, Porcupine), this lesson goes from sensor data → text → speech, completing the two-way interaction loop for a talking IoT device.

Detailed guide

Read temperature/humidity from a DHT22 sensor and speak it aloud offline through an I2S speaker on a Raspberry Pi 4, using pyttsx3/espeak-ng.

1. Introduction

This project builds a voice-announcement (Text-to-Speech) system on a Raspberry Pi 4: periodically reading temperature and humidity from a DHT22 sensor, then speaking them through a speaker as a natural sentence (e.g. "The current temperature is 28 degrees C, humidity 65 percent") instead of just showing numbers on a screen. TTS runs fully offline using the espeak-ng/pyttsx3 engine — no cloud API required.

This is a "mirror image" of the STT lessons elsewhere in this series: instead of speech → text (Whisper, Porcupine), this lesson goes from sensor data → text → speech, completing the two-way interaction loop for a talking IoT device.

2. Components Needed

ComponentQtyReference Price
Raspberry Pi 4 Model B (2GB or more)1~1,500,000₫
DHT22 (AM2302) Temperature/Humidity Sensor1~65,000₫
I2S Speaker (MAX98357A + 3W speaker)*1~120,000₫
32GB Class 10 MicroSD1~120,000₫
5V/3A USB-C Power Supply1~80,000₫

* Component substitution note: the IoTLabs Maker hardware_catalog has no speaker/audio-DAC module yet, so this lesson uses a custom "MAX98357A I2S Mono Amplifier + 3W Speaker" module based on the public Adafruit MAX98357A I2S Class-D Amplifier Breakout datasheet — a popular, inexpensive choice for playing audio on a Raspberry Pi over I2S (no separate USB DAC needed).

3. Wiring Diagram

DHT22Raspberry Pi 4
VCC3V3 (Pin 1)
GNDGND
DATAGPIO4 (Pin 7)
I2S Speaker (MAX98357A)Raspberry Pi 4
VIN5V (Pin 2)
GNDGND
BCLKGPIO18 / PCM_CLK (Pin 12)
LRCGPIO17 (Pin 11)*
DINGPIO27 (Pin 13)*
SD (enable)3V3 (pulled high = amp always on)

Same note as the other lessons in this series: the Raspberry Pi's standard physical I2S pinout uses GPIO18 (BCLK), GPIO19 (LRCLK), GPIO21 (DOUT).

This diagram uses GPIO17/GPIO27 to illustrate the connection logic. For a real deployment, use the correct standard I2S pins and enable the matching audio driver (e.g. dtoverlay=hifiberry-dac or the driver matching the MAX98357A board) in /boot/config.txt.

The DHT22 should have a 10kΩ pull-up resistor on its DATA pin if the wire run is long or the signal is unstable (per the datasheet's recommendation).

4. Example #1 — Reading the DHT22 Sensor

File read_dht22.py: reads temperature/humidity, retrying on checksum errors (a normal occurrence with cheap 1-Wire-style sensors).

#!/usr/bin/env python3
"""read_dht22.py - Doc nhiet do/do am tu cam bien DHT22 qua GPIO4."""

import time

try:
    import adafruit_dht
    import board

    _DHT_AVAILABLE = True
except (ImportError, NotImplementedError):
    _DHT_AVAILABLE = False

DATA_PIN_NAME = "D4"  # GPIO4


def create_sensor():
    if not _DHT_AVAILABLE:
        return None
    pin = getattr(board, DATA_PIN_NAME)
    return adafruit_dht.DHT22(pin)


def read_once(sensor, retries: int = 3, delay_s: float = 2.0):
    """Doc 1 lan, tra ve (temperature_c, humidity_pct) hoac (None, None) neu loi."""
    for attempt in range(retries):
        try:
            temperature_c = sensor.temperature
            humidity_pct = sensor.humidity
            if temperature_c is not None and humidity_pct is not None:
                return temperature_c, humidity_pct
        except RuntimeError as exc:
            print(f"[Doc loi lan {attempt + 1}/{retries}]: {exc}")
        time.sleep(delay_s)
    return None, None


def main() -> None:
    sensor = create_sensor()
    if sensor is None:
        print("Khong co phan cung DHT22 that (chay tren may dev) - dung du lieu mo phong.")
        temperature_c, humidity_pct = 28.5, 65.0
    else:
        temperature_c, humidity_pct = read_once(sensor)

    if temperature_c is None:
        print("Khong doc duoc cam bien sau nhieu lan thu.")
        return
    print(f"Nhiet do: {temperature_c:.1f} C, Do am: {humidity_pct:.1f} %")


if __name__ == "__main__":
    main()

5. Example #2 — Offline Speech Synthesis (pyttsx3/espeak-ng)

File speak_text.py: converts a Vietnamese text string into speech through the I2S speaker. Requires espeak-ng to be installed at the system level first (sudo apt install espeak-ng).

#!/usr/bin/env python3
"""speak_text.py - Doc van ban tieng Viet thanh giong noi offline bang pyttsx3 (espeak-ng)."""

import pyttsx3

VOICE_RATE = 150  # tu/phut, cham hon mac dinh de de nghe tieng Viet


def create_engine():
    engine = pyttsx3.init()
    engine.setProperty("rate", VOICE_RATE)
    return engine


def speak(text: str, engine=None) -> None:
    own_engine = engine is None
    if engine is None:
        engine = create_engine()
    print(f"[TTS]: {text}")
    engine.say(text)
    engine.runAndWait()
    if own_engine:
        engine.stop()


if __name__ == "__main__":
    speak("Xin chao, day la thong bao thu nghiem tu Raspberry Pi.")

6. Example #3 — The Full Application: Periodic Sensor Readings → Spoken Announcements

File main.py: a loop that reads the DHT22 every 60 seconds, builds a natural sentence, and speaks it through the I2S speaker.

#!/usr/bin/env python3
"""main.py - Tich hop hoan chinh: doc DHT22 dinh ky -> tong hop cau thoai -> phat qua loa I2S."""

import signal
import sys
import time

try:
    import adafruit_dht
    import board

    _DHT_AVAILABLE = True
except (ImportError, NotImplementedError):
    _DHT_AVAILABLE = False

import pyttsx3

DATA_PIN_NAME = "D4"
READ_INTERVAL_S = 60
VOICE_RATE = 150

_running = True


def create_sensor():
    if not _DHT_AVAILABLE:
        return None
    pin = getattr(board, DATA_PIN_NAME)
    return adafruit_dht.DHT22(pin)


def read_once(sensor, retries: int = 3, delay_s: float = 2.0):
    if sensor is None:
        return 28.5, 65.0  # gia tri mo phong khi khong co phan cung that
    for attempt in range(retries):
        try:
            temperature_c = sensor.temperature
            humidity_pct = sensor.humidity
            if temperature_c is not None and humidity_pct is not None:
                return temperature_c, humidity_pct
        except RuntimeError as exc:
            print(f"[Doc loi lan {attempt + 1}/{retries}]: {exc}")
        time.sleep(delay_s)
    return None, None


def build_announcement(temperature_c: float, humidity_pct: float) -> str:
    return (
        f"Nhiet do hien tai la {temperature_c:.0f} do C, "
        f"do am {humidity_pct:.0f} phan tram."
    )


def shutdown(sig, frame):
    global _running
    _running = False


def main() -> None:
    global _running
    signal.signal(signal.SIGINT, shutdown)
    signal.signal(signal.SIGTERM, shutdown)

    sensor = create_sensor()
    engine = pyttsx3.init()
    engine.setProperty("rate", VOICE_RATE)

    print(f"San sang. Doc cam bien moi {READ_INTERVAL_S}s. Ctrl+C de dung.")
    try:
        while _running:
            temperature_c, humidity_pct = read_once(sensor)
            if temperature_c is None:
                print("Khong doc duoc cam bien, bo qua chu ky nay.")
            else:
                text = build_announcement(temperature_c, humidity_pct)
                print(f"[TTS]: {text}")
                engine.say(text)
                engine.runAndWait()

            for _ in range(READ_INTERVAL_S):
                if not _running:
                    break
                time.sleep(1)
    finally:
        engine.stop()
        print("\nDa dung.")


if __name__ == "__main__":
    main()
    sys.exit(0)

7. Common Issues

IssueCauseFix
RuntimeError: Checksum did not validate1-Wire-style signal noise, a wire run that's too long, or reading too fast (the DHT22 needs ≥2s between reads)Add a delay of at least 2s between reads, use a short wire, add a 10kΩ pull-up resistor on DATA
No sound is heard even though the code runs without errorsALSA hasn't picked up the I2S speaker as the default outputCheck aplay -l, configure the correct dtoverlay for the I2S DAC, and set the I2S speaker as default in ~/.asoundrc
The spoken voice sounds like English/unfamiliar instead of Vietnameseespeak-ng is missing the Vietnamese language pack, or pyttsx3 picked the wrong voiceCheck espeak-ng --voices=vi, select the correct Vietnamese voice via engine.setProperty('voice', ...)
ModuleNotFoundError: No module named 'board'The CircuitPython library wasn't installed correctly for the Raspberry Pi platformInstall via pip install adafruit-circuitpython-dht and pip install RPI.GPIO adafruit-blinka following Adafruit's Raspberry Pi guide
The program hangs for a long time at engine.runAndWait()The TTS driver is waiting on an unresponsive audio device (busy or missing)Recheck the ALSA configuration, test audio playback with speaker-test before running the TTS code

8. Summary

This project built a complete voice-announcement system: periodic sensor reads, building a natural sentence, and playing it through an I2S speaker — all running offline with no cloud dependency. This "read data → speak it" design pattern applies to any kind of IoT announcement (threshold alerts, device status, a startup greeting) just by changing the build_announcement() function.

A note on accuracy: the temperature/humidity accuracy figures (±0.5°C, ±2-5% RH) come from the DHT22's public datasheet — these are the manufacturer's nominal specs under standard conditions, not measurements verified on your specific board.

Offline TTS voice quality (espeak-ng) isn't as natural as a paid cloud TTS service — a reasonable trade-off for a fully offline solution.