0
0
Lập trình
Harry Tran
Harry Tran106580903228332612117

Bắt đầu với AWS IoT Core, ESP32, MQTT, Rust & Terraform

Đăng vào 3 tuần trước

• 8 phút đọc

Giới thiệu

Hầu hết các ví dụ/tutorial về ESP32 và AWS IoT Core đều sử dụng framework Arduino và dựa vào việc thao tác thủ công qua bảng điều khiển AWS. Cách tiếp cận này có thể hoạt động cho các bản demo nhanh, nhưng nhanh chóng trở nên rối rắm và dễ mắc lỗi—đặc biệt nếu bạn cần tái tạo cấu hình hoặc mở rộng cho nhiều thiết bị.

Gần đây, tôi đã tạo một kho lưu trữ giúp cho việc kết nối các thiết bị ESP32 với AWS IoT Core trở nên dễ dàng hơn với Infrastructure-as-Code và firmware hiện đại viết bằng Rust sử dụng framework Espressif ESP-IDF.

Các yêu cầu cần thiết

  • Các công cụ cần thiết để làm cho ví dụ này hoạt động được mô tả trong kho lưu trữ.
  • Hiểu biết cơ bản về giao thức MQTT.
  • Hiểu biết cơ bản về Terraform.
  • Clone kho lưu trữ:
Copy
git clone https://github.com/RamMaths/aws-iot-esp32-example.git

Chúng ta sẽ xây dựng gì?

Thay vì một demo “hello world” khác, mục tiêu ở đây là tạo ra một mẫu tối thiểu nhưng có thể mở rộng cho các thiết bị ESP32 giao tiếp với AWS IoT Core.

Bước 1: Đăng ký các chủ đề

Trong MQTT, một chủ đề hoạt động như một kênh giao tiếp giữa các thiết bị và dịch vụ.

  • Các thiết bị ESP32 đăng ký chủ đề: esp32/1
  • AWS IoT Core Test Client đăng ký chủ đề: esp32/2

Cấu hình này đảm bảo rằng:

  • Các lệnh gửi từ client thử nghiệm trên esp32/1 sẽ được nhận bởi các thiết bị ESP32.
  • Phản hồi từ thiết bị được công bố lên esp32/2 sẽ được nhận bởi client thử nghiệm.

Bước 2: Công bố một lệnh từ Client thử nghiệm

Tiếp theo, chúng ta sử dụng AWS IoT Test Client để công bố một thông điệp ở định dạng JSON lên chủ đề esp32/1.

Copy
{
    "message": "ping"
}
  • Trường message chứa lệnh mà chúng ta muốn thiết bị ESP32 thực hiện.
  • Trong ví dụ này, chúng ta gửi "ping".
  • Firmware lắng nghe giá trị này và so khớp với các hành động đã định nghĩa trước (ví dụ, phản hồi với "pong").

Sử dụng JSON giữ cho giao tiếp đơn giản và có thể mở rộng—bạn có thể sau này thêm nhiều trường hoặc lệnh mới mà không cần thay đổi cấu trúc tổng thể.

Bước 3: Phản hồi từ thiết bị

Khi một thiết bị ESP32 nhận được lệnh, firmware sẽ so khớp nó với các hành động đã định nghĩa trước.

Đối với lệnh ping, thiết bị sẽ phản hồi bằng cách công bố:

Copy
{
  "message": "pong"
}

đến chủ đề esp32/2.
Vì client thử nghiệm đã đăng ký chủ đề này, nó sẽ ngay lập tức nhận được phản hồi.

🔧 Ví dụ này chỉ triển khai một workflow ping → pong đơn giản, nhưng dự án này được thiết kế để mở rộng. Ví dụ, nếu một thiết bị đăng ký esp32/light, bạn có thể thêm các lệnh như:

Copy
{ "message": "on" }
{ "message": "off" }

Lấy tài nguyên đám mây

Với kho lưu trữ đã được clone và CLI Terraform đã được cài đặt, chúng ta có thể bắt đầu cung cấp các tài nguyên AWS IoT Core cần thiết.

1. Khởi tạo Terraform

Đi vào thư mục Terraform và khởi tạo dự án:

Copy
cd terraform
terraform init

👉 Hãy chắc chắn rằng thông tin xác thực AWS của bạn đã được cấu hình (aws configure) trước khi tiếp tục.

2. Cấu hình các biến

Sao chép tệp biến mẫu:

Copy
cp terraform.tfvars.example terraform.tfvars

Mở terraform.tfvars và chỉnh sửa mảng things. Mỗi thing đại diện cho một thiết bị IoT trong AWS IoT Core. Đối với mỗi thiết bị, bạn định nghĩa:

  • name: định danh thiết bị trong AWS IoT
  • topic_prefix: không gian tên chủ đề MQTT mà thiết bị có thể công bố/đăng ký

Ví dụ cấu hình cho hai thiết bị ESP32 (S3 và C3):

Copy
# Các biến Terraform cho ví dụ AWS IoT ESP32
# Sao chép tệp này vào terraform.tfvars và tùy chỉnh giá trị

# Danh sách các IoT Things để tạo với cấu hình của chúng
things = [
  {
    name         = "esp32s3"
    topic_prefix = "esp32"
  },
  {
    name         = "esp32c3"
    topic_prefix = "esp32"
  }
]

# Khu vực AWS nơi các tài nguyên IoT sẽ được tạo
region = "us-east-1"

# Thẻ để áp dụng cho tất cả các tài nguyên AWS
tags = {
  Project     = "ESP32-IoT-Example"
  Environment = "development"
  ManagedBy   = "terraform"
  Owner       = "tên-của-bạn"
}

3. Áp dụng Terraform

Cuối cùng, triển khai các tài nguyên:

Copy
terraform plan
terraform apply

Điều này sẽ tạo:

  • IoT Things cho mỗi thiết bị (esp32s3, esp32c3)
  • Chứng chỉ và chính sách (tự động tải xuống vào certs/)
  • Quyền truy cập chủ đề dựa trên các tiền tố bạn đã chỉ định.

✅ Tại thời điểm này, môi trường AWS của bạn đã được cung cấp hoàn toàn và sẵn sàng để các thiết bị ESP32 kết nối một cách an toàn.

Biên dịch Firmware

Với các tài nguyên đám mây đã được cung cấp bởi Terraform, bước tiếp theo là cấu hình, xây dựng và nạp firmware ESP32.

1. Cấu hình Dự án

Đi vào thư mục firmware cho phiên bản ESP32 của bạn:

Sau khi terraform tạo ra các tài nguyên, nó sẽ xuất ra một cái gì đó như thế này:

Copy
cd firmware/example

(Tham khảo hướng dẫn cài đặt README để biết chi tiết về việc cấu hình các mục tiêu Xtensa và RISC-V.)

2. Tạo cfg.toml

Terraform xuất ra chi tiết kết nối mà bạn cần. Cấu hình của bạn nên trông như thế này:

Copy
[example]
# Cấu hình Wi-Fi
wifi_ssid = "MẠNG_WIFI_CỦA_BẠN"
wifi_pass = "MẬT_KHẨU_WIFI_CỦA_BẠN"
mqtt_url = "mqtts://your-endpoint.iot.region.amazonaws.com"
mqtt_client_id = "thing_name"
mqtt_topic_pub = "topic_prefix"
mqtt_topic_sub = "topic_prefix"
cert_ca = "certs/AmazonRootCA1.pem"
cert_crt = "certs/[certificate-id]-certificate.pem.crt"
cert_key = "certs/[certificate-id]-private.pem.key"
  • Thay thế wifi_ssidwifi_pass bằng thông tin mạng của bạn.
  • Đặt mqtt_topic_pub = "esp32/2" và mqtt_topic_sub = "esp32/1".
  • Giữ nguyên phần còn lại như được tạo ra bởi Terraform.

Lưu tệp này dưới dạng cfg.toml trong dự án firmware của bạn.

3. Sao chép Chứng chỉ

Sao chép các chứng chỉ cho thiết bị của bạn vào thư mục firmware. Thư mục phải được đặt tên là certs:

Copy
cp -r ../../terraform/certs/thing_name ./certs

4. Xây dựng và Nạp

Bây giờ, biên dịch và nạp firmware vào ESP32 của bạn:

Copy
cargo build --release
cargo espflash ./path/to/the/binary -p /path/to/your/esp32/port
cargo espmonitor /path/to/your/esp32/port

Những gì mong đợi trong log

Khi ESP32 kết nối với Wi-Fi và thiết lập một phiên MQTT với AWS IoT Core, bạn sẽ thấy các log tương tự như sau:

Copy
I (1234) example: WiFi connected successfully
I (1235) example: MQTT client created successfully
I (1236) example: Subscribed to topic "esp32/1"
I (1237) example: Starting main application loop

Các thông điệp này xác nhận rằng:

  • Thiết bị của bạn đã gia nhập mạng Wi-Fi
  • Client MQTT đã được khởi tạo đúng cách
  • Đăng ký vào chủ đề kỳ vọng đã thành công
  • Ứng dụng hiện đang chạy và sẵn sàng xử lý các thông điệp

Các lỗi thường gặp

Thường thì bạn sẽ thấy các thông điệp giống như:

Copy
Failed to subscribe to topic "esp32/1": {}, retrying...

Điều này xảy ra trong khi AWS IoT xác thực các chứng chỉ và thiết lập kết nối an toàn. Miễn là các lần thử lại cuối cùng thành công và bạn thấy thông điệp đăng ký, cấu hình của bạn đang hoạt động đúng.

🎉 Xin chúc mừng!

Nếu bạn đã theo dõi đến đây, bạn đã đi một chặng đường dài — từ việc thiết lập các tài nguyên đám mây với Terraform, đến việc cấu hình chứng chỉ, nạp firmware, và thấy ESP32 của bạn kết nối an toàn với AWS IoT Core. Đó là một thành tựu lớn! 🚀

Cảm ơn bạn đã đọc và lập trình cùng. Tôi hy vọng bạn không chỉ làm cho mọi thứ hoạt động mà còn học được điều gì đó mới mẻ về cách ESP32, MQTT và AWS IoT kết hợp với nhau.

🧑‍💻 Còn gì tiếp theo? Thử nghiệm!

Kho lưu trữ này được xem như một điểm khởi đầu. Hãy thoải mái khám phá và làm cho nó trở thành của riêng bạn:

  • Thêm nhiều lệnh hơn ngoài demo ping → pong đơn giản

  • Khám phá cách kết nối Wi-Fi được thiết lập trong startup.rs

  • Xem xét kỹ lưỡng client MQTT chung của chúng tôi trong client.rs

    • Nó chạy một luồng riêng biệt để lắng nghe tin nhắn
    • Sử dụng crossbeam_channel để chuyển tiếp tin nhắn đến luồng chính một cách an toàn
  • Thử nghiệm với việc công bố các lệnh JSON có cấu trúc hoặc thậm chí dữ liệu cảm biến

🔧 Mở rộng Vòng lặp Chính

Bên trong main.rs, bạn sẽ tìm thấy vòng lặp chính nơi các tin nhắn MQTT đến được so khớp với các lệnh. Hiện tại nó chỉ xử lý "ping". Đây là nơi bạn có thể mở rộng nó:

Copy
match msg.message.as_str() {
    "ping" => {
        info!("Ping received, sending pong");
        JsonMessage {
            message: format!("pong from: {}", app.config.mqtt_client_id),
        }
    }
    "light_on" => {
        info!("Turning light ON");
        JsonMessage {
            message: "Light is now ON".to_string(),
        }
    }
    "light_off" => {
        info!("Turning light OFF");
        JsonMessage {
            message: "Light is now OFF".to_string(),
        }
    }
    _ => {
        warn!("Unknown action: {}", msg.message);
        JsonMessage {
            message: format!("Unknown action: {}", msg.message),
        }
    }
}

Bạn có thể sử dụng mẫu này để kết nối bất kỳ hành động nào của thiết bị — bật tắt chân GPIO, điều khiển các thiết bị ngoại vi, hoặc gửi phản hồi có cấu trúc trở lại AWS IoT Core.

🙌 Cảm ơn bạn

Cảm ơn bạn một lần nữa vì đã đọc. Bây giờ hãy đi và thử nghiệm, phá vỡ mọi thứ, sửa chúng lại, và xây dựng điều gì đó tuyệt vời. Đó là nơi việc học thực sự diễn ra.

Nếu bạn thấy điều này hữu ích:

  • ⭐ Đánh dấu kho lưu trữ trên GitHub để hỗ trợ dự án
  • 💬 Chia sẻ phản hồi, câu hỏi hoặc cải tiến trong phần thảo luận/vấn đề của kho lưu trữ
  • 🚀 Chia sẻ những gì bạn đã xây dựng với mẫu này — tôi rất muốn thấy các dự án của bạn!
Gợi ý câu hỏi phỏng vấn
Không có dữ liệu

Không có dữ liệu

Bài viết được đề xuất
Bài viết cùng tác giả

Bình luận

Chưa có bình luận nào

Chưa có bình luận nào