dey-examples/awsiot-sample/src/device_control.c

355 lines
8.4 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 <dirent.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include "device_control.h"
/*------------------------------------------------------------------------------
D E F I N I T I O N S
------------------------------------------------------------------------------*/
#define MAX_LENGTH 256
#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
------------------------------------------------------------------------------*/
/*
* 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
*
* Return: The CPU temperature in C, -1 if error.
*/
double get_cpu_temp(void)
{
char file_data[MAX_LENGTH] = {0};
double temperature ;
int result;
if (read_file(FILE_CPU_TEMP, file_data, MAX_LENGTH) <= 0) {
IOT_ERROR("Error reading CPU temperature file");
return -1;
}
result = sscanf(file_data, "%lf", &temperature);
if (result < 1) {
IOT_ERROR("Error getting CPU temperature: not enough fields");
return -1;
}
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
*
* @path: Absolute path of the file to read.
* @buffer: Buffer to store the contents of the file.
* @bytes_to_read: The number of bytes to read (including the terminating
* null byte).
*
* Return: The number of read bytes (including the terminating null byte), -1 on
* error.
*/
static long read_file(const char * path, char *buffer, long bytes_to_read)
{
FILE *fd = NULL;
long read_size = 0;
if (access(path, R_OK) != 0) {
IOT_DEBUG("File cannot be read: %s", path);
return -1;
}
if ((fd = fopen(path, "rb")) == NULL) {
IOT_DEBUG("fopen error: %s", path);
return -1;
}
if (fgets(buffer, bytes_to_read, fd) == NULL) {
IOT_DEBUG("fgets error: %s", path);
read_size = -1;
} else {
read_size = strlen(buffer) + 1;
}
fclose(fd);
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;
}