329 lines
8.7 KiB
C
329 lines
8.7 KiB
C
/*
|
|
* Copyright (c) 2017 Digi International Inc.
|
|
*
|
|
* Permission to use, copy, modify, and/or distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
* PERFORMANCE OF THIS SOFTWARE.
|
|
* =======================================================================
|
|
*/
|
|
|
|
#include <aws_iot_log.h>
|
|
#include <aws_iot_version.h>
|
|
#include <aws_iot_shadow_interface.h>
|
|
#include <getopt.h>
|
|
#include <libgen.h>
|
|
#include <signal.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
|
|
#include "aws_config.h"
|
|
#include "aws_control.h"
|
|
#include "daemonize.h"
|
|
#include "device_control.h"
|
|
|
|
/*------------------------------------------------------------------------------
|
|
D E F I N I T I O N S
|
|
------------------------------------------------------------------------------*/
|
|
#define STRINGIFY(x) #x
|
|
#define TOSTRING(x) STRINGIFY(x)
|
|
|
|
#define VERSION "0.1" GIT_REVISION
|
|
#define AWS_IOT_VERSION TOSTRING(VERSION_MAJOR) "." TOSTRING(VERSION_MINOR) "." TOSTRING(VERSION_PATCH) "-" VERSION_TAG
|
|
|
|
#define USAGE \
|
|
"AWS IoT Device SDK demo.\n" \
|
|
"Copyright(c) Digi International Inc.\n" \
|
|
"\n" \
|
|
"Version: %s\n" \
|
|
"AWS IoT SDK Version: " AWS_IOT_VERSION "\n" \
|
|
"\n" \
|
|
"Usage: %s [options]\n\n" \
|
|
" -d --daemon Daemonize the process\n" \
|
|
" -c --config-file=<PATH> Use a custom configuration file instead of\n" \
|
|
" the default one located in " AWS_IOT_CONFIG_FILE "\n" \
|
|
" -h --help Print help and exit\n" \
|
|
"\n"
|
|
|
|
/*------------------------------------------------------------------------------
|
|
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);
|
|
static void sigint_handler(int signum);
|
|
static void usage(char const *const name);
|
|
|
|
/*------------------------------------------------------------------------------
|
|
G L O B A L V A R I A B L E S
|
|
------------------------------------------------------------------------------*/
|
|
static volatile int stop = 0;
|
|
static AWS_IoT_Client mqtt_client;
|
|
|
|
/*------------------------------------------------------------------------------
|
|
F U N C T I O N D E F I N I T I O N S
|
|
------------------------------------------------------------------------------*/
|
|
int main(int argc, char **argv)
|
|
{
|
|
int result = EXIT_SUCCESS;
|
|
char *name = basename(argv[0]);
|
|
static int opt, opt_index;
|
|
int create_daemon = 0;
|
|
char *config_file = NULL;
|
|
static const char *short_options = "dc:h";
|
|
static const struct option long_options[] = {
|
|
{"daemon", no_argument, NULL, 'd'},
|
|
{"config-file", required_argument, NULL, 'c'},
|
|
{"help", no_argument, NULL, 'h'},
|
|
{NULL, 0, NULL, 0}
|
|
};
|
|
|
|
while (1) {
|
|
opt = getopt_long(argc, argv, short_options, long_options,
|
|
&opt_index);
|
|
if (opt == -1)
|
|
break;
|
|
|
|
switch (opt) {
|
|
case 'd':
|
|
create_daemon = 1;
|
|
break;
|
|
case 'c':
|
|
config_file = optarg;
|
|
break;
|
|
case 'h':
|
|
usage(name);
|
|
goto done;
|
|
default:
|
|
usage(name);
|
|
result = EXIT_FAILURE;
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
/* Daemonize if requested. */
|
|
if (create_daemon) {
|
|
if (start_daemon(name) != 0) {
|
|
result = EXIT_FAILURE;
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
/* Do the real work. */
|
|
start_aws_iot(config_file);
|
|
|
|
done:
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* start_aws_iot() - Start AWS IoT Device SDK
|
|
*
|
|
* @config_file: Absolute path of the configuration file to use.
|
|
* NULL to use the default one (/etc/awsiotsdk.conf).
|
|
*
|
|
* Return: 0 on success, 1 otherwise.
|
|
*/
|
|
static int start_aws_iot(const char *config_file)
|
|
{
|
|
IoT_Error_t rc = FAILURE;
|
|
device_shadow_t device_shadow;
|
|
time_t time_start;
|
|
|
|
add_sigkill_signal();
|
|
|
|
rc = initialize_shadow_client(&mqtt_client, config_file);
|
|
if (rc != SUCCESS) {
|
|
IOT_ERROR("Cannot initialize Shadow client, error: %d", rc);
|
|
return rc;
|
|
}
|
|
|
|
rc = connect_shadow_client(&mqtt_client);
|
|
if (rc != SUCCESS) {
|
|
IOT_ERROR("Unable to connect, error: %d", rc);
|
|
return rc;
|
|
}
|
|
|
|
rc = initialize_shadow(&mqtt_client, &device_shadow);
|
|
if (rc != SUCCESS) {
|
|
IOT_ERROR("Unable to initialize device Shadow, error: %d", rc);
|
|
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 ||
|
|
rc == NETWORK_RECONNECTED ||
|
|
rc == SUCCESS ||
|
|
!check_stop()) {
|
|
aws_iot_cfg_t *aws_cfg = device_shadow.aws_config;
|
|
double t;
|
|
double load;
|
|
int change_shadow = 0;
|
|
|
|
rc = aws_iot_mqtt_yield(&mqtt_client, 200);
|
|
|
|
if (rc == NETWORK_ATTEMPTING_RECONNECT) {
|
|
sleep(1);
|
|
/* If the client is attempting to reconnect,
|
|
* skip the rest of the loop */
|
|
continue;
|
|
}
|
|
|
|
t = get_cpu_temp();
|
|
device_shadow.temp_update = (t <= (device_shadow.temp - aws_cfg->temp_variation) ||
|
|
t >= (device_shadow.temp + aws_cfg->temp_variation));
|
|
|
|
load = get_cpu_load();
|
|
device_shadow.cpu_load_update = (load <= (device_shadow.cpu_load - aws_cfg->cpuload_variation) ||
|
|
load >= (device_shadow.cpu_load + aws_cfg->cpuload_variation));
|
|
|
|
change_shadow = device_shadow.temp_update ||
|
|
device_shadow.cpu_load_update ||
|
|
device_shadow.led_update;
|
|
|
|
if (change_shadow ||
|
|
(time(NULL) - time_start) >= aws_cfg->shadow_report_rate) {
|
|
device_shadow.temp = t;
|
|
device_shadow.cpu_load = load;
|
|
|
|
IOT_INFO("\n=========================================");
|
|
IOT_INFO("Updating shadow...");
|
|
if (device_shadow.temp_update)
|
|
IOT_INFO(
|
|
"Temperature variation greater than %dC\n",
|
|
aws_cfg->temp_variation);
|
|
if (device_shadow.cpu_load_update)
|
|
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);
|
|
|
|
if ((time(NULL) - time_start) >= aws_cfg->shadow_report_rate)
|
|
time_start = time(NULL);
|
|
}
|
|
|
|
sleep(1);
|
|
}
|
|
|
|
done:
|
|
if (rc != SUCCESS) {
|
|
IOT_ERROR("Error occurred in the loop: %d", rc);
|
|
graceful_shutdown();
|
|
}
|
|
|
|
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
|
|
*
|
|
* Return: 1 if it is successfully stopped, 0 otherwise.
|
|
*/
|
|
static int check_stop(void)
|
|
{
|
|
if (stop) {
|
|
IoT_Error_t rc = disconnect_shadow_client(&mqtt_client);
|
|
if (rc != SUCCESS) {
|
|
IOT_ERROR("Disconnect error: %d", rc);
|
|
stop = 0;
|
|
}
|
|
}
|
|
|
|
return stop;
|
|
}
|
|
|
|
/*
|
|
* add_sigkill_signal() - Add the kill signal to the process
|
|
*/
|
|
static void add_sigkill_signal(void)
|
|
{
|
|
struct sigaction new_action;
|
|
struct sigaction old_action;
|
|
|
|
/* Setup signal hander. */
|
|
atexit(graceful_shutdown);
|
|
new_action.sa_handler = sigint_handler;
|
|
sigemptyset(&new_action.sa_mask);
|
|
new_action.sa_flags = 0;
|
|
sigaction(SIGINT, NULL, &old_action);
|
|
if (old_action.sa_handler != SIG_IGN)
|
|
sigaction(SIGINT, &new_action, NULL);
|
|
}
|
|
|
|
/*
|
|
* graceful_shutdown() - Stop MQTT connection and all threads
|
|
*/
|
|
void graceful_shutdown(void)
|
|
{
|
|
stop = 1;
|
|
check_stop();
|
|
}
|
|
|
|
/**
|
|
* sigint_handler() - Manage signal received.
|
|
*
|
|
* @signum: Received signal.
|
|
*/
|
|
static void sigint_handler(int signum)
|
|
{
|
|
IOT_DEBUG("Received signal %d to close Cloud connection.", signum);
|
|
exit(0);
|
|
}
|
|
|
|
/**
|
|
* usage() - Print usage information
|
|
*
|
|
* @name: Application name.
|
|
*/
|
|
static void usage(char const *const name)
|
|
{
|
|
printf(USAGE, VERSION, name);
|
|
}
|