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:
parent
4837f8edc5
commit
f789318be7
|
|
@ -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
|
||||
-------------------------
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
/*------------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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_ */
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*
|
||||
|
|
|
|||
Loading…
Reference in New Issue