New file |
| | |
| | | /********************************************************************************* |
| | | * Copyright: (C) 2024 LingYun IoT System Studio |
| | | * All rights reserved. |
| | | * |
| | | * Filename: relay.h |
| | | * Description: This file is used to control relay |
| | | * |
| | | * |
| | | * Pin connection: |
| | | * Relay Module IGKBoard-IMX6ULL |
| | | * VCC <-----> 5V |
| | | * I <-----> #Pin31 |
| | | * GND <-----> GND |
| | | * |
| | | ********************************************************************************/ |
| | | |
| | | #include <stdio.h> |
| | | #include <stdlib.h> |
| | | #include <unistd.h> |
| | | #include <fcntl.h> |
| | | #include <dirent.h> |
| | | #include <string.h> |
| | | #include <time.h> |
| | | #include <errno.h> |
| | | #include <signal.h> |
| | | #include <getopt.h> |
| | | #include <libgen.h> |
| | | |
| | | #include "relay.h" |
| | | |
| | | static inline void msleep(unsigned long ms); |
| | | |
| | | static void program_usage(const char *progname) |
| | | { |
| | | printf("Usage: %s [OPTION]...\n", progname); |
| | | printf(" This is relay control program. \n"); |
| | | |
| | | printf(" -r[relay ] Specify relay device, such as 0\n"); |
| | | printf(" -s[status ] Specify relay status, 0 for open, 1 for close\n"); |
| | | printf(" -h[help ] Display this help information\n"); |
| | | |
| | | printf("\n"); |
| | | printf("%s program Version v1.0.0\n", progname); |
| | | printf("Copyright (C) 2023 LingYun IoT System Studio.\n"); |
| | | |
| | | return; |
| | | } |
| | | |
| | | int main(int argc, char *argv[]) |
| | | { |
| | | char *progname=NULL; |
| | | int rv; |
| | | int which = -1; |
| | | int status = ON; |
| | | |
| | | struct option long_options[] = { |
| | | {"relay", required_argument, NULL, 'r'}, |
| | | {"status", required_argument, NULL, 's'}, |
| | | {"help", no_argument, NULL, 'h'}, |
| | | {NULL, 0, NULL, 0} |
| | | }; |
| | | |
| | | progname = basename(argv[0]); |
| | | |
| | | /* Parser the command line parameters */ |
| | | while ((rv = getopt_long(argc, argv, "r:s:h", long_options, NULL)) != -1) |
| | | { |
| | | switch (rv) |
| | | { |
| | | case 'r': /* Set relay number, such as 0...max */ |
| | | which = atoi(optarg); |
| | | break; |
| | | |
| | | case 's': /* Set relay status, 0 for open and 1 for close */ |
| | | status = atoi(optarg); |
| | | break; |
| | | |
| | | case 'h': /* Get help information */ |
| | | program_usage(progname); |
| | | return 0; |
| | | |
| | | default: |
| | | break; |
| | | } |
| | | } |
| | | |
| | | if( (rv=init_relay()) < 0 ) |
| | | { |
| | | printf("initial relays gpio failure, rv=%d\n", rv); |
| | | return 1; |
| | | } |
| | | |
| | | turn_relay(which, status); |
| | | |
| | | return 0; |
| | | } |
| | | |
| | | |
| | | /*+----------------------------------+ |
| | | *| Relay API based on libgpiod v2.0 | |
| | | *+----------------------------------+*/ |
| | | |
| | | static relay_t relays_info[RELAY_CNT] = |
| | | { |
| | | {"relay1", 0, 22, ACTIVE_HIGH, 0, NULL}, /* #31, GPIO1_IO22 on chip0 line 22, active high */ |
| | | }; |
| | | |
| | | /* relays context */ |
| | | static relays_t relays = |
| | | { |
| | | .relays = relays_info, |
| | | .count = RELAY_CNT, |
| | | }; |
| | | |
| | | int init_relay(void) |
| | | { |
| | | relay_t *relay; |
| | | int i, rv = 0; |
| | | char chip_dev[32]; |
| | | struct gpiod_chip *chip; /* gpio chip */ |
| | | struct gpiod_line_settings *settings; /* gpio direction, bias, active_low, value */ |
| | | struct gpiod_line_config *line_cfg; /* gpio line */ |
| | | struct gpiod_request_config *req_cfg; /* gpio consumer, it can be NULL */ |
| | | |
| | | /* defined in libgpiod-2.0/lib/line-settings.c: |
| | | |
| | | struct gpiod_line_settings { |
| | | enum gpiod_line_direction direction; |
| | | enum gpiod_line_edge edge_detection; |
| | | enum gpiod_line_drive drive; |
| | | enum gpiod_line_bias bias; |
| | | bool active_low; |
| | | enum gpiod_line_clock event_clock; |
| | | long debounce_period_us; |
| | | enum gpiod_line_value output_value; |
| | | }; |
| | | */ |
| | | settings = gpiod_line_settings_new(); |
| | | if (!settings) |
| | | { |
| | | printf("unable to allocate line settings\n"); |
| | | rv = -2; |
| | | goto cleanup; |
| | | } |
| | | |
| | | /* defined in libgpiod-2.0/lib/line-config.c |
| | | |
| | | struct gpiod_line_config { |
| | | struct per_line_config line_configs[LINES_MAX]; |
| | | size_t num_configs; |
| | | enum gpiod_line_value output_values[LINES_MAX]; |
| | | size_t num_output_values; |
| | | struct settings_node *sref_list; |
| | | }; |
| | | */ |
| | | |
| | | line_cfg = gpiod_line_config_new(); |
| | | if (!line_cfg) |
| | | { |
| | | printf("unable to allocate the line config structure"); |
| | | rv = -2; |
| | | goto cleanup; |
| | | } |
| | | |
| | | |
| | | /* defined in libgpiod-2.0/lib/request-config.c: |
| | | |
| | | struct gpiod_request_config { |
| | | char consumer[GPIO_MAX_NAME_SIZE]; |
| | | size_t event_buffer_size; |
| | | }; |
| | | */ |
| | | req_cfg = gpiod_request_config_new(); |
| | | if (!req_cfg) |
| | | { |
| | | printf("unable to allocate the request config structure"); |
| | | rv = -2; |
| | | goto cleanup; |
| | | } |
| | | |
| | | for(i=0; i<relays.count; i++) |
| | | { |
| | | relay = &relays.relays[i]; |
| | | |
| | | snprintf(chip_dev, sizeof(chip_dev), "/dev/gpiochip%d", relay->chip_num); |
| | | chip = gpiod_chip_open(chip_dev); |
| | | if( !chip ) |
| | | { |
| | | printf("open gpiochip failure, maybe you need running as root\n"); |
| | | rv = -3; |
| | | goto cleanup; |
| | | } |
| | | |
| | | /* Set as output direction, active low and default level as inactive */ |
| | | gpiod_line_settings_reset(settings); |
| | | gpiod_line_settings_set_direction(settings, GPIOD_LINE_DIRECTION_OUTPUT); |
| | | gpiod_line_settings_set_active_low(settings, relay->active); |
| | | gpiod_line_settings_set_output_value(settings, GPIOD_LINE_VALUE_INACTIVE); |
| | | |
| | | /* set gpio line */ |
| | | gpiod_line_config_reset(line_cfg); |
| | | gpiod_line_config_add_line_settings(line_cfg, &relay->gpio_num, 1, settings); |
| | | |
| | | /* Can be NULL for default settings. */ |
| | | gpiod_request_config_set_consumer(req_cfg, relay->name); |
| | | |
| | | /* Request a set of lines for exclusive usage. */ |
| | | relay->request = gpiod_chip_request_lines(chip, req_cfg, line_cfg); |
| | | |
| | | gpiod_chip_close(chip); |
| | | //printf("request %5s relay[%d] for gpio output okay\n", relay->name, relay->gpio); |
| | | } |
| | | |
| | | cleanup: |
| | | |
| | | if( rv< 0 ) |
| | | term_relay(); |
| | | |
| | | if( line_cfg ) |
| | | gpiod_line_config_free(line_cfg); |
| | | |
| | | if( req_cfg ) |
| | | gpiod_request_config_free(req_cfg); |
| | | |
| | | if( settings ) |
| | | gpiod_line_settings_free(settings); |
| | | |
| | | return rv; |
| | | } |
| | | |
| | | int term_relay(void) |
| | | { |
| | | int i; |
| | | relay_t *relay; |
| | | |
| | | printf("terminate relay gpios\n"); |
| | | |
| | | for(i=0; i<relays.count; i++) |
| | | { |
| | | relay = &relays.relays[i]; |
| | | |
| | | if( relay->request ) |
| | | { |
| | | turn_relay(i, OFF); |
| | | gpiod_line_request_release(relay->request); |
| | | } |
| | | } |
| | | |
| | | return 0; |
| | | } |
| | | |
| | | int turn_relay(int which, int cmd) |
| | | { |
| | | relay_t *relay; |
| | | int rv = 0; |
| | | int value = 0; |
| | | |
| | | if( which<0 || which>=relays.count ) |
| | | { |
| | | printf("Invalid input arguments\n"); |
| | | return -1; |
| | | } |
| | | |
| | | relay = &relays.relays[which]; |
| | | |
| | | value = OFF==cmd ? GPIOD_LINE_VALUE_INACTIVE : GPIOD_LINE_VALUE_ACTIVE; |
| | | gpiod_line_request_set_value(relay->request, relay->gpio_num, value); |
| | | |
| | | relay->status = OFF==cmd ? OFF : ON; |
| | | |
| | | return 0; |
| | | } |
| | | |
| | | int toggle_relay(int which) |
| | | { |
| | | relay_t *relay; |
| | | |
| | | if( which<0 || which>=relays.count ) |
| | | { |
| | | printf("Invalid input arguments\n"); |
| | | return -1; |
| | | } |
| | | |
| | | relay = &relays.relays[which]; |
| | | |
| | | return turn_relay(which, !relay->status); |
| | | } |
| | | |
| | | /*+-------------------------------+ |
| | | *| Misc functions API | |
| | | *+-------------------------------+*/ |
| | | |
| | | static inline void msleep(unsigned long ms) |
| | | { |
| | | struct timespec cSleep; |
| | | unsigned long ulTmp; |
| | | |
| | | cSleep.tv_sec = ms / 1000; |
| | | if (cSleep.tv_sec == 0) |
| | | { |
| | | ulTmp = ms * 10000; |
| | | cSleep.tv_nsec = ulTmp * 100; |
| | | } |
| | | else |
| | | { |
| | | cSleep.tv_nsec = 0; |
| | | } |
| | | |
| | | nanosleep(&cSleep, 0); |
| | | |
| | | return ; |
| | | } |
| | | |