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);