aws-iot-device-sdk-rust 0.8.1

An easy to use SDK for connecting to AWS IoT Core.
Documentation

Documentation crates.io License: MIT

aws-iot-device-sdk-rust

A Rust SDK for connecting IoT devices to AWS IoT Core via MQTT 3.1.1. Built on top of rumqttc with TLS powered by rustls.

Features

  • Async and sync clients for connecting to AWS IoT Core
  • Mutual TLS authentication using X.509 certificates
  • Publish and subscribe to MQTT topics
  • Broadcast incoming messages to multiple receivers
  • Configurable MQTT options (keep-alive, packet size, last will, etc.)

Installation

Add the following to your Cargo.toml:

[dependencies]
aws-iot-device-sdk-rust = "0.8"

By default the async client is enabled. To use the sync client instead:

[dependencies]
aws-iot-device-sdk-rust = { version = "0.8", default-features = false, features = ["sync"] }

You can also enable both:

[dependencies]
aws-iot-device-sdk-rust = { version = "0.8", features = ["sync"] }

Prerequisites

To connect to AWS IoT Core you need:

  1. An AWS IoT thing registered in your account
  2. A root CA certificate (e.g. AmazonRootCA1.pem)
  3. A device certificate (*.crt)
  4. A private key (*.pem)
  5. Your AWS IoT endpoint (found in the AWS IoT Core console under Settings)

Usage

Async client

Create an AWSIoTAsyncClient, spawn an event loop listener on a separate task, and use receivers to process incoming messages. Multiple receivers can be created to fan out messages to different parts of your application.

use aws_iot_device_sdk_rust::{
    async_event_loop_listener, AWSIoTAsyncClient, AWSIoTSettings, Packet, QoS,
};
use std::error::Error;

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
    let aws_settings = AWSIoTSettings::new(
        "my-device-id".to_owned(),
        "AmazonRootCA1.pem".to_owned(),
        "device-cert.crt".to_owned(),
        "private-key.pem".to_owned(),
        "your-endpoint.iot.region.amazonaws.com".to_owned(),
        None,
    );

    let (iot_core_client, eventloop_stuff) = AWSIoTAsyncClient::new(aws_settings).await?;

    iot_core_client.subscribe("my/topic", QoS::AtMostOnce).await?;
    iot_core_client.publish("my/topic", QoS::AtMostOnce, "hello").await?;

    let mut receiver = iot_core_client.get_receiver().await;

    let recv_thread = tokio::spawn(async move {
        loop {
            if let Ok(event) = receiver.recv().await {
                match event {
                    Packet::Publish(p) => {
                        println!("Topic: {}, Payload: {:?}", p.topic, p.payload);
                    }
                    _ => println!("Other event: {:?}", event),
                }
            }
        }
    });

    let listen_thread = tokio::spawn(async move {
        async_event_loop_listener(eventloop_stuff).await.unwrap();
    });

    tokio::join!(recv_thread, listen_thread);
    Ok(())
}

Sync client

The sync client works the same way but uses std::thread instead of tokio. Enable it with the sync feature.

use aws_iot_device_sdk_rust::{
    sync_client::event_loop_listener, AWSIoTClient, AWSIoTSettings, QoS,
};
use std::error::Error;

fn main() -> Result<(), Box<dyn Error>> {
    let aws_settings = AWSIoTSettings::new(
        "my-device-id".to_owned(),
        "AmazonRootCA1.pem".to_owned(),
        "device-cert.crt".to_owned(),
        "private-key.pem".to_owned(),
        "your-endpoint.iot.region.amazonaws.com".to_owned(),
        None,
    );

    let (mut iot_core_client, event_loop) = AWSIoTClient::new(aws_settings)?;

    std::thread::spawn(move || event_loop_listener(event_loop));

    iot_core_client.subscribe("my/topic/#", QoS::AtMostOnce)?;

    let mut receiver = iot_core_client.get_receiver()?;

    let recv_thread = std::thread::spawn(move || loop {
        if let Ok(event) = receiver.recv() {
            println!("Received: {:?}", event);
        }
    });

    recv_thread.join().unwrap();
    Ok(())
}

MQTT options

You can customize MQTT connection options by passing MQTTOptionsOverrides to AWSIoTSettings:

use aws_iot_device_sdk_rust::settings::MQTTOptionsOverrides;
use std::time::Duration;

let mut overrides = MQTTOptionsOverrides::default();
overrides.keep_alive = Some(Duration::from_secs(30));
overrides.clean_session = Some(true);
overrides.conn_timeout = Some(10);

let aws_settings = AWSIoTSettings::new(
    "my-device-id".to_owned(),
    "AmazonRootCA1.pem".to_owned(),
    "device-cert.crt".to_owned(),
    "private-key.pem".to_owned(),
    "your-endpoint.iot.region.amazonaws.com".to_owned(),
    Some(overrides),
);

Using the underlying rumqttc client directly

If you need more control, you can retrieve the underlying rumqttc client and work with it directly:

let (iot_core_client, (eventloop, _)) = AWSIoTAsyncClient::new(aws_settings).await?;
let client = iot_core_client.get_client().await;
// Use `client` and `eventloop` with the rumqttc API directly

Error handling

All fallible operations return Result<_, AWSIoTError>. The error type wraps the underlying rumqttc and I/O errors:

Variant Description
AWSConnectionError MQTT connection failure (wraps rumqttc::ConnectionError)
MQTTClientError Client operation failure such as publish/subscribe (wraps rumqttc::ClientError)
IoError File I/O error when reading certificates or keys
MutexError Mutex poisoning (sync client only)
KeyNormalizationError Private key normalization failed: non-UTF-8 key data, malformed PEM structure, invalid base64, unrecognized EC curve, or PKCS8 re-encoding failure

License

This project is licensed under the MIT License.