From 4837f8edc5dd8404458a68913848e6414f73c02c Mon Sep 17 00:00:00 2001 From: Tatiana Leon Date: Thu, 11 May 2017 15:24:47 +0200 Subject: [PATCH] aws iot sample: add support to report CPU load The application also monitors the CPU load along with the temperature. It also uploads the Thing Shadow when the load variation is bigger than 10% (per param in the config file). This commit adds the following configuration parameter: * 'cpu_load_variation': Minimum absolute value of the difference between last reported CPU load and the current value to report again. https://jira.digi.com/browse/DEL-4149 Signed-off-by: Tatiana Leon --- awsiot-sample/README.md | 9 +++-- awsiot-sample/cfg_files/awsiotsdk.conf | 6 +++ awsiot-sample/src/aws_config.c | 23 ++++++++++++ awsiot-sample/src/aws_config.h | 3 ++ awsiot-sample/src/aws_control.c | 14 ++++++- awsiot-sample/src/aws_control.h | 7 ++++ awsiot-sample/src/device_control.c | 52 ++++++++++++++++++++++++++ awsiot-sample/src/device_control.h | 1 + awsiot-sample/src/main.c | 18 ++++++++- 9 files changed, 125 insertions(+), 8 deletions(-) diff --git a/awsiot-sample/README.md b/awsiot-sample/README.md index 735ef81..3e1c0df 100644 --- a/awsiot-sample/README.md +++ b/awsiot-sample/README.md @@ -2,11 +2,11 @@ AWS IoT device SDK Demo Application =================================== Demo application to connect devices to AWS IoT. -This application monitors the CPU temperature. +This application monitors the CPU temperature and load. The demo uploads the device Thing Shadow every minute or when the difference -between the temperature current value and the last reported is bigger than a -configured value. +between the current value of temperature or load and the last reported is bigger +than a configured value (1C for temperature and 10% for CPU load). The repository has the following directories: @@ -50,7 +50,8 @@ You can specify the configuration file with `-c`: { "state" : { "reported" : { - "temperature" : 45.971 + "temperature" : 45.971, + "cpuLoad" : 4.504505 } }, "clientToken" : "-3" diff --git a/awsiot-sample/cfg_files/awsiotsdk.conf b/awsiot-sample/cfg_files/awsiotsdk.conf index bb64fed..0a5262e 100644 --- a/awsiot-sample/cfg_files/awsiotsdk.conf +++ b/awsiot-sample/cfg_files/awsiotsdk.conf @@ -59,3 +59,9 @@ shadow_report_rate = 60 # By default, 1C. temperature_variation = 1 +# CPU load variation: Minimum absolute value of the difference between last +# reported CPU load and the current value to report to the cloud again (in %). +# Its value must be between 0.1% and 50.0%. +# By default, 10%. +cpu_load_variation = 10 + diff --git a/awsiot-sample/src/aws_config.c b/awsiot-sample/src/aws_config.c index f49e3dd..2753f5d 100644 --- a/awsiot-sample/src/aws_config.c +++ b/awsiot-sample/src/aws_config.c @@ -44,6 +44,9 @@ #define SETTING_TEMP_VARIATION "temperature_variation" #define SETTING_TEMP_VARIATION_MIN 0.1 /* C */ #define SETTING_TEMP_VARIATION_MAX 10.0 /* C */ +#define SETTING_CPULOAD_VARIATION "cpu_load_variation" +#define SETTING_CPULOAD_VARIATION_MIN 1 /* % */ +#define SETTING_CPULOAD_VARIATION_MAX 50.0 /* % */ #define SETTING_UNKNOWN "__unknown" @@ -58,6 +61,7 @@ static int cfg_check_certificates_path(cfg_t *cfg, cfg_opt_t *opt); static int cfg_check_cert_file(cfg_t *cfg, cfg_opt_t *opt); static int cfg_check_shadow_report_rate(cfg_t *cfg, cfg_opt_t *opt); static int cfg_check_temp_variation(cfg_t *cfg, cfg_opt_t *opt); +static int cfg_check_cpuload_variation(cfg_t *cfg, cfg_opt_t *opt); static int cfg_check_int_range(cfg_t *cfg, cfg_opt_t *opt, uint32_t min, uint32_t max); static int cfg_check_float_range(cfg_t *cfg, cfg_opt_t *opt, float min, float max); static int cfg_check_empty_string(cfg_t *cfg, cfg_opt_t *opt); @@ -106,6 +110,7 @@ int parse_configuration(const char *const filename, aws_iot_cfg_t *aws_cfg) CFG_INT (SETTING_SHADOW_REPORT_RATE, 60, CFGF_NONE), CFG_FLOAT (SETTING_TEMP_VARIATION, 1, CFGF_NONE), + CFG_FLOAT (SETTING_CPULOAD_VARIATION, 10, CFGF_NONE), /* Needed for unknown settings. */ CFG_STR (SETTING_UNKNOWN, NULL, CFGF_NONE), @@ -133,6 +138,7 @@ int parse_configuration(const char *const filename, aws_iot_cfg_t *aws_cfg) cfg_set_validate_func(cfg, SETTING_PRIVKEY_NAME, cfg_check_cert_file); cfg_set_validate_func(cfg, SETTING_SHADOW_REPORT_RATE, cfg_check_shadow_report_rate); cfg_set_validate_func(cfg, SETTING_TEMP_VARIATION, cfg_check_temp_variation); + cfg_set_validate_func(cfg, SETTING_CPULOAD_VARIATION, cfg_check_cpuload_variation); /* Parse the configuration file. */ switch (cfg_parse(cfg, filename)) { @@ -192,6 +198,7 @@ static int fill_aws_iot_config(aws_iot_cfg_t *aws_cfg) return -1; 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); return 0; } @@ -336,6 +343,22 @@ static int cfg_check_temp_variation(cfg_t *cfg, cfg_opt_t *opt) SETTING_TEMP_VARIATION_MAX); } +/* + * cfg_check_cpuload_variation() - Validate CPU load variation value is between + * 0.1 and 50.0 + * + * @cfg: The section where the option is defined. + * @opt: The option to check. + * + * @Return: 0 on success, any other value otherwise. + */ +static int cfg_check_cpuload_variation(cfg_t *cfg, cfg_opt_t *opt) +{ + return cfg_check_float_range(cfg, opt, + SETTING_CPULOAD_VARIATION_MIN, + SETTING_CPULOAD_VARIATION_MAX); +} + /* * cfg_check_int_range() - Validate a parameter int value is between the given range * diff --git a/awsiot-sample/src/aws_config.h b/awsiot-sample/src/aws_config.h index f290739..2acc083 100644 --- a/awsiot-sample/src/aws_config.h +++ b/awsiot-sample/src/aws_config.h @@ -47,6 +47,8 @@ * @shadow_report_rate: Frequency at which report system information (seconds) * @temp_variation: Temperature variation between last reported and current * (C) to report again + * @cpuload_variation: CPU load variation between last reported and current (%) + * to report again */ typedef struct { char *thing_name; @@ -59,6 +61,7 @@ typedef struct { char *priv_key_fname; uint32_t shadow_report_rate; uint16_t temp_variation; + uint16_t cpuload_variation; } aws_iot_cfg_t; /*------------------------------------------------------------------------------ diff --git a/awsiot-sample/src/aws_control.c b/awsiot-sample/src/aws_control.c index b053d71..fd827c3 100644 --- a/awsiot-sample/src/aws_control.c +++ b/awsiot-sample/src/aws_control.c @@ -185,6 +185,14 @@ IoT_Error_t initialize_shadow(AWS_IoT_Client *mqtt_client, device_shadow->temp = 0; device_shadow->temp_update = 0; + device_shadow->cpu_load_handler.cb = NULL; + device_shadow->cpu_load_handler.pKey = ATTR_CPU_LOAD; + device_shadow->cpu_load_handler.pData = &(device_shadow->cpu_load); + device_shadow->cpu_load_handler.type = SHADOW_JSON_DOUBLE; + + device_shadow->cpu_load = 0; + device_shadow->cpu_load_update = 0; + device_shadow->aws_config = &aws_cfg; return SUCCESS; @@ -208,8 +216,9 @@ 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, - 1, - &(device_shadow->temp_handler)); + 2, + &(device_shadow->temp_handler), + &(device_shadow->cpu_load_handler)); if (rc != SUCCESS) return rc; @@ -260,6 +269,7 @@ static void shadow_update_status_callback(const char *pThingName, case SHADOW_ACK_ACCEPTED: IOT_INFO("Shadow update accepted"); dev_shadow->temp_update = 0; + dev_shadow->cpu_load_update = 0; break; } } diff --git a/awsiot-sample/src/aws_control.h b/awsiot-sample/src/aws_control.h index bd956ab..d3b5f2d 100644 --- a/awsiot-sample/src/aws_control.h +++ b/awsiot-sample/src/aws_control.h @@ -29,6 +29,7 @@ D E F I N I T I O N S ------------------------------------------------------------------------------*/ #define ATTR_TEMPERATURE "temperature" +#define ATTR_CPU_LOAD "cpuLoad" /*------------------------------------------------------------------------------ D A T A T Y P E S D E F I N I T I O N S @@ -40,6 +41,9 @@ * @temp: Last temperature reported (C) * @temp_handler: Temperature handler * @temp_update: Temperature value locally updated + * @cpu_load: Last CPU load reported (%) + * @cpu_load_handler: CPU load handler + * @cpu_load_update: CPU load value locally updated * @update_required: Update shadow immediately * @aws_config: AWS IoT Decive SDK configuration struct */ @@ -47,6 +51,9 @@ typedef struct { double temp; jsonStruct_t temp_handler; unsigned int temp_update; + double cpu_load; + jsonStruct_t cpu_load_handler; + unsigned int cpu_load_update; unsigned int update_required; aws_iot_cfg_t *aws_config; } device_shadow_t; diff --git a/awsiot-sample/src/device_control.c b/awsiot-sample/src/device_control.c index 110b806..61e7e1b 100644 --- a/awsiot-sample/src/device_control.c +++ b/awsiot-sample/src/device_control.c @@ -29,6 +29,7 @@ #define MAX_LENGTH 256 #define FILE_CPU_TEMP "/sys/class/thermal/thermal_zone0/temp" +#define FILE_CPU_LOAD "/proc/stat" /*------------------------------------------------------------------------------ F U N C T I O N D E C L A R A T I O N S @@ -38,6 +39,57 @@ static long read_file(const char * path, char *buffer, long bytes_to_read); /*------------------------------------------------------------------------------ F U N C T I O N D E F I N I T I O N S ------------------------------------------------------------------------------*/ +/* + * get_cpu_load() - Get the CPU load of the system + * + * Return: The CPU load in %, -1 if the value is not available. + */ +double get_cpu_load(void) +{ + static unsigned long long last_work = 0, last_total = 0; + char data[MAX_LENGTH] = {0}; + unsigned long long int fields[10]; + unsigned long long work = 0, total = 0; + double usage = -1; + int i, result; + + if (read_file(FILE_CPU_LOAD, data, MAX_LENGTH) <= 0) { + IOT_ERROR("Error reading cpu load file"); + return usage; + } + + result = sscanf(data, "cpu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu", + &fields[0], &fields[1], &fields[2], &fields[3], &fields[4], + &fields[5], &fields[6], &fields[7], &fields[8], &fields[9]); + + if (result < 4) { + IOT_ERROR("CPU load not enough fields error"); + return usage; + } + + for (i = 0; i < 3; i++) { + work += fields[i]; + } + for (i = 0; i < result; i++) { + total += fields[i]; + } + + if (last_work == 0 && last_total == 0) { + /* The first time report 0%. */ + usage = 0; + } else { + unsigned long long diff_work = work - last_work; + unsigned long long diff_total = total - last_total; + + usage = diff_work * 100.0 / diff_total; + } + + last_total = total; + last_work = work; + + return usage; +} + /* * get_cpu_temp() - Get the CPU temperature of the system * diff --git a/awsiot-sample/src/device_control.h b/awsiot-sample/src/device_control.h index 2451e1f..5556d2b 100644 --- a/awsiot-sample/src/device_control.h +++ b/awsiot-sample/src/device_control.h @@ -23,6 +23,7 @@ /*------------------------------------------------------------------------------ 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); #endif /* DEVICE_CONTROL_H_ */ diff --git a/awsiot-sample/src/main.c b/awsiot-sample/src/main.c index 6039ddd..ca2a98e 100644 --- a/awsiot-sample/src/main.c +++ b/awsiot-sample/src/main.c @@ -168,6 +168,8 @@ static int start_aws_iot(const char *config_file) !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); @@ -182,9 +184,17 @@ static int start_aws_iot(const char *config_file) device_shadow.temp_update = (t <= (device_shadow.temp - aws_cfg->temp_variation) || t >= (device_shadow.temp + aws_cfg->temp_variation)); - if (device_shadow.temp_update || + 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; + + 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..."); @@ -192,8 +202,12 @@ static int start_aws_iot(const char *config_file) 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); IOT_INFO("Temperature: %fC", t); + IOT_INFO("CPU Load: %f%%", load); IOT_INFO("=========================================\n"); rc = update_shadow(&mqtt_client);