Raspberry Pi + Google Assistant SDK: Trợ lý giọng nói cơ bản
Trung cấp1/8/2026- Tác giả: IoTlabs Maker

Raspberry Pi + Google Assistant SDK: Trợ lý giọng nói cơ bản

Dựng trợ lý giọng nói cơ bản trên Raspberry Pi 4 bằng Google Assistant SDK (gRPC), hỏi đáp bằng giọng nói qua mic và loa I2S, kích hoạt bằng nút bấm.

Raspberry PiGoogle Assistant SDKPythonVoice AssistantgRPCI2S

Dự án hướng dẫn dựng một trợ lý giọng nói cơ bản trên Raspberry Pi 4 dùng Google Assistant Library / SDK (qua gRPC): nhấn nút để bắt đầu hỏi, Google Assistant xử lý câu hỏi trên cloud và trả lời bằng giọng nói qua loa I2S.

Khác với các bài offline khác trong series (Whisper, Porcupine, TTS nội bộ), dự án này minh hoạ cách tích hợp dịch vụ trợ lý giọng nói thương mại đầy đủ tính năng (kiến thức tổng quát, thời tiết, lịch, nhắc việc...) — đánh đổi lại là cần kết nối Internet và tài khoản Google Cloud.

Bài phù hợp để so sánh trực tiếp ưu/nhược điểm giữa giải pháp tự xây (offline, riêng tư, giới hạn tính năng) và giải pháp thương mại (online, nhiều tính năng, phụ thuộc dịch vụ bên thứ ba) khi thiết kế sản phẩm IoT thực tế.

Hướng dẫn chi tiết

Dựng trợ lý giọng nói cơ bản trên Raspberry Pi 4 bằng Google Assistant SDK, hỏi đáp qua mic và loa I2S, kích hoạt bằng nút bấm.

1. Giới thiệu

Dự án hướng dẫn dựng một trợ lý giọng nói cơ bản trên Raspberry Pi 4 dùng Google Assistant Library / SDK (qua gRPC): nhấn nút để bắt đầu hỏi, Google Assistant xử lý câu hỏi trên cloud và trả lời bằng giọng nói qua loa I2S.

Khác với các bài offline khác trong series (Whisper, Porcupine, TTS nội bộ), dự án này minh hoạ cách tích hợp dịch vụ trợ lý giọng nói thương mại đầy đủ tính năng (kiến thức tổng quát, thời tiết, lịch, nhắc việc...) — đánh đổi lại là cần kết nối Internet và tài khoản Google Cloud.

Bài phù hợp để so sánh trực tiếp ưu/nhược điểm giữa giải pháp tự xây (offline, riêng tư, giới hạn tính năng) và giải pháp thương mại (online, nhiều tính năng, phụ thuộc dịch vụ bên thứ ba) khi thiết kế sản phẩm IoT thực tế.

2. Linh kiện cần có

Linh kiện

Số lượng

Giá tham khảo

Raspberry Pi 4 Model B (4GB trở lên)

1

~1.800.000đ

Mic I2S MEMS INMP441

1

~90.000đ

Loa I2S (MAX98357A + loa 3W)*

1

~120.000đ

Nút nhấn (push-to-talk)

1

~2.000đ

MicroSD 32GB Class 10

1

~120.000đ

Nguồn 5V/3A USB-C

1

~80.000đ

Ghi chú thay thế linh kiện: cùng module loa tự tạo "MAX98357A I2S Mono Amplifier + Loa 3W" đã dùng ở bài Text-to-Speech trong series, vì hardware_catalog chưa có module loa/DAC âm thanh nào.

Yêu cầu tài khoản: cần tài khoản Google Cloud (có bật Google Assistant API), thiết lập OAuth credentials qua google-oauthlib-tool theo hướng dẫn chính thức của Google trước khi chạy được code trong bài. Không có sẵn tài khoản demo miễn phí dùng chung.

3. Sơ đồ kết nối

INMP441 (Mic I2S)

Raspberry Pi 4

VCC

3V3 (Pin 1)

GND

GND

SCK

GPIO18 / PCM_CLK (Pin 12) — chia sẻ BCLK với loa

WS

GPIO17 (Pin 11) — chia sẻ LRC với loa*

SD

GPIO27 (Pin 13)

L/R

GND

Loa I2S (MAX98357A)

Raspberry Pi 4

VIN

5V (Pin 2)

GND

GND

BCLK

GPIO18 (chia sẻ với mic SCK)

LRC

GPIO17 (chia sẻ với mic WS)

DIN

GPIO22 (Pin 15)

SD (enable)

3V3 (kéo lên = luôn bật amp)

Nút nhấn

Raspberry Pi 4

Chân A

GPIO4 (Pin 7), INPUT_PULLUP

Chân B

GND

Thiết kế chia sẻ BCLK/LRC giữa mic và loa phản ánh đúng kiến trúc I2S full-duplex thực tế của Raspberry Pi: một bus I2S vật lý (GPIO18=BCLK, GPIO19=LRCLK chuẩn) dùng chung xung clock/word-select cho cả luồng ghi (mic → GPIO20/DIN) và luồng phát (→ loa qua GPIO21/DOUT), chỉ khác đường dữ liệu.

Sơ đồ minh hoạ dùng GPIO17 làm WS/LRC chung và GPIO27/GPIO22 làm 2 đường dữ liệu riêng (SD của mic, DIN của loa) — khi triển khai thực tế, dùng đúng bộ chân I2S chuẩn (GPIO18/19/20/21) và cấu hình ALSA cho thiết bị audio full-duplex.

4. Code ví dụ #1 — Nút push-to-talk kích hoạt truy vấn

File button_trigger.py: chờ nhấn nút GPIO4 để bắt đầu một lượt hỏi trợ lý (thay cho việc dùng wake word liên tục — đơn giản hơn cho bản demo cơ bản).

#!/usr/bin/env python3
"""button_trigger.py - Cho nhan nut push-to-talk qua GPIO4 de kich hoat truy van tro ly."""

import time

try:
    import RPi.GPIO as GPIO
except ImportError:
    GPIO = None

BUTTON_PIN = 4


def gpio_init() -> None:
    if GPIO is None:
        return
    GPIO.setmode(GPIO.BCM)
    GPIO.setwarnings(False)
    GPIO.setup(BUTTON_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP)


def button_pressed() -> bool:
    if GPIO is None:
        return False
    return GPIO.input(BUTTON_PIN) == GPIO.LOW


def wait_for_press(poll_interval_s: float = 0.02) -> None:
    """Blocking: doi den khi nut duoc nhan."""
    print("Cham nut de bat dau hoi tro ly...")
    while not button_pressed():
        time.sleep(poll_interval_s)
    print("Da nhan nut - bat dau ghi am cau hoi.")


def cleanup() -> None:
    if GPIO is not None:
        GPIO.cleanup()


if __name__ == "__main__":
    gpio_init()
    try:
        wait_for_press()
    finally:
        cleanup()

5. Code ví dụ #2 — Gọi Google Assistant SDK

File assistant_query.py: kiểm tra credentials và minh hoạ luồng gửi câu hỏi tới Google Assistant. Phần gọi gRPC audio-stream thật được rút gọn thành text-in/text-out để ví dụ chạy được mà không cần kết nối Google Cloud khi đọc bài — khi triển khai thật, thay bằng lời gọi google.assistant.embedded.v1alpha2 streaming audio.

#!/usr/bin/env python3
"""assistant_query.py - Goi Google Assistant SDK (gRPC) de gui 1 cau hoi bang giong noi."""

import json
import os
from pathlib import Path

CREDENTIALS_PATH = Path.home() / ".config" / "google-oauthlib-tool" / "credentials.json"
DEVICE_CONFIG_PATH = Path.home() / ".config" / "googlesamples-assistant" / "device_config.json"


def check_credentials() -> bool:
    """Kiem tra credentials da duoc thiet lap truoc khi goi Assistant SDK."""
    if not CREDENTIALS_PATH.exists():
        print(f"Thieu credentials tai {CREDENTIALS_PATH}. Chay google-oauthlib-tool truoc.")
        return False
    if not DEVICE_CONFIG_PATH.exists():
        print(f"Thieu device config tai {DEVICE_CONFIG_PATH}. Dang ky device truoc.")
        return False
    return True


def load_device_config() -> dict:
    with open(DEVICE_CONFIG_PATH, "r", encoding="utf-8") as f:
        return json.load(f)


def send_text_query(text: str) -> str:
    """Mo phong goi Assistant SDK bang van ban (thay cho audio stream that)."""
    if not check_credentials():
        return "Loi: chua thiet lap Google Assistant credentials."
    print(f"[Gui cau hoi toi Google Assistant]: {text}")
    return f"(Mo phong) Google Assistant da nhan cau hoi: '{text}'"


if __name__ == "__main__":
    response = send_text_query("Hom nay troi the nao?")
    print(f"[Phan hoi]: {response}")

6. Code ví dụ #3 — Ứng dụng hoàn chỉnh: nút → hỏi trợ lý → phát trả lời

File main.py: vòng lặp hoàn chỉnh — nhấn nút, gửi câu hỏi, nhận trả lời và đọc qua loa I2S bằng TTS nội bộ.

#!/usr/bin/env python3
"""main.py - Tich hop hoan chinh: nut push-to-talk -> goi Google Assistant SDK -> phat tra loi."""

import json
import signal
import sys
import time
from pathlib import Path

try:
    import RPi.GPIO as GPIO
except ImportError:
    GPIO = None

import pyttsx3

BUTTON_PIN = 4
CREDENTIALS_PATH = Path.home() / ".config" / "google-oauthlib-tool" / "credentials.json"
DEVICE_CONFIG_PATH = Path.home() / ".config" / "googlesamples-assistant" / "device_config.json"

_running = True


def gpio_init() -> None:
    if GPIO is None:
        return
    GPIO.setmode(GPIO.BCM)
    GPIO.setwarnings(False)
    GPIO.setup(BUTTON_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP)


def button_pressed() -> bool:
    if GPIO is None:
        return False
    return GPIO.input(BUTTON_PIN) == GPIO.LOW


def check_credentials() -> bool:
    if not CREDENTIALS_PATH.exists():
        print(f"Thieu credentials tai {CREDENTIALS_PATH}. Chay google-oauthlib-tool truoc.")
        return False
    if not DEVICE_CONFIG_PATH.exists():
        print(f"Thieu device config tai {DEVICE_CONFIG_PATH}. Dang ky device truoc.")
        return False
    return True


def record_query_placeholder() -> str:
    """Placeholder ghi am + STT qua Google Assistant audio stream."""
    return "Hom nay troi the nao?"


def query_assistant(text: str) -> str:
    if not check_credentials():
        return "Xin loi, chua the ket noi Google Assistant."
    print(f"[Gui cau hoi]: {text}")
    return f"(Mo phong) Day la cau tra loi cho: {text}"


def speak(engine, text: str) -> None:
    print(f"[TTS]: {text}")
    engine.say(text)
    engine.runAndWait()


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


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

    engine = pyttsx3.init()
    print("San sang. Cham nut de hoi tro ly. Ctrl+C de dung.")

    try:
        while _running:
            while not button_pressed() and _running:
                time.sleep(0.02)
            if not _running:
                break

            query_text = record_query_placeholder()
            answer = query_assistant(query_text)
            speak(engine, answer)

            time.sleep(1)
    finally:
        engine.stop()
        if GPIO is not None:
            GPIO.cleanup()
        print("\nDa dung.")


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

7. Lỗi thường gặp

Lỗi

Nguyên nhân

Cách sửa

Thiếu credentials tại ~/.config/google-oauthlib-tool/credentials.json

Chưa chạy bước OAuth setup

Chạy google-oauthlib-tool theo hướng dẫn chính thức Google Assistant SDK, đăng nhập tài khoản Google, tải file credentials về đúng đường dẫn

PermissionDenied khi gọi Assistant API

Chưa bật Google Assistant API trong Google Cloud Console cho project

Vào Google Cloud Console → APIs & Services → bật "Google Assistant API"

Không nghe/ghi được âm thanh dù credentials đúng

Cấu hình ALSA full-duplex (mic + loa cùng lúc) chưa đúng

Kiểm tra arecord -laplay -l đều thấy thiết bị I2S, cấu hình đúng driver hỗ trợ ghi/phát đồng thời

Trợ lý phản hồi chậm hoặc timeout

Kết nối Internet không ổn định — dịch vụ Google Assistant cần mạng liên tục

Kiểm tra kết nối mạng, đây là đánh đổi cố hữu khi dùng dịch vụ cloud thay vì giải pháp offline

ModuleNotFoundError: No module named 'google.assistant'

Chưa cài đúng gói SDK

Cài theo đúng hướng dẫn chính thức: pip install google-assistant-library google-assistant-grpc

8. Tổng kết

Dự án đã dựng khung trợ lý giọng nói cơ bản tích hợp Google Assistant SDK: nút kích hoạt, gửi truy vấn, nhận và phát trả lời qua loa I2S. So với các bài offline khác trong series, cách tiếp cận này đánh đổi sự riêng tư/độc lập mạng để lấy tính năng đầy đủ và chất lượng giọng nói cao hơn, lựa chọn phù hợp phụ thuộc vào yêu cầu sản phẩm cụ thể (thiết bị dùng nơi không có mạng nên chọn hướng offline như Whisper/Porcupine; thiết bị cần trả lời kiến thức tổng quát nên cân nhắc dịch vụ cloud).

Lưu ý quan trọng: phần gọi Google Assistant SDK trong bài là khung sườn minh hoạ (text placeholder thay cho audio stream gRPC thật) để giữ code mẫu độc lập, compile được và không yêu cầu tài khoản Google Cloud khi đọc bài.

Khi triển khai thực tế, cần thay query_assistant()/record_query_placeholder() bằng lời gọi gRPC streaming audio thật theo tài liệu chính thức của Google, và tự kiểm thử trên phần cứng thật trước khi coi là hoàn chỉnh.