aws iot sample: add support to remotely control an LED

The application allows to switch on and off the configured LED.

This commit adds the following configuration parameter:
  * 'user_led ': GPIO number of the LED to be remotely controlled.
    If -1, no LED is controlled, but the application simulates its status.

https://jira.digi.com/browse/DEL-4149

Signed-off-by: Tatiana Leon <tatiana.leon@digi.com>
This commit is contained in:
Tatiana Leon 2017-05-11 18:25:42 +02:00
parent 4837f8edc5
commit f789318be7
9 changed files with 347 additions and 6 deletions

View File

@ -2,7 +2,8 @@ AWS IoT device SDK Demo Application
===================================
Demo application to connect devices to AWS IoT.
This application monitors the CPU temperature and load.
This application monitors the CPU temperature and load and allows to remotely
switch on/off a device LED.
The demo uploads the device Thing Shadow every minute or when the difference
between the current value of temperature or load and the last reported is bigger
@ -51,12 +52,35 @@ You can specify the configuration file with `-c`:
"state" : {
"reported" : {
"temperature" : 45.971,
"cpuLoad" : 4.504505
"cpuLoad" : 4.504505,
"ledON" : false
}
},
"clientToken" : "<thing_name>-3"
}
```
* To switch on the configured LED send to the topic
`$aws/things/<thing_name>/shadow/update`:
```
{
"state" : {
"desired" : {
"ledON" : true
}
}
}
```
* To switch off the configured LED send to the topic
`$aws/things/<thing_name>/shadow/update`:
```
{
"state" : {
"desired" : {
"ledON" : false
}
}
}
```
Compiling the application
-------------------------

View File

@ -65,3 +65,7 @@ temperature_variation = 1
# By default, 10%.
cpu_load_variation = 10
# User LED: GPIO number of the LED.
# If -1, no LED is controlled, but the application still simulates its status.
# User LED on board are: ccimx6sbc, 34; ccimx6ulsbc, 488; ccimx6ulstarter, 75.
user_led = -1

View File

@ -47,6 +47,7 @@
#define SETTING_CPULOAD_VARIATION "cpu_load_variation"
#define SETTING_CPULOAD_VARIATION_MIN 1 /* % */
#define SETTING_CPULOAD_VARIATION_MAX 50.0 /* % */
#define SETTING_USER_LED "user_led"
#define SETTING_UNKNOWN "__unknown"
@ -112,6 +113,8 @@ int parse_configuration(const char *const filename, aws_iot_cfg_t *aws_cfg)
CFG_FLOAT (SETTING_TEMP_VARIATION, 1, CFGF_NONE),
CFG_FLOAT (SETTING_CPULOAD_VARIATION, 10, CFGF_NONE),
CFG_INT (SETTING_USER_LED, -1, CFGF_NONE),
/* Needed for unknown settings. */
CFG_STR (SETTING_UNKNOWN, NULL, CFGF_NONE),
CFG_END()
@ -199,6 +202,7 @@ static int fill_aws_iot_config(aws_iot_cfg_t *aws_cfg)
aws_cfg->shadow_report_rate = cfg_getint(cfg, SETTING_SHADOW_REPORT_RATE);
aws_cfg->temp_variation = cfg_getfloat(cfg, SETTING_TEMP_VARIATION);
aws_cfg->cpuload_variation = cfg_getfloat(cfg, SETTING_CPULOAD_VARIATION);
aws_cfg->led_gpio = cfg_getint(cfg, SETTING_USER_LED);
return 0;
}

View File

@ -49,6 +49,7 @@
* (C) to report again
* @cpuload_variation: CPU load variation between last reported and current (%)
* to report again
* @led_gpio: GPIO number of the LED
*/
typedef struct {
char *thing_name;
@ -62,6 +63,7 @@ typedef struct {
uint32_t shadow_report_rate;
uint16_t temp_variation;
uint16_t cpuload_variation;
int led_gpio;
} aws_iot_cfg_t;
/*------------------------------------------------------------------------------

View File

@ -35,6 +35,8 @@
/*------------------------------------------------------------------------------
F U N C T I O N D E C L A R A T I O N S
------------------------------------------------------------------------------*/
static void led_actuate_callback(const char *json_string, uint32_t len,
jsonStruct_t *json_struct);
static void shadow_update_status_callback(const char *pThingName,
ShadowActions_t action,
Shadow_Ack_Status_t status,
@ -175,6 +177,8 @@ IoT_Error_t disconnect_shadow_client(AWS_IoT_Client *mqtt_client)
IoT_Error_t initialize_shadow(AWS_IoT_Client *mqtt_client,
device_shadow_t *dev_shadow)
{
IoT_Error_t rc = FAILURE;
device_shadow = dev_shadow;
device_shadow->temp_handler.cb = NULL;
@ -193,9 +197,22 @@ IoT_Error_t initialize_shadow(AWS_IoT_Client *mqtt_client,
device_shadow->cpu_load = 0;
device_shadow->cpu_load_update = 0;
device_shadow->led_actuator.cb = led_actuate_callback;
device_shadow->led_actuator.pKey = ATTR_LED;
device_shadow->led_actuator.pData = &(device_shadow->led_on);
device_shadow->led_actuator.type = SHADOW_JSON_BOOL;
device_shadow->led_on = false;
device_shadow->led_update = 0;
device_shadow->aws_config = &aws_cfg;
return SUCCESS;
rc = aws_iot_shadow_register_delta(mqtt_client,
&(device_shadow->led_actuator));
if (rc != SUCCESS)
IOT_ERROR("Unable to register Shadow Delta for LED, error: %d", rc);
return rc;
}
/**
@ -216,9 +233,10 @@ IoT_Error_t update_shadow(AWS_IoT_Client *mqtt_client)
return rc;
rc = aws_iot_shadow_add_reported(json_doc_buf, size_json_doc_buf,
2,
3,
&(device_shadow->temp_handler),
&(device_shadow->cpu_load_handler));
&(device_shadow->cpu_load_handler),
&(device_shadow->led_actuator));
if (rc != SUCCESS)
return rc;
@ -232,6 +250,35 @@ IoT_Error_t update_shadow(AWS_IoT_Client *mqtt_client)
(void *) device_shadow, 4, true);
}
/**
* led_actuate_callback() - Callback to be executed on receiving the LED value
*
* @json_string: Thing name of the shadow to be updated.
* @len: The response of the action.
* @json_struct: The struct used to parse JSON value.
*/
static void led_actuate_callback(const char *json_string, uint32_t len,
jsonStruct_t *json_struct)
{
bool status = false;
int led_gpio = aws_cfg.led_gpio;
IOT_UNUSED(json_string);
IOT_UNUSED(len);
if (json_struct == NULL)
return;
status = *(bool *) (json_struct->pData);
if (led_gpio <= -1 ||
(led_gpio > -1 && set_gpio_value(led_gpio, status) == 0)) {
IOT_INFO("Delta - LED state changed to %s", status ? ON : OFF);
device_shadow->led_update = 1;
} else {
IOT_ERROR("Error setting LED to %s", status ? ON : OFF);
}
}
/**
* shadow_update_status_callback() - AWS IoT Device SDK update status callback
*
@ -270,6 +317,7 @@ static void shadow_update_status_callback(const char *pThingName,
IOT_INFO("Shadow update accepted");
dev_shadow->temp_update = 0;
dev_shadow->cpu_load_update = 0;
dev_shadow->led_update = 0;
break;
}
}

View File

@ -30,6 +30,10 @@
------------------------------------------------------------------------------*/
#define ATTR_TEMPERATURE "temperature"
#define ATTR_CPU_LOAD "cpuLoad"
#define ATTR_LED "ledON"
#define ON "ON"
#define OFF "OFF"
/*------------------------------------------------------------------------------
D A T A T Y P E S D E F I N I T I O N S
@ -44,6 +48,9 @@
* @cpu_load: Last CPU load reported (%)
* @cpu_load_handler: CPU load handler
* @cpu_load_update: CPU load value locally updated
* @led_on: Last LED value reported
* @led_actuator: LED actuator
* @led_update: LED value locally updated
* @update_required: Update shadow immediately
* @aws_config: AWS IoT Decive SDK configuration struct
*/
@ -54,6 +61,9 @@ typedef struct {
double cpu_load;
jsonStruct_t cpu_load_handler;
unsigned int cpu_load_update;
bool led_on;
jsonStruct_t led_actuator;
unsigned int led_update;
unsigned int update_required;
aws_iot_cfg_t *aws_config;
} device_shadow_t;

View File

@ -18,6 +18,8 @@
*/
#include <aws_iot_log.h>
#include <dirent.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
@ -31,10 +33,18 @@
#define FILE_CPU_TEMP "/sys/class/thermal/thermal_zone0/temp"
#define FILE_CPU_LOAD "/proc/stat"
#define PATH_GPIO_FILES "/sys/class/gpio/"
#define EXPORT_FILE PATH_GPIO_FILES "export"
#define GPIO_DIR_FILE_FORMAT PATH_GPIO_FILES "gpio%d/direction"
#define GPIO_VALUE_FILE_FORMAT PATH_GPIO_FILES "gpio%d/value"
/*------------------------------------------------------------------------------
F U N C T I O N D E C L A R A T I O N S
------------------------------------------------------------------------------*/
static int export_gpio(int gpio);
static int is_gpio_exported( int gpio);
static long read_file(const char * path, char *buffer, long bytes_to_read);
static int write_file(const char *const path, const char *const format, ...);
/*------------------------------------------------------------------------------
F U N C T I O N D E F I N I T I O N S
@ -114,6 +124,158 @@ double get_cpu_temp(void)
return temperature / 1000;
}
/**
* init_gpio() - Export and initialize a GPIO
*
* @gpio: GPIO kernel number
*
* The provided GPIO is exported and configured as input with edge "none".
*
* Return: 0 if the GPIO was initialized successfully, -1 otherwise.
*/
int init_gpio(int gpio)
{
int error = 0;
if (gpio < 0) {
IOT_ERROR("Bad GPIO number '%d'", gpio);
return -1;
}
/* Export the GPIO. */
if (export_gpio(gpio) != 0) {
IOT_ERROR("Cannot initialize GPIO %d", gpio);
return -1;
}
/* Configure GPIO as input. */
error = set_gpio_direction(gpio, INPUT);
if (error != 0)
IOT_ERROR("Cannot initialize GPIO %d", gpio);
return error;
}
/**
* set_gpio_value() - Set GPIO value, if it is configured as an output
*
* @gpio: GPIO kernel number.
* @value: 1 to set to high, 0 to low.
*
* Return: 0 if the GPIO was successfully set, -1 otherwise.
*/
int set_gpio_value(int gpio, unsigned int value)
{
char gpio_path[MAX_LENGTH] = {0};
if (gpio < 0) {
IOT_ERROR("Bad GPIO number '%d'", gpio);
return -1;
}
snprintf(gpio_path, MAX_LENGTH, GPIO_VALUE_FILE_FORMAT, gpio);
if (write_file(gpio_path, "%d", value) != 0) {
IOT_ERROR("Error setting GPIO %d value to %s (%d)", gpio,
value ? "high" : "low", value);
return -1;
}
return 0;
}
/**
* set_gpio_direction() - Set GPIO direction, input or output
*
* @gpio: GPIO kernel number.
* @out: 1 to configure as out, 0 as input.
*
* Return: 0 if the GPIO was successfully configured, -1 otherwise.
*/
int set_gpio_direction(int gpio, unsigned int out)
{
char gpio_path[MAX_LENGTH] = {0};
char buf[MAX_LENGTH] = {0};
IOT_DEBUG("GPIO %d as %s", gpio, out ? "Output" : "Input");
if (gpio < 0) {
IOT_ERROR("Bad GPIO number '%d'", gpio);
return -1;
}
snprintf(gpio_path, MAX_LENGTH, GPIO_DIR_FILE_FORMAT, gpio);
if (out)
strcpy(buf, "out");
else
strcpy(buf, "in");
if (write_file(gpio_path, "%s", buf) != 0) {
IOT_ERROR("Error setting GPIO %d direction to '%s'", gpio,
out ? "Output" : "Input");
return -1;
}
return 0;
}
/**
* export_gpio() - Export a GPIO
*
* @gpio: GPIO kernel number.
*
* Return: 0 if the GPIO was successfully exported, -1 otherwise.
*/
static int export_gpio(int gpio)
{
if (gpio < 0) {
IOT_ERROR("Bad GPIO number '%d'", gpio);
return -1;
}
if (is_gpio_exported(gpio))
return 0;
if (write_file(EXPORT_FILE, "%d", gpio) != 0) {
IOT_ERROR("GPIO %d not exported, error writing to 'export' file",
gpio);
return -1;
}
/* Check if the GPIO was really exported. */
if (!is_gpio_exported(gpio)) {
IOT_ERROR("GPIO %d not exported", gpio);
return -1;
}
return 0;
}
/**
* is_gpio_exported() - Check if a GPIO is exported
*
* @gpio: GPIO kernel number.
*
* Return: 1 if the GPIO is exported, 0 otherwise.
*/
static int is_gpio_exported( int gpio)
{
char dir_path[MAX_LENGTH] = {0};
DIR *dir = NULL;
snprintf(dir_path, MAX_LENGTH, PATH_GPIO_FILES "gpio%d", gpio);
dir = opendir(dir_path);
if (dir) {
closedir(dir);
return 1;
} else {
return 0;
}
}
/**
* read_file() - Read the given file and returns its contents
*
@ -150,3 +312,46 @@ static long read_file(const char * path, char *buffer, long bytes_to_read)
return read_size;
}
/**
* write_file() - Write data to a file
*
* @path: Absolute path of the file to be written.
* @format: String that contains the text to be written to the file.
*
* Return: 0 if the file was written successfully, -1 otherwise.
*/
static int write_file(const char *const path, const char *const format, ...)
{
va_list args;
FILE *f = NULL;
int len, error = 0;
if (access(path, W_OK) != 0) {
IOT_DEBUG("File cannot be written: %s", path);
return -1;
}
va_start(args, format);
f = fopen(path, "w");
if (f == NULL) {
IOT_DEBUG("fopen error: %s", path);
error = -1;
goto done;
}
len = vfprintf(f, format, args);
if (len < 0) {
IOT_DEBUG("vfprintf error: %s", path);
error = -1;
}
fsync(fileno(f));
fclose(f);
done:
va_end(args);
return error;
}

View File

@ -20,10 +20,19 @@
#ifndef DEVICE_CONTROL_H_
#define DEVICE_CONTROL_H_
/*------------------------------------------------------------------------------
D E F I N I T I O N S
------------------------------------------------------------------------------*/
#define INPUT 0
#define OUTPUT 1
/*------------------------------------------------------------------------------
F U N C T I O N D E C L A R A T I O N S
------------------------------------------------------------------------------*/
double get_cpu_load(void);
double get_cpu_temp(void);
int init_gpio(int gpio);
int set_gpio_value(int gpio, unsigned int value);
int set_gpio_direction(int gpio, unsigned int out);
#endif /* DEVICE_CONTROL_H_ */

View File

@ -58,6 +58,7 @@
F U N C T I O N D E C L A R A T I O N S
------------------------------------------------------------------------------*/
static int start_aws_iot(const char *config_file);
static int initialize_system(device_shadow_t *dev_shadow);
static int check_stop(void);
static void add_sigkill_signal(void);
static void graceful_shutdown(void);
@ -160,6 +161,9 @@ static int start_aws_iot(const char *config_file)
goto done;
}
if (initialize_system(&device_shadow) != 0)
IOT_WARN("Unable to initialize the whole system");
time_start = time(NULL);
/* Loop and publish shadow changes */
while (rc == NETWORK_ATTEMPTING_RECONNECT ||
@ -189,7 +193,8 @@ static int start_aws_iot(const char *config_file)
load >= (device_shadow.cpu_load + aws_cfg->cpuload_variation));
change_shadow = device_shadow.temp_update ||
device_shadow.cpu_load_update;
device_shadow.cpu_load_update ||
device_shadow.led_update;
if (change_shadow ||
(time(NULL) - time_start) >= aws_cfg->shadow_report_rate) {
@ -206,8 +211,15 @@ static int start_aws_iot(const char *config_file)
IOT_INFO(
"CPU Load variation greater than %d%%\n",
aws_cfg->cpuload_variation);
if (device_shadow.led_update)
IOT_INFO(
"LED changed to %s\n",
device_shadow.led_on ? ON : OFF);
IOT_INFO("Temperature: %fC", t);
IOT_INFO("CPU Load: %f%%", load);
IOT_INFO(
"LED status: %s",
device_shadow.led_on ? ON : OFF);
IOT_INFO("=========================================\n");
rc = update_shadow(&mqtt_client);
@ -228,6 +240,29 @@ done:
return rc;
}
/*
* initialize_system() - Stop application
*
* @dev_shadow: Device shadow.
*
* Return: 0 if it is successfully initialized, -1 otherwise.
*/
static int initialize_system(device_shadow_t *dev_shadow)
{
int led_gpio = dev_shadow->aws_config->led_gpio;
if (led_gpio > -1 &&
(init_gpio(led_gpio) != 0 ||
set_gpio_direction(led_gpio, OUTPUT) != 0 ||
set_gpio_value(led_gpio, dev_shadow->led_on) != 0)) {
IOT_ERROR("Unable to initialize User LED");
dev_shadow->aws_config->led_gpio = -1;
return -1;
}
return 0;
}
/*
* check_stop() - Stop application
*