Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added a MQTT pub/sub example of using AWS IoT #173

Merged
merged 3 commits into from
Aug 29, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions examples/aws_iot/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
PROGRAM=aws_iot
EXTRA_COMPONENTS = extras/paho_mqtt_c extras/mbedtls
include ../../common.mk
60 changes: 60 additions & 0 deletions examples/aws_iot/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
Please follow the steps below to build and run the example on your ESP8266.

1. Modify client_config.c to provide your own account-specific AWS IoT
endpoint, ECC-based client certificate, and private key.

Your endpoint is in the form of ```<prefix>.iot.<region>.amazonaws.com```.
It can be retrieved using the following command:

```sh
$ aws iot describe-endpoint
```

Your ECC-based certificate and private key can be generated by using
the following commands:

```sh
$ openssl ecparam -out ecckey.key -name prime256v1 -genkey
$ openssl req -new -sha256 -key ecckey.key -nodes -out eccCsr.csr
$ aws iot create-certificate-from-csr --certificate-signing-request file://eccCsr.csr --certificate-pem-outfile eccCert.crt --set-as-active
```

To convert the certificate or key file into C string, you could try
the following example:

```sh
$ cat ecckey.key | sed -e 's/^/"/g' | sed -e 's/$/\\r\\n"/g'
```

*Note, more information about using ECC-based certificate with AWS IoT
can be found in the following blog*

https://aws.amazon.com/blogs/iot/elliptic-curve-cryptography-and-forward-secrecy-support-in-aws-iot-3/

2. Create and attach AWS IoT access policy to the certificate

```sh
$ aws iot create-policy --policy-name test-thing-policy --policy-document '{ "Version": "2012-10-17", "Statement": [{"Action": ["iot:*"], "Resource": ["*"], "Effect": "Allow" }] }'
$ aws iot attach-principal-policy --policy-name test-thing-policy --principal "arn:aws:iot:eu-west-1:892804553548:cert/2d9c2da32a95b5e95a277c3b8f7af40869727f5259dc2e907fc8aba916c857e"
```

*Note, the 'principal' argument is the certificate ARN generated from the
pervious command 'aws iot create-certificate-from-csr'.*

3. Modify include/ssid_config.h with your Wifi access Id and credential.

4. Build and flash the example firmware to the device using the command
below:

```sh
$ make flash -C examples/aws_iot ESPPORT=/dev/ttyUSB0
```

*Note, it assumes your ESP8266 is connected through USB and exposed under
your Linux host as /dev/ttyUSB0.*

5. Once the ESP8266 is connected to AWS IoT, you can use the MQTT client
on the AWS IoT console to receive the messages published by the ESP8266
to topic 'esp8266/status'. You could also publish 'on' or 'off' message
to topic 'esp8266/control' to toggle the GPIO/LED (GPIO2 is used by the
example).
280 changes: 280 additions & 0 deletions examples/aws_iot/aws_iot.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,280 @@
/*
* Derived from examples/mqtt_client/mqtt_client.c - added TLS1.2 support and some minor modifications.
*/
#include "espressif/esp_common.h"
#include "esp/uart.h"

#include <string.h>

#include <FreeRTOS.h>
#include <task.h>
#include <queue.h>
#include <ssid_config.h>

#include <espressif/esp_sta.h>
#include <espressif/esp_wifi.h>

#include <paho_mqtt_c/MQTTESP8266.h>
#include <paho_mqtt_c/MQTTClient.h>

// this must be ahead of any mbedtls header files so the local mbedtls/config.h can be properly referenced
#include "ssl_connection.h"

#define MQTT_PUB_TOPIC "esp8266/status"
#define MQTT_SUB_TOPIC "esp8266/control"
#define GPIO_LED 2
#define MQTT_PORT 8883

/* certs, key, and endpoint */
extern char *ca_cert, *client_endpoint, *client_cert, *client_key;

static int wifi_alive = 0;
static int ssl_reset;
static SSLConnection *ssl_conn;
static xQueueHandle publish_queue;

static void beat_task(void *pvParameters) {
char msg[16];
int count = 0;

while (1) {
if (!wifi_alive) {
vTaskDelay(1000 / portTICK_RATE_MS);
continue;
}

printf("Schedule to publish\r\n");

snprintf(msg, sizeof(msg), "%d", count);
if (xQueueSend(publish_queue, (void *) msg, 0) == pdFALSE) {
printf("Publish queue overflow\r\n");
}

vTaskDelay(10000 / portTICK_RATE_MS);
}
}

static void topic_received(MessageData *md) {
MQTTMessage *message = md->message;
int i;

printf("Received: ");
for (i = 0; i < md->topic->lenstring.len; ++i)
printf("%c", md->topic->lenstring.data[i]);

printf(" = ");
for (i = 0; i < (int) message->payloadlen; ++i)
printf("%c", ((char *) (message->payload))[i]);
printf("\r\n");

if (!strncmp(message->payload, "on", 2)) {
printf("Turning on LED\r\n");
gpio_write(GPIO_LED, 0);
} else if (!strncmp(message->payload, "off", 3)) {
printf("Turning off LED\r\n");
gpio_write(GPIO_LED, 1);
}
}

static const char *get_my_id(void) {
// Use MAC address for Station as unique ID
static char my_id[13];
static bool my_id_done = false;
int8_t i;
uint8_t x;
if (my_id_done)
return my_id;
if (!sdk_wifi_get_macaddr(STATION_IF, (uint8_t *) my_id))
return NULL;
for (i = 5; i >= 0; --i) {
x = my_id[i] & 0x0F;
if (x > 9)
x += 7;
my_id[i * 2 + 1] = x + '0';
x = my_id[i] >> 4;
if (x > 9)
x += 7;
my_id[i * 2] = x + '0';
}
my_id[12] = '\0';
my_id_done = true;
return my_id;
}

static int mqtt_ssl_read(Network* n, unsigned char* buffer, int len,
int timeout_ms) {
int r = ssl_read(ssl_conn, buffer, len, timeout_ms);
if (r <= 0
&& (r != MBEDTLS_ERR_SSL_WANT_READ
&& r != MBEDTLS_ERR_SSL_WANT_WRITE
&& r != MBEDTLS_ERR_SSL_TIMEOUT)) {
printf("%s: TLS read error (%d), resetting\n\r", __func__, r);
ssl_reset = 1;
};
return r;
}

static int mqtt_ssl_write(Network* n, unsigned char* buffer, int len,
int timeout_ms) {
int r = ssl_write(ssl_conn, buffer, len, timeout_ms);
if (r <= 0
&& (r != MBEDTLS_ERR_SSL_WANT_READ
&& r != MBEDTLS_ERR_SSL_WANT_WRITE)) {
printf("%s: TLS write error (%d), resetting\n\r", __func__, r);
ssl_reset = 1;
}
return r;
}

static void mqtt_task(void *pvParameters) {
int ret = 0;
struct Network network;
MQTTClient client = DefaultClient;
char mqtt_client_id[20];
uint8_t mqtt_buf[100];
uint8_t mqtt_readbuf[100];
MQTTPacket_connectData data = MQTTPacket_connectData_initializer;

memset(mqtt_client_id, 0, sizeof(mqtt_client_id));
strcpy(mqtt_client_id, "ESP-");
strcat(mqtt_client_id, get_my_id());

ssl_conn = (SSLConnection *) malloc(sizeof(SSLConnection));
while (1) {
if (!wifi_alive) {
vTaskDelay(1000 / portTICK_RATE_MS);
continue;
}

printf("%s: started\n\r", __func__);
ssl_reset = 0;
ssl_init(ssl_conn);
ssl_conn->ca_cert_str = ca_cert;
ssl_conn->client_cert_str = client_cert;
ssl_conn->client_key_str = client_key;

NewNetwork(&network);
network.mqttread = mqtt_ssl_read;
network.mqttwrite = mqtt_ssl_write;

printf("%s: connecting to MQTT server %s ... ", __func__,
client_endpoint);
ret = ssl_connect(ssl_conn, client_endpoint, MQTT_PORT);

if (ret) {
printf("error: %d\n\r", ret);
ssl_destroy(ssl_conn);
continue;
}
printf("done\n\r");
NewMQTTClient(&client, &network, 5000, mqtt_buf, 100, mqtt_readbuf,
100);

data.willFlag = 0;
data.MQTTVersion = 4;
data.cleansession = 1;
data.clientID.cstring = mqtt_client_id;
data.username.cstring = NULL;
data.password.cstring = NULL;
data.keepAliveInterval = 1000;
printf("Send MQTT connect ... ");
ret = MQTTConnect(&client, &data);
if (ret) {
printf("error: %d\n\r", ret);
ssl_destroy(ssl_conn);
continue;
}
printf("done\r\n");
MQTTSubscribe(&client, MQTT_SUB_TOPIC, QOS1, topic_received);
xQueueReset(publish_queue);

while (wifi_alive && !ssl_reset) {
char msg[64];
while (xQueueReceive(publish_queue, (void *) msg, 0) == pdTRUE) {
portTickType task_tick = xTaskGetTickCount();
uint32_t free_heap = xPortGetFreeHeapSize();
uint32_t free_stack = uxTaskGetStackHighWaterMark(NULL);
snprintf(msg, sizeof(msg), "%u: free heap %u, free stack %u",
task_tick, free_heap, free_stack * 4);
printf("Publishing: %s\r\n", msg);

MQTTMessage message;
message.payload = msg;
message.payloadlen = strlen(msg);
message.dup = 0;
message.qos = QOS1;
message.retained = 0;
ret = MQTTPublish(&client, MQTT_PUB_TOPIC, &message);
if (ret != SUCCESS) {
printf("error while publishing message: %d\n", ret);
break;
}
}

ret = MQTTYield(&client, 1000);
if (ret == DISCONNECTED)
break;
}
printf("Connection dropped, request restart\n\r");
ssl_destroy(ssl_conn);
}
}

static void wifi_task(void *pvParameters) {
uint8_t status = 0;
uint8_t retries = 30;
struct sdk_station_config config = { .ssid = WIFI_SSID, .password =
WIFI_PASS, };

printf("%s: Connecting to WiFi\n\r", __func__);
sdk_wifi_set_opmode (STATION_MODE);
sdk_wifi_station_set_config(&config);

while (1) {
wifi_alive = 0;

while ((status != STATION_GOT_IP) && (retries)) {
status = sdk_wifi_station_get_connect_status();
printf("%s: status = %d\n\r", __func__, status);
if (status == STATION_WRONG_PASSWORD) {
printf("WiFi: wrong password\n\r");
break;
} else if (status == STATION_NO_AP_FOUND) {
printf("WiFi: AP not found\n\r");
break;
} else if (status == STATION_CONNECT_FAIL) {
printf("WiFi: connection failed\r\n");
break;
}
vTaskDelay(1000 / portTICK_RATE_MS);
--retries;
}

while ((status = sdk_wifi_station_get_connect_status())
== STATION_GOT_IP) {
if (wifi_alive == 0) {
printf("WiFi: Connected\n\r");
wifi_alive = 1;
}
vTaskDelay(500 / portTICK_RATE_MS);
}

wifi_alive = 0;
printf("WiFi: disconnected\n\r");
vTaskDelay(1000 / portTICK_RATE_MS);
}
}

void user_init(void) {
uart_set_baud(0, 115200);
printf("SDK version: %s, free heap %u\n", sdk_system_get_sdk_version(),
xPortGetFreeHeapSize());

gpio_enable(GPIO_LED, GPIO_OUTPUT);
gpio_write(GPIO_LED, 1);

publish_queue = xQueueCreate(3, 16);
xTaskCreate(&wifi_task, (int8_t *) "wifi_task", 256, NULL, 2, NULL);
xTaskCreate(&beat_task, (int8_t *) "beat_task", 256, NULL, 2, NULL);
xTaskCreate(&mqtt_task, (int8_t *) "mqtt_task", 2048, NULL, 2, NULL);
}
29 changes: 29 additions & 0 deletions examples/aws_iot/ca_cert.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// trusted root CA certificate - https://www.symantec.com/content/en/us/enterprise/verisign/roots/VeriSign-Class%203-Public-Primary-Certification-Authority-G5.pem
const char *ca_cert = "-----BEGIN CERTIFICATE-----\r\n"
"MIIE0zCCA7ugAwIBAgIQGNrRniZ96LtKIVjNzGs7SjANBgkqhkiG9w0BAQUFADCB\r\n"
"yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL\r\n"
"ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp\r\n"
"U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW\r\n"
"ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0\r\n"
"aG9yaXR5IC0gRzUwHhcNMDYxMTA4MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCByjEL\r\n"
"MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW\r\n"
"ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2ln\r\n"
"biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJp\r\n"
"U2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9y\r\n"
"aXR5IC0gRzUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvJAgIKXo1\r\n"
"nmAMqudLO07cfLw8RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKzj/i5Vbex\r\n"
"t0uz/o9+B1fs70PbZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIz\r\n"
"SdhDY2pSS9KP6HBRTdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQG\r\n"
"BO+QueQA5N06tRn/Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+\r\n"
"rCpSx4/VBEnkjWNHiDxpg8v+R70rfk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/\r\n"
"NIeWiu5T6CUVAgMBAAGjgbIwga8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8E\r\n"
"BAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEwHzAH\r\n"
"BgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVy\r\n"
"aXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFH/TZafC3ey78DAJ80M5+gKv\r\n"
"MzEzMA0GCSqGSIb3DQEBBQUAA4IBAQCTJEowX2LP2BqYLz3q3JktvXf2pXkiOOzE\r\n"
"p6B4Eq1iDkVwZMXnl2YtmAl+X6/WzChl8gGqCBpH3vn5fJJaCGkgDdk+bW48DW7Y\r\n"
"5gaRQBi5+MHt39tBquCWIMnNZBU4gcmU7qKEKQsTb47bDN0lAtukixlE0kF6BWlK\r\n"
"WE9gyn6CagsCqiUXObXbf+eEZSqVir2G3l6BFoMtEMze/aiCKm0oHw0LxOXnGiYZ\r\n"
"4fQRbxC1lfznQgUy286dUV4otp6F01vvpX1FQHKOtw5rDgb7MzVIcbidJ4vEZV8N\r\n"
"hnacRHr2lVz2XTIIM6RUthg/aFzyQkqFOFSDX9HoLPKsEdao7WNq\r\n"
"-----END CERTIFICATE-----\r\n";
17 changes: 17 additions & 0 deletions examples/aws_iot/client_config.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// AWS IoT client endpoint
const char *client_endpoint = "<your-prefix>.iot.<aws-region>.amazonaws.com";

// AWS IoT device certificate (ECC)
const char *client_cert =
"-----BEGIN CERTIFICATE-----\r\n"
"------------------ <your client certificate> -------------------\r\n"
"-----END CERTIFICATE-----\r\n";

// AWS IoT device private key (ECC)
const char *client_key =
"-----BEGIN EC PARAMETERS-----\r\n"
"BggqhkjOPQMBBw==\r\n"
"-----END EC PARAMETERS-----\r\n"
"-----BEGIN EC PRIVATE KEY-----\r\n"
"------------------ <your client private key> -------------------\r\n"
"-----END EC PRIVATE KEY-----\r\n";
Loading