update mqttd program to compatble with android app
3 files deleted
6 files modified
13 files added
7 files renamed
| | |
| | | *.o |
| | | lib*.so* |
| | | lib*.a |
| | | mqttd |
| | | iotd |
| | | |
| | | # ctags files |
| | | cscope.* |
| | |
| | | { |
| | | if ( !(fp = fopen(fname, "a+")) ) |
| | | { |
| | | fprintf(stderr, "%s() failed: %s\n", __func__, strerror(errno)); |
| | | fprintf(stderr, "%s() %s failed: %s\n", __func__, fname, strerror(errno)); |
| | | return -2; |
| | | } |
| | | L.fp = fp; |
| | |
| | | # |
| | | #******************************************************************************* |
| | | |
| | | PWD=$(shell pwd ) |
| | | |
| | | CFLAGS += -Wno-format-overflow |
| | | PWD=$(shell pwd) |
| | | LIBNAME=$(shell basename ${PWD} ) |
| | | TOPDIR=$(shell dirname ${PWD} ) |
| | | |
| | | BUILD_ARCH=$(shell uname -m) |
| | | ifneq ($(findstring $(BUILD_ARCH), "x86_64" "i386"),) |
| | | CROSS_COMPILE?=arm-linux-gnueabihf- |
| | | endif |
| | | |
| | | LIBNAME=$(shell basename ${PWD} ) |
| | | TOPDIR=$(shell dirname ${PWD} ) |
| | | OPENLIBS_INCPATH=${TOPDIR}/openlibs/install/include |
| | | OPENLIBS_LIBPATH=${TOPDIR}/openlibs/install/lib |
| | | |
| | | all: clean |
| | | CFLAGS+=-I${OPENLIBS_INCPATH} -I${TOPDIR}/booster |
| | | |
| | | all: prelibs clean |
| | | @rm -f *.o |
| | | @${CROSS_COMPILE}gcc ${CFLAGS} -I${TOPDIR} -c *.c |
| | | ${CROSS_COMPILE}gcc ${CFLAGS} -c *.c |
| | | ${CROSS_COMPILE}ar -rcs lib${LIBNAME}.a *.o |
| | | |
| | | prelibs: |
| | | if [ -n "${CROSS_COMPILE}" ] ; then cd ${TOPDIR}/openlibs/libgpiod && ./build.sh ; fi; |
| | | |
| | | clean: |
| | | @rm -f *.o |
| | | @rm -f *.a |
| | |
| | | * All rights reserved. |
| | | * |
| | | * Filename: proc.c |
| | | * Description: This file is the process API |
| | | * Description: This file is the process/thread API |
| | | * |
| | | * Version: 1.0.0(7/06/2020) |
| | | * Author: Guo Wenxue <guowenxue@gmail.com> |
| | |
| | | break; |
| | | } |
| | | } |
| | | |
| | | |
| | | /* install default signal process functions */ |
| | | void install_default_signal(void) |
| | |
| | | |
| | | return 0; |
| | | } |
| | | |
| | | |
| | | |
| | | /* **************************************************************************** |
| | | * FunctionName: record_daemon_pid |
| | |
| | | return 0; |
| | | } |
| | | |
| | | |
| | | |
| | | /* **************************************************************************** |
| | | * FunctionName: set_daemon_running |
| | | * Description : Set the programe running as daemon if it's not running and record |
| | |
| | | goto CleanUp; |
| | | |
| | | CleanUp: |
| | | |
| | | |
| | | if( thread_id ) |
| | | { |
| | | if( rv ) |
| | |
| | | pthread_attr_destroy(&thread_attr); |
| | | return rv; |
| | | } |
| | | |
| | | |
| | | /* excute a linux command by system() */ |
| | | void exec_system_cmd(const char *format, ...) |
| | |
| | | |
| | | system(cmd); |
| | | } |
| | | |
| | | |
| | |
| | | * |
| | | ********************************************************************************/ |
| | | |
| | | #ifndef __PROC_H_ |
| | | #define __PROC_H_ |
| | | #ifndef __UTIL_PROC_H_ |
| | | #define __UTIL_PROC_H_ |
| | | |
| | | #include <signal.h> |
| | | #include <time.h> |
| | | |
| | | #define PID_ASCII_SIZE 11 |
| | | |
| | |
| | | |
| | | /* get daemon process ID from $pid_file */ |
| | | extern pid_t get_daemon_pid(const char *pid_file); |
| | | |
| | | /* +------------------------+ |
| | | * | inline 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 ; |
| | | } |
| | | |
| | | #endif |
| New file |
| | |
| | | /******************************************************************************** |
| | | * Copyright: (C) 2020 LingYun IoT System Studio |
| | | * All rights reserved. |
| | | * |
| | | * Filename: util.h |
| | | * Description: This file is common utility functions |
| | | * |
| | | * Version: 1.0.0(7/06/2012~) |
| | | * Author: Guo Wenxue <guowenxue@gmail.com> |
| | | * ChangeLog: 1, Release initial version on "7/06/2012 09:21:33 PM" |
| | | * |
| | | ********************************************************************************/ |
| | | |
| | | #ifndef __UTIL_H_ |
| | | #define __UTIL_H_ |
| | | |
| | | #include <time.h> |
| | | #include <stddef.h> |
| | | |
| | | #define container_of(ptr, type, member) ((type *)((char *)(ptr) - offsetof(type, member))) |
| | | |
| | | /* +------------------------+ |
| | | * | time 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 ; |
| | | } |
| | | |
| | | static inline int check_timeout(time_t *last_time, int interval) |
| | | { |
| | | int timeout = 0; |
| | | time_t now; |
| | | |
| | | time(&now); |
| | | |
| | | if( difftime(now, *last_time)>interval ) |
| | | { |
| | | timeout = 1; |
| | | *last_time = now; |
| | | } |
| | | |
| | | return timeout; |
| | | } |
| | | |
| | | #endif |
| New file |
| | |
| | | /********************************************************************************* |
| | | * Copyright: (C) 2019 LingYun IoT System Studio |
| | | * All rights reserved. |
| | | * |
| | | * Filename: config.c |
| | | * Description: This file is iotd configure file parser function |
| | | * |
| | | * Version: 1.0.0(2019年06月25日) |
| | | * Author: Guo Wenxue <guowenxue@gmail.com> |
| | | * ChangeLog: 1, Release initial version on "2019年06月25日 22时23分55秒" |
| | | * |
| | | ********************************************************************************/ |
| | | #include "config.h" |
| | | #include "logger.h" |
| | | #include "iniparser.h" |
| | | |
| | | int parser_conf(const char *conf_file, iotd_ctx_t *ctx, int debug) |
| | | { |
| | | dictionary *ini; |
| | | const char *str; |
| | | int val; |
| | | int rv = 0; |
| | | logger_t *logger; |
| | | hwinfo_t *hwinfo; |
| | | mqtt_ctx_t *mqtt; |
| | | |
| | | if( !conf_file || !ctx ) |
| | | { |
| | | fprintf(stderr, "%s() Invalid input arguments\n", __func__); |
| | | return -1; |
| | | } |
| | | |
| | | memset(ctx, 0, sizeof(*ctx)); |
| | | logger = &ctx->logger; |
| | | hwinfo = &ctx->hwinfo; |
| | | mqtt = &ctx->mqtt; |
| | | |
| | | ini = iniparser_load(conf_file); |
| | | if( !ini ) |
| | | { |
| | | fprintf(stderr, "ERROR: Configure file '%s' load failed\n", conf_file); |
| | | return -2; |
| | | } |
| | | |
| | | /*+------------------------------------------------------+ |
| | | *| parser logger settings and start logger system | |
| | | *+------------------------------------------------------+*/ |
| | | if( !debug ) |
| | | { |
| | | str = iniparser_getstring(ini, "logger:file", "/tmp/iotd.log"); |
| | | strncpy(logger->logfile, str, sizeof(logger->logfile)); |
| | | logger->logsize = iniparser_getint(ini, "logger:size", 1024); |
| | | logger->loglevel = iniparser_getint(ini, "logger:level", LOG_LEVEL_INFO); |
| | | } |
| | | else |
| | | { |
| | | strncpy(logger->logfile, "console", sizeof(logger->logfile)); |
| | | logger->loglevel = LOG_LEVEL_DEBUG; |
| | | logger->logsize = 0; |
| | | } |
| | | |
| | | if( log_open(logger->logfile, logger->loglevel, logger->logsize, LOG_LOCK_DISABLE) < 0 ) |
| | | { |
| | | fprintf(stderr, "Logger system initialise failure\n"); |
| | | return -2; |
| | | } |
| | | |
| | | log_info("Logger system initialise ok\n"); |
| | | |
| | | |
| | | /*+------------------------------------------------------+ |
| | | *| parser production ID | |
| | | *+------------------------------------------------------+*/ |
| | | |
| | | if( !(str=iniparser_getstring(ini, "common:devid", NULL)) ) |
| | | { |
| | | log_error("ERROR: Parser device ID failure\n"); |
| | | rv = -3; |
| | | goto cleanup; |
| | | } |
| | | /* cJSON parser ID will get "" */ |
| | | strncpy(mqtt->devid, str, sizeof(mqtt->devid)); |
| | | log_info("Parser device ID [%s]\n", mqtt->devid); |
| | | |
| | | |
| | | /*+------------------------------------------------------+ |
| | | *| parser hardware module configuration | |
| | | *+------------------------------------------------------+*/ |
| | | |
| | | /* relay */ |
| | | hwinfo->relay=iniparser_getint(ini, "hardware:relay", 0); |
| | | if( !hwinfo->relay ) |
| | | log_warn("Parser relay module disabled\n"); |
| | | else |
| | | log_info("Parser relay module enabled\n"); |
| | | |
| | | /* RGB 3-colors LED */ |
| | | hwinfo->led=iniparser_getint(ini, "hardware:rgbled", 0); |
| | | if( !hwinfo->led ) |
| | | log_warn("Parser RGB 3-colors Led module disabled\n"); |
| | | else |
| | | log_info("Parser RGB 3-colors Led module enabled\n"); |
| | | |
| | | /* beeper */ |
| | | hwinfo->beeper=iniparser_getint(ini, "hardware:beep", 0); |
| | | if( !hwinfo->beeper ) |
| | | log_warn("Parser beeper module disabled\n"); |
| | | else |
| | | log_info("Parser beeper module enabled\n"); |
| | | |
| | | /* DS18B20 temperature module */ |
| | | hwinfo->ds18b20=iniparser_getint(ini, "hardware:ds18b20", 0); |
| | | if( !hwinfo->ds18b20 ) |
| | | log_warn("Parser DS18B20 temperature module disabled\n"); |
| | | else |
| | | log_info("Parser DS18B20 temperature module enabled\n"); |
| | | |
| | | /* SHT20 temperature and hummidity module */ |
| | | hwinfo->sht2x=iniparser_getint(ini, "hardware:sht2x", 0); |
| | | if( !hwinfo->sht2x ) |
| | | log_warn("Parser SHT2X temperature and hummidity module disabled\n"); |
| | | else |
| | | log_info("Parser SHT2X temperature and hummidity module enabled\n"); |
| | | |
| | | /* TSL2561 light intensity sensor module */ |
| | | hwinfo->tsl2561=iniparser_getint(ini, "hardware:tsl2561", 0); |
| | | if( !hwinfo->tsl2561 ) |
| | | log_warn("Parser TSL2561 light intensity sensor module disabled\n"); |
| | | else |
| | | log_info("Parser TSL2561 light intensity sensor module enabled\n"); |
| | | |
| | | /*+------------------------------------------------------+ |
| | | *| parser broker settings | |
| | | *+------------------------------------------------------+*/ |
| | | |
| | | if( !(str=iniparser_getstring(ini, "broker:hostname", NULL)) ) |
| | | { |
| | | log_error("ERROR: Parser MQTT broker server hostname failure\n"); |
| | | rv = -4; |
| | | goto cleanup; |
| | | } |
| | | strncpy(mqtt->host, str, sizeof(mqtt->host) ); |
| | | |
| | | if( (val=iniparser_getint(ini, "broker:port", -1)) < 0 ) |
| | | { |
| | | log_error("ERROR: Parser MQTT broker server port failure\n"); |
| | | rv = -5; |
| | | goto cleanup; |
| | | } |
| | | mqtt->port = val; |
| | | log_info("Parser MQTT broker server [%s:%d]\n", mqtt->host, mqtt->port); |
| | | |
| | | if( (str=iniparser_getstring(ini, "broker:token", NULL)) ) |
| | | { |
| | | strncpy(mqtt->token, str, sizeof(mqtt->uid) ); |
| | | log_info("Parser broker token [%s]\n", mqtt->token); |
| | | } |
| | | |
| | | if( (str=iniparser_getstring(ini, "broker:username", NULL)) ) |
| | | strncpy(mqtt->uid, str, sizeof(mqtt->uid) ); |
| | | |
| | | if( (str=iniparser_getstring(ini, "broker:password", NULL)) ) |
| | | strncpy(mqtt->pwd, str, sizeof(mqtt->pwd) ); |
| | | |
| | | if( mqtt->uid ) |
| | | log_info("Parser broker account [%s:%s]\n", mqtt->uid, mqtt->pwd); |
| | | |
| | | mqtt->keepalive = iniparser_getint(ini, "broker:keepalive", DEF_KEEPALIVE); |
| | | log_info("Parser broker keepalive timeout [%d] seconds\n", mqtt->keepalive); |
| | | |
| | | /*+------------------------------------------------------+ |
| | | *| parser publisher settings | |
| | | *+------------------------------------------------------+*/ |
| | | |
| | | if( !(str=iniparser_getstring(ini, "publisher:pubTopic", NULL)) ) |
| | | { |
| | | log_error("ERROR: Parser MQTT broker publisher topic failure\n"); |
| | | rv = -6; |
| | | goto cleanup; |
| | | } |
| | | snprintf(mqtt->pubTopic, sizeof(mqtt->pubTopic), "%s/%s", str, mqtt->devid); |
| | | |
| | | mqtt->pubQos = iniparser_getint(ini, "publisher:pubQos", DEF_QOS); |
| | | mqtt->interval = iniparser_getint(ini, "publisher:interval", DEF_PUBINTERVAL); |
| | | log_info("Parser publisher topic \"%s\" with Qos[%d] interval[%d]\n", mqtt->pubTopic, mqtt->pubQos, mqtt->interval); |
| | | |
| | | /*+------------------------------------------------------+ |
| | | *| parser subscriber settings | |
| | | *+------------------------------------------------------+*/ |
| | | |
| | | if( !(str=iniparser_getstring(ini, "subsciber:subTopic", NULL)) ) |
| | | { |
| | | log_error("ERROR: Parser MQTT broker publisher topic failure\n"); |
| | | rv = -7; |
| | | goto cleanup; |
| | | } |
| | | snprintf(mqtt->subTopic, sizeof(mqtt->subTopic), "%s/%s", str, mqtt->devid); |
| | | |
| | | mqtt->subQos = iniparser_getint(ini, "subsciber:subQos", DEF_QOS); |
| | | log_info("Parser subscriber topic \"%s\" with Qos[%d]\n", mqtt->subTopic, mqtt->subQos); |
| | | |
| | | cleanup: |
| | | if( ini ) |
| | | iniparser_freedict(ini); |
| | | |
| | | if( rv ) |
| | | log_close(); |
| | | |
| | | return rv; |
| | | } |
| | | |
| New file |
| | |
| | | /********************************************************************************* |
| | | * Copyright: (C) 2019 LingYun IoT System Studio |
| | | * All rights reserved. |
| | | * |
| | | * Filename: config.h |
| | | * Description: This file is iotd configure file parser function |
| | | * |
| | | * Version: 1.0.0(2019年06月25日) |
| | | * Author: Guo Wenxue <guowenxue@gmail.com> |
| | | * ChangeLog: 1, Release initial version on "2019年06月25日 22时23分55秒" |
| | | * |
| | | ********************************************************************************/ |
| | | #ifndef __CONF_H_ |
| | | #define __CONF_H_ |
| | | |
| | | #include "utils.h" |
| | | |
| | | enum |
| | | { |
| | | Qos0, /* 发送者只发送一次消息,不进行重试,Broker不会返回确认消息。在Qos0情况下,Broker可能没有接受到消息 */ |
| | | Qos1, /* 发送者最少发送一次消息,确保消息到达Broker,Broker需要返回确认消息PUBACK。在Qos1情况下,Broker可能接受到重复消息 */ |
| | | Qos2, /* Qos2使用两阶段确认来保证消息的不丢失和不重复。在Qos2情况下,Broker肯定会收到消息,且只收到一次 */ |
| | | }; |
| | | |
| | | #define DEF_KEEPALIVE 30 |
| | | #define DEF_PUBINTERVAL 120 |
| | | #define DEF_QOS Qos2 |
| | | |
| | | typedef struct hwinfo_s |
| | | { |
| | | int relay; /* relay aviable or not. 0:Disable 1: Enable */ |
| | | int led; /* RGB led aviable or not. 0:Disable 1: Enable */ |
| | | int beeper; /* beeper aviable or not. 0:Disable 1: Enable */ |
| | | int ds18b20; /* DS1B820 aviable or not. 0:Disable 1: Enable */ |
| | | int sht2x; /* SHT20 aviable or not. 0:Disable 1: Enable */ |
| | | int tsl2561; /* TSL2561 aviable or not. 0:Disable 1: Enable */ |
| | | } hwinfo_t; |
| | | |
| | | /* logger settings */ |
| | | typedef struct logger_s |
| | | { |
| | | char logfile[128]; /* logger record file */ |
| | | int loglevel; /* logger level */ |
| | | int logsize; /* logger file maxsize, oversize will rollback */ |
| | | } logger_t; |
| | | |
| | | typedef struct mqtt_ctx_s |
| | | { |
| | | char devid[32]; /* device ID */ |
| | | void *userdata; /* user data pointer */ |
| | | |
| | | /* Broker settings */ |
| | | char host[128]; /* MQTT broker server name */ |
| | | int port; /* MQTT broker listen port */ |
| | | char uid[64]; /* username */ |
| | | char pwd[64]; /* password */ |
| | | char token[64]; /* token */ |
| | | int keepalive; /* MQTT broker send PING message to subsciber/publisher keepalive timeout<seconds> */ |
| | | |
| | | /* Publisher settings */ |
| | | char pubTopic[256]; /* Publisher topic */ |
| | | int pubQos; /* Publisher Qos */ |
| | | int interval ; /* Publish sensor data interval time, unit seconds */ |
| | | |
| | | /* Subscriber settings */ |
| | | char subTopic[256]; /* Subscriber topic */ |
| | | int subQos; /* Subscriber Qos */ |
| | | } mqtt_ctx_t; |
| | | |
| | | typedef struct iotd_ctx_s |
| | | { |
| | | logger_t logger; |
| | | hwinfo_t hwinfo; |
| | | mqtt_ctx_t mqtt; |
| | | } iotd_ctx_t; |
| | | |
| | | /* get iotd_ctx address by mqtt_ctx address */ |
| | | #define to_iotd(ctx) container_of(ctx, iotd_ctx_t, mqtt); |
| | | |
| | | extern int parser_conf(const char *conf_file, iotd_ctx_t *ctx, int debug); |
| | | |
| | | #endif |
| | | |
| File was renamed from project/4.mqttd/etc/mqttd.conf |
| | |
| | | [common] |
| | | devid="RPi3B#01" |
| | | devid="rpi001" |
| | | |
| | | # 树莓派连接的外设信息,0:禁用或未连接 其他: 使能或相关硬件连接的Pin管脚(wPI模式) |
| | | # 树莓派连接的外设信息,0:禁用或未连接 |
| | | [hardware] |
| | | |
| | | # 是否使能 RGB 三色灯模块,0:禁用 1:使能 |
| | | rgbled=1 |
| | | |
| | | # 是否使能继电器模块,0:禁用 1:使能 |
| | | relay=0 |
| | | |
| | | # 是否使能 DS18b20 温度传感器模块,0:禁用 1:使能 |
| | | ds18b20=1 |
| | | ds18b20=0 |
| | | |
| | | # 是否使能 SHT2X 温湿度传感器模块,0:禁用 1:使能 |
| | | sht2x=1 |
| | |
| | | [logger] |
| | | |
| | | # 日志记录文件 |
| | | file=/tmp/mqttd.log |
| | | file=/var/log/iotd.log |
| | | |
| | | # 日志级别: 0:ERROR 1:WARN 2:INFO 3:DEBUG 4:TRACE |
| | | level=2 |
| | |
| | | |
| | | [publisher] |
| | | |
| | | # mosquitto_sub -h main.iot-yun.club -p 10883 -u lingyun -P lingyun -t \$Sys/Studio/Uplink |
| | | # mosquitto_sub -h weike-iot.com -p 8013 -u lingyun -P lingyun -t \$Sys/Studio/Uplink/rpi001 |
| | | pubTopic="$Sys/Studio/Uplink" |
| | | pubQos=0 |
| | | |
| | |
| | | |
| | | [subsciber] |
| | | |
| | | # mosquitto_pub -h main.iot-yun.club -p 10883 -u lingyun -P lingyun -t \$Sys/Studio/Downlink -m '{"id":"RPi3B#01", "leds":[{"red":"on","green":"off","blue":"on"}]}' |
| | | # mosquitto_pub -h weike-iot.com -p 8013 -u lingyun -P lingyun -t \$Sys/Studio/Downlink/rpi001 -m '{ "RedLed":"on" }' |
| | | # mosquitto_pub -h weike-iot.com -p 8013 -u lingyun -P lingyun -t \$Sys/Studio/Downlink/rpi001 -m '{ "GreenLed":"on" }' |
| | | # mosquitto_pub -h weike-iot.com -p 8013 -u lingyun -P lingyun -t \$Sys/Studio/Downlink/rpi001 -m '{ "BlueLed":"on" }' |
| | | # mosquitto_pub -h weike-iot.com -p 8013 -u lingyun -P lingyun -t \$Sys/Studio/Downlink/rpi001 -m '{ "Relay":"on" }' |
| | | subTopic="$Sys/Studio/Downlink" |
| | | subQos=0 |
| | | |
| New file |
| | |
| | | [Unit] |
| | | Description=RaspberryPi iotd program Service |
| | | After=network.target |
| | | |
| | | [Service] |
| | | Type=simple |
| | | ExecStartPre=/bin/rm -f /tmp/.iotd.pid /var/log/iotd.log |
| | | ExecStart=/usr/bin/iotd -c /etc/iotd.conf |
| | | ExecStop=/bin/rm -f /tmp/.iotd.pid |
| | | |
| | | Restart=always |
| | | RestartSec=2 |
| | | |
| | | [Install] |
| | | WantedBy=multi-user.target |
| | | |
| New file |
| | |
| | | /********************************************************************************* |
| | | * Copyright: (C) 2019 LingYun IoT System Studio |
| | | * All rights reserved. |
| | | * |
| | | * Filename: main.c |
| | | * Description: This file is MQTT publisher/subscriber example program. |
| | | * |
| | | * Version: 1.0.0(29/01/19) |
| | | * Author: Guo Wenxue <guowenxue@gmail.com> |
| | | * ChangeLog: 1, Release initial version on "29/01/19 15:34:41" |
| | | * |
| | | ********************************************************************************/ |
| | | #include <stdio.h> |
| | | #include <stdlib.h> |
| | | #include <unistd.h> |
| | | #include <time.h> |
| | | #include <getopt.h> |
| | | #include <libgen.h> |
| | | #include <string.h> |
| | | #include <errno.h> |
| | | |
| | | #include <mosquitto.h> |
| | | #include <cjson/cJSON.h> |
| | | |
| | | #include "logger.h" |
| | | #include "proc.h" |
| | | #include "config.h" |
| | | #include "ds18b20.h" |
| | | #include "sht20.h" |
| | | #include "leds.h" |
| | | #include "relay.h" |
| | | |
| | | #define PROG_VERSION "v1.0.0" |
| | | #define DAEMON_PIDFILE "/tmp/.iotd.pid" |
| | | |
| | | void *mqtt_worker(void *args); |
| | | |
| | | static void program_usage(char *progname) |
| | | { |
| | | printf("Usage: %s [OPTION]...\n", progname); |
| | | printf(" %s is LingYun studio iotd program running on RaspberryPi\n", progname); |
| | | |
| | | printf("\nMandatory arguments to long options are mandatory for short options too:\n"); |
| | | printf(" -b[daemon ] Running in daemon mode\n"); |
| | | printf(" -d[debug ] Running in debug mode\n"); |
| | | printf(" -c[conf ] Specify configure file\n"); |
| | | printf(" -h[help ] Display this help information\n"); |
| | | printf(" -v[version ] Display the program version\n"); |
| | | |
| | | printf("\n%s version %s\n", progname, PROG_VERSION); |
| | | return; |
| | | } |
| | | |
| | | int main (int argc, char **argv) |
| | | { |
| | | int daemon = 0; |
| | | pthread_t tid; |
| | | iotd_ctx_t ctx; |
| | | char *conf_file="/etc/iotd.conf"; |
| | | int debug = 0; |
| | | int opt; |
| | | char *progname=NULL; |
| | | |
| | | struct option long_options[] = { |
| | | {"conf", required_argument, NULL, 'c'}, |
| | | {"daemon", no_argument, NULL, 'b'}, |
| | | {"debug", no_argument, NULL, 'd'}, |
| | | {"version", no_argument, NULL, 'v'}, |
| | | {"help", no_argument, NULL, 'h'}, |
| | | {NULL, 0, NULL, 0} |
| | | }; |
| | | |
| | | progname = (char *)basename(argv[0]); |
| | | |
| | | /* parser the command line parameters */ |
| | | while ((opt = getopt_long(argc, argv, "c:bdvh", long_options, NULL)) != -1) |
| | | { |
| | | switch (opt) |
| | | { |
| | | case 'c': /* Set configure file */ |
| | | conf_file = optarg; |
| | | break; |
| | | |
| | | case 'b': /* Set daemon running */ |
| | | daemon = 1; |
| | | break; |
| | | |
| | | case 'd': /* Set debug running */ |
| | | debug = 1; |
| | | break; |
| | | |
| | | case 'v': /* Get software version */ |
| | | printf("%s version %s\n", progname, PROG_VERSION); |
| | | return 0; |
| | | |
| | | case 'h': /* Get help information */ |
| | | program_usage(progname); |
| | | return 0; |
| | | |
| | | default: |
| | | break; |
| | | } |
| | | } |
| | | |
| | | /* parser configure file */ |
| | | if( parser_conf(conf_file, &ctx, debug)<0 ) |
| | | { |
| | | fprintf(stderr, "Parser iotd configure file failure\n"); |
| | | return -2; |
| | | } |
| | | |
| | | /* install signal proc handler */ |
| | | install_default_signal(); |
| | | |
| | | /* check program already running or not, if already running then exit, or set running as daemon */ |
| | | if( check_set_program_running(daemon, DAEMON_PIDFILE) < 0 ) |
| | | goto cleanup; |
| | | |
| | | /* initial mosquitto library */ |
| | | mosquitto_lib_init(); |
| | | |
| | | /* start MQTT thread */ |
| | | if( thread_start(&tid, mqtt_worker, &ctx.mqtt ) < 0 ) |
| | | { |
| | | log_error("Start MQTT worker thread failure\n"); |
| | | goto cleanup; |
| | | } |
| | | log_info("Start MQTT worker thread ok\n"); |
| | | |
| | | /* control thread loop */ |
| | | while( !g_signal.stop ) |
| | | { |
| | | sleep(1); |
| | | } |
| | | |
| | | cleanup: |
| | | mosquitto_lib_cleanup(); |
| | | log_close(); |
| | | |
| | | return 0; |
| | | } |
| | | |
| | | |
| | | /* |
| | | * +--------------------------------+ |
| | | * | MQTT Thread worker | |
| | | * +--------------------------------+ |
| | | */ |
| | | |
| | | /* process publisher(uplink) data */ |
| | | void pub_connect_callback(struct mosquitto *mosq, void *userdata, int result) |
| | | { |
| | | mqtt_ctx_t *mqtt = (mqtt_ctx_t *)userdata; |
| | | iotd_ctx_t *iotd = to_iotd(mqtt); |
| | | int rv = 0; |
| | | char msg[128]; |
| | | float temp = 0.0; /* temperature */ |
| | | float rh = 0.0; /* relative humidity */ |
| | | int retain = 0; |
| | | static time_t last_time = 0; |
| | | |
| | | /* publish time is not arrive */ |
| | | if( !check_timeout(&last_time, mqtt->interval) ) |
| | | return ; |
| | | |
| | | log_debug("Publish topic '%s'\n", mqtt->pubTopic); |
| | | |
| | | if( iotd->hwinfo.ds18b20 ) |
| | | { |
| | | log_debug("DS18B20 temperature sensor enabled, start broadcast it\n"); |
| | | if( ds18b20_get_temperature(&temp)<0 ) |
| | | return ; |
| | | |
| | | memset(msg, 0, sizeof(msg)); |
| | | snprintf(msg, sizeof(msg), "{\"temperature\":%.2f}", temp); |
| | | rv = mosquitto_publish(mosq, NULL, mqtt->pubTopic, strlen(msg), msg, mqtt->pubQos, retain); |
| | | if( rv ) |
| | | { |
| | | log_error("Publisher broadcast message '%s' failure: %d\n", msg, rv); |
| | | } |
| | | else |
| | | { |
| | | log_info("Publisher broadcast message '%s' ok\n", msg); |
| | | } |
| | | } |
| | | |
| | | if( iotd->hwinfo.sht2x ) |
| | | { |
| | | log_debug("SHT2X temperature and humidity sensor enabled, start broadcast it\n"); |
| | | if( sht2x_get_temp_humidity(&temp, &rh)<0 ) |
| | | return ; |
| | | |
| | | memset(msg, 0, sizeof(msg)); |
| | | snprintf(msg, sizeof(msg), "{\"temperature\":%.2f, \"humidity\":%.2f}", temp, rh); |
| | | rv = mosquitto_publish(mosq, NULL, mqtt->pubTopic, strlen(msg), msg, mqtt->pubQos, retain); |
| | | if( rv ) |
| | | { |
| | | log_error("Publisher broadcast message '%s' failure: %d\n", msg, rv); |
| | | } |
| | | else |
| | | { |
| | | log_info("Publisher broadcast message '%s' ok\n", msg); |
| | | } |
| | | } |
| | | |
| | | return ; |
| | | } |
| | | |
| | | /* process subscriber(downlink) data */ |
| | | void proc_json_items(cJSON *root, mqtt_ctx_t *mqtt) |
| | | { |
| | | iotd_ctx_t *iotd = to_iotd(mqtt); |
| | | cJSON *item; |
| | | char leds[LED_CNT][16] = {"RedLed", "GreenLed", "BlueLed"}; |
| | | char relays[RELAY_CNT][16] = {"Relay"}; |
| | | int which; |
| | | |
| | | if( !root ) |
| | | { |
| | | log_error("Invalid input arguments $root\n"); |
| | | return ; |
| | | } |
| | | |
| | | /* parser JSON command for relays */ |
| | | if( iotd->hwinfo.relay ) |
| | | { |
| | | for(which=0; which<RELAY_CNT; which++) |
| | | { |
| | | item = cJSON_GetObjectItem(root, relays[which]); |
| | | if( !item ) |
| | | continue; |
| | | |
| | | if( strcasecmp(item->valuestring, "on") ) |
| | | turn_relay(which, ON); |
| | | else if( strcasecmp(item->valuestring, "off") ) |
| | | turn_relay(which, OFF); |
| | | } |
| | | } |
| | | |
| | | /* parser JSON command for RGB leds */ |
| | | if( iotd->hwinfo.led ) |
| | | { |
| | | for(which=0; which<LED_CNT; which++) |
| | | { |
| | | item = cJSON_GetObjectItem(root, leds[which]); |
| | | if( !item ) |
| | | continue; |
| | | |
| | | if( strcasecmp(item->valuestring, "on") ) |
| | | turn_led(which, ON); |
| | | else if( strcasecmp(item->valuestring, "off") ) |
| | | turn_led(which, OFF); |
| | | } |
| | | } |
| | | |
| | | return ; |
| | | } |
| | | |
| | | void sub_message_callback(struct mosquitto *mosq, void *userdata, const struct mosquitto_message *message) |
| | | { |
| | | mqtt_ctx_t *ctx = (mqtt_ctx_t *)userdata; |
| | | |
| | | cJSON *root = NULL; |
| | | |
| | | if ( !message->payloadlen ) |
| | | { |
| | | log_error("%s (null)\n", message->topic); |
| | | return ; |
| | | } |
| | | |
| | | log_info("Subscriber receive message: '%s'\n", message->payload); |
| | | |
| | | root = cJSON_Parse(message->payload); |
| | | if( !root ) |
| | | { |
| | | log_error("cJSON_Parse parser failure: %s\n", cJSON_GetErrorPtr()); |
| | | return ; |
| | | } |
| | | |
| | | /* process receive message data */ |
| | | proc_json_items(root, ctx); |
| | | |
| | | /* must delete it, or it will result memory leak */ |
| | | cJSON_Delete(root); |
| | | |
| | | return ; |
| | | } |
| | | |
| | | void mqtt_connect_callback(struct mosquitto *mosq, void *userdata, int result) |
| | | { |
| | | mqtt_ctx_t *ctx = (mqtt_ctx_t *)userdata; |
| | | |
| | | if( result ) |
| | | { |
| | | log_error("mosquitto connect to broker server failed, rv=%d\n", result); |
| | | return ; |
| | | } |
| | | |
| | | log_info("mosquitto connect to broker server[%s:%d] successfully\n", ctx->host, ctx->port); |
| | | mosquitto_subscribe(mosq, NULL, ctx->subTopic, ctx->subQos); |
| | | } |
| | | |
| | | void mqtt_disconnect_callback(struct mosquitto *mosq, void *userdata, int result) |
| | | { |
| | | mqtt_ctx_t *ctx = (mqtt_ctx_t *)userdata; |
| | | |
| | | log_warn("mosquitto disconnect to broker server[%s:%d], reason=%d\n", ctx->host, ctx->port, result); |
| | | } |
| | | |
| | | void *mqtt_worker(void *args) |
| | | { |
| | | mqtt_ctx_t *ctx = (mqtt_ctx_t *)args; |
| | | struct mosquitto *mosq; |
| | | bool session = true; |
| | | int rv = 0; |
| | | |
| | | mosq = mosquitto_new(ctx->devid, session, ctx); |
| | | if( !mosq ) |
| | | { |
| | | log_error("mosquitto_new failure\n"); |
| | | return NULL; |
| | | } |
| | | |
| | | /* connnect to broker by token or uid/pwd */ |
| | | if( strlen(ctx->token ) > 0) |
| | | { |
| | | log_info("Using token authentication: '%s'\n", ctx->token); |
| | | mosquitto_username_pw_set(mosq, ctx->token, NULL); |
| | | } |
| | | else if( strlen(ctx->uid)> 0 && strlen(ctx->pwd)> 0 ) |
| | | { |
| | | log_info("Using username/password authentication\n"); |
| | | mosquitto_username_pw_set(mosq, ctx->uid, ctx->pwd); |
| | | } |
| | | |
| | | /* set callback functions */ |
| | | mosquitto_connect_callback_set(mosq, mqtt_connect_callback); |
| | | mosquitto_disconnect_callback_set(mosq, mqtt_disconnect_callback); |
| | | mosquitto_message_callback_set(mosq, sub_message_callback); |
| | | |
| | | while( !g_signal.stop ) |
| | | { |
| | | /* connect to MQTT broker */ |
| | | if( mosquitto_connect(mosq, ctx->host, ctx->port, ctx->keepalive) ) |
| | | { |
| | | log_error("mosquitto connect to broker[%s:%d] failure: %s\n", ctx->host, ctx->port, strerror(errno)); |
| | | sleep(1); |
| | | continue; |
| | | } |
| | | |
| | | while(!g_signal.stop) |
| | | { |
| | | /* periodically publish and report data */ |
| | | pub_connect_callback(mosq, ctx, MOSQ_ERR_SUCCESS); |
| | | |
| | | /* MQTT process in Non-blocking mode, timeout for 1s */ |
| | | if( MOSQ_ERR_SUCCESS != (rv = mosquitto_loop(mosq, 1000, 1)) ) |
| | | { |
| | | log_warn("MQTT loop error: %s, reconnecting...\n", mosquitto_strerror(rv)); |
| | | break; |
| | | } |
| | | } |
| | | |
| | | mosquitto_disconnect(mosq); |
| | | sleep(1); |
| | | } |
| | | |
| | | mosquitto_destroy(mosq); |
| | | return NULL; |
| | | } |
| | | |
| | |
| | | #******************************************************************************* |
| | | |
| | | PRJ_PATH=$(shell pwd) |
| | | APP_NAME = mqttd |
| | | APP_NAME = iotd |
| | | |
| | | BUILD_ARCH=$(shell uname -m) |
| | | ifneq ($(findstring $(BUILD_ARCH), "x86_64" "i386"),) |
| | |
| | | CFLAGS = -Wall -Wshadow -Wundef -Wmaybe-uninitialized -D_GNU_SOURCE |
| | | |
| | | # C source file in sub-directory |
| | | SRCS=booster |
| | | SRCS=booster modules |
| | | SRCS_PATH=$(patsubst %,${PRJ_PATH}/%,$(SRCS)) |
| | | CFLAGS+=$(patsubst %,-I%,$(SRCS_PATH)) |
| | | LDFLAGS+=$(patsubst %,-L%,$(SRCS_PATH)) |
| | |
| | | LDFLAGS+=-lpthread |
| | | |
| | | all: entry subdir |
| | | ${CROSS_COMPILE}gcc ${CFLAGS} mqttd.c -o ${APP_NAME} ${LDFLAGS} |
| | | ${CROSS_COMPILE}gcc ${CFLAGS} ${SRCFILES} -o ${APP_NAME} ${LDFLAGS} |
| | | |
| | | entry: |
| | | @echo "Building ${APP_NAME} on ${BUILD_ARCH}" |
| | | |
| | | subdir: |
| | | @for dir in ${libs} ; do CFLAGS="${CFLAGS}" make -C $${dir}; done |
| | | @for dir in ${libs} ; do make -C $${dir} ; done |
| | | |
| | | install: |
| | | cp ${APP_NAME} /tftp |
| File was renamed from project/4.mqttd/booster/leds.c |
| | |
| | | return -2; |
| | | } |
| | | |
| | | for(i=0; i<ctx->count; i++) |
| | | { |
| | | if( which == i ) |
| | | { |
| | | led = &ctx->leds[i]; |
| | | for(i=0; i<ctx->count; i++) |
| | | { |
| | | if( which == i ) |
| | | { |
| | | led = &ctx->leds[i]; |
| | | |
| | | led->line = gpiod_chip_get_line(ctx->chip, led->gpio); |
| | | if( !led->line ) |
| | | { |
| | | log_error("open gpioline for %s[%d] failed\n", led->name, led->gpio); |
| | | rv = -3; |
| | | goto failed; |
| | | } |
| | | led->line = gpiod_chip_get_line(ctx->chip, led->gpio); |
| | | if( !led->line ) |
| | | { |
| | | log_error("open gpioline for %s[%d] failed\n", led->name, led->gpio); |
| | | rv = -3; |
| | | goto failed; |
| | | } |
| | | |
| | | rv = gpiod_line_request_output(led->line, led->name, !led->active); |
| | | if( rv ) |
| | | { |
| | | log_error("request gpio output for %5s[%d] failed: %s\n", led->name, led->gpio, strerror(errno)); |
| | | rv = -4; |
| | | goto failed; |
| | | } |
| | | rv = gpiod_line_request_output(led->line, led->name, !led->active); |
| | | if( rv ) |
| | | { |
| | | log_error("request gpio output for %5s[%d] failed: %s\n", led->name, led->gpio, strerror(errno)); |
| | | rv = -4; |
| | | goto failed; |
| | | } |
| | | |
| | | log_debug("request %5s led[%d] for gpio output okay\n", led->name, led->gpio); |
| | | } |
| | | } |
| | | log_debug("request %5s led[%d] for gpio output okay\n", led->name, led->gpio); |
| | | } |
| | | } |
| | | |
| | | return 0; |
| | | |
| | |
| | | int i; |
| | | led_info_t *led; |
| | | |
| | | log_warn("terminate RGB Led gpios\n"); |
| | | log_debug("terminate RGB Led gpios\n"); |
| | | |
| | | if( !ctx ) |
| | | { |
| | |
| | | return 0; |
| | | |
| | | for(i=0; i<ctx->count; i++) |
| | | { |
| | | if( which == i ) |
| | | { |
| | | led = &ctx->leds[i]; |
| | | { |
| | | if( which == i ) |
| | | { |
| | | led = &ctx->leds[i]; |
| | | |
| | | if( led->line ) |
| | | gpiod_line_release(led->line); |
| | | } |
| | | } |
| | | if( led->line ) |
| | | gpiod_line_release(led->line); |
| | | } |
| | | } |
| | | |
| | | gpiod_chip_close(ctx->chip); |
| | | return 0; |
| | |
| | | |
| | | led = &ctx.leds[which]; |
| | | |
| | | log_info("turn Led %s %s\n", led->name, cmd?"on":"off"); |
| | | |
| | | if( OFF == cmd ) |
| | | { |
| | | gpiod_line_set_value(led->line, !led->active); |
| New file |
| | |
| | | #******************************************************************************** |
| | | # Copyright: (C) 2023 LingYun IoT System Studio |
| | | # All rights reserved. |
| | | # |
| | | # Filename: Makefile |
| | | # Description: This file used compile all the source code to static library |
| | | # |
| | | # Version: 1.0.0(11/08/23) |
| | | # Author: Guo Wenxue <guowenxue@gmail.com> |
| | | # ChangeLog: 1, Release initial version on "11/08/23 16:18:43" |
| | | # |
| | | #******************************************************************************* |
| | | |
| | | PWD=$(shell pwd) |
| | | LIBNAME=$(shell basename ${PWD} ) |
| | | TOPDIR=$(shell dirname ${PWD} ) |
| | | |
| | | BUILD_ARCH=$(shell uname -m) |
| | | ifneq ($(findstring $(BUILD_ARCH), "x86_64" "i386"),) |
| | | CROSS_COMPILE?=arm-linux-gnueabihf- |
| | | endif |
| | | |
| | | OPENLIBS_INCPATH=${TOPDIR}/openlibs/install/include |
| | | OPENLIBS_LIBPATH=${TOPDIR}/openlibs/install/lib |
| | | |
| | | CFLAGS+=-I${OPENLIBS_INCPATH} -I${TOPDIR}/booster |
| | | |
| | | all: prelibs clean |
| | | @rm -f *.o |
| | | ${CROSS_COMPILE}gcc ${CFLAGS} -c *.c |
| | | ${CROSS_COMPILE}ar -rcs lib${LIBNAME}.a *.o |
| | | |
| | | prelibs: |
| | | if [ -n "${CROSS_COMPILE}" ] ; then cd ${TOPDIR}/openlibs/libgpiod && ./build.sh ; fi; |
| | | |
| | | clean: |
| | | @rm -f *.o |
| | | @rm -f *.a |
| | | |
| | | distclean: |
| | | @make clean |
| New file |
| | |
| | | /******************************************************************************** |
| | | * Copyright: (C) 2023 LingYun IoT System Studio |
| | | * All rights reserved. |
| | | * |
| | | * Filename: modules.h |
| | | * Description: This file |
| | | * |
| | | * Version: 1.0.0(08/17/2023) |
| | | * Author: Guo Wenxue <guowenxue@gmail.com> |
| | | * ChangeLog: 1, Release initial version on "08/17/2023 09:27:25 PM" |
| | | * |
| | | ********************************************************************************/ |
| | | |
| | | #ifndef _MODULES_H_ |
| | | #define _MODULES_H_ |
| | | |
| | | #include "ds18b20.h" |
| | | #include "leds.h" |
| | | #include "pwm.h" |
| | | #include "relay.h" |
| | | #include "sht20.h" |
| | | #include "tsl2561.h" |
| | | |
| | | #endif /* ----- #ifndef _MODULES_H_ ----- */ |
| New file |
| | |
| | | /********************************************************************************* |
| | | * Copyright: (C) 2021 LingYun IoT System Studio |
| | | * All rights reserved. |
| | | * |
| | | * Filename: pwm.c |
| | | * Description: This file is used to control PWM buzzer/Led |
| | | * |
| | | * Pin connection: |
| | | * PWM Module Raspberry Pi Board |
| | | * VCC <-----> 5V |
| | | * buzzer <-----> #Pin32(BCM GPIO12) |
| | | * Led <-----> #Pin33(BCM GPIO13) |
| | | * GND <-----> GND |
| | | * |
| | | * /boot/config.txt: |
| | | * |
| | | * dtoverlay=pwm,pin=12,func=4 (Buzzer) |
| | | * dtoverlay=pwm,pin=13,func=4 (Led) |
| | | * |
| | | ********************************************************************************/ |
| | | |
| | | #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 "logger.h" |
| | | #include "utils.h" |
| | | #include "pwm.h" |
| | | |
| | | /* check PWM $channel export or not */ |
| | | int check_pwm(int channel) |
| | | { |
| | | char path[256]; |
| | | |
| | | /* check /sys/class/pwm/pwmchip0/pwmN exist or not */ |
| | | snprintf(path, sizeof(path), "%s/pwm%d", PWMCHIP_PATH, channel); |
| | | return access(path, F_OK) ? 0 : 1; |
| | | } |
| | | |
| | | /* export($export=1)/unexport($export=0) PWM $channel */ |
| | | int export_pwm(int channel, int export) |
| | | { |
| | | int fd; |
| | | char buf[32]; |
| | | char path[256]; |
| | | |
| | | if( export && check_pwm(channel) ) |
| | | return 0; /* export already when export */ |
| | | else if( !export && !check_pwm(channel) ) |
| | | return 0; /* unexport already when unexport */ |
| | | |
| | | /* export PWM channel by echo N > /sys/class/pwm/pwmchip0/export */ |
| | | snprintf(path, sizeof(path), "%s/%s", PWMCHIP_PATH, export?"export":"unexport"); |
| | | if( (fd=open(path, O_WRONLY)) < 0 ) |
| | | { |
| | | log_error("open '%s' failed: %s\n", path, strerror(errno)); |
| | | return -3; |
| | | } |
| | | |
| | | snprintf(buf, sizeof(buf), "%d", channel); |
| | | write(fd, buf, strlen(buf)); |
| | | |
| | | msleep(100); |
| | | |
| | | if( export && check_pwm(channel) ) |
| | | return 0; /* export already when export */ |
| | | else if( !export && !check_pwm(channel) ) |
| | | return 0; /* unexport already when unexport */ |
| | | |
| | | return -4; |
| | | } |
| | | |
| | | /* configure PWM $channel */ |
| | | int config_pwm(int channel, int freq, int duty) |
| | | { |
| | | int fd; |
| | | char buf[32]; |
| | | char path[256]; |
| | | int period; |
| | | int duty_cycle; |
| | | |
| | | if( !check_pwm(channel) ) |
| | | return -2; |
| | | |
| | | /* open PWM period file and write period in ns */ |
| | | snprintf(path, sizeof(path), "%s/pwm%d/period", PWMCHIP_PATH, channel); |
| | | if( (fd=open(path, O_WRONLY)) < 0 ) |
| | | { |
| | | log_error("open '%s' failed: %s\n", path, strerror(errno)); |
| | | return -3; |
| | | } |
| | | period = 1000000000/freq; |
| | | snprintf(buf, sizeof(buf), "%d", period); |
| | | write(fd, buf, strlen(buf)); |
| | | |
| | | /* open PWM duty_cycle file and write duty */ |
| | | snprintf(path, sizeof(path), "%s/pwm%d/duty_cycle", PWMCHIP_PATH, channel); |
| | | if( (fd=open(path, O_WRONLY)) < 0 ) |
| | | { |
| | | log_error("open '%s' failed: %s\n", path, strerror(errno)); |
| | | return -3; |
| | | } |
| | | duty_cycle = (period*duty) / 100; |
| | | snprintf(buf, sizeof(buf), "%d", duty_cycle); |
| | | write(fd, buf, strlen(buf)); |
| | | |
| | | return 0; |
| | | } |
| | | |
| | | int init_pwm(int channel, int freq, int duty) |
| | | { |
| | | int rv; |
| | | char buf[32]; |
| | | char path[256]; |
| | | |
| | | if( (rv=export_pwm(channel, 1)) ) |
| | | { |
| | | log_error("export PWM channel[%d] failed, rv=%d\n", channel, rv); |
| | | return rv; |
| | | } |
| | | |
| | | if( (rv=config_pwm(channel, freq, duty)) ) |
| | | { |
| | | log_error("config PWM channel[%d] failed, rv=%d\n", channel, rv); |
| | | return rv; |
| | | } |
| | | |
| | | log_debug("config pwm%d with freq[%d] duty[%d] okay\n", channel, freq, duty); |
| | | |
| | | return 0; |
| | | } |
| | | |
| | | int turn_pwm(int channel, int status) |
| | | { |
| | | int fd; |
| | | char buf[32]; |
| | | char path[256]; |
| | | |
| | | if( !check_pwm(channel) ) |
| | | return -1; |
| | | |
| | | /* open PWM enable file and enable(1)/disable(0) it */ |
| | | snprintf(path, sizeof(path), "%s/pwm%d/enable", PWMCHIP_PATH, channel); |
| | | if( (fd=open(path, O_WRONLY)) < 0 ) |
| | | { |
| | | log_error("open '%s' failed: %s\n", path, strerror(errno)); |
| | | return -3; |
| | | } |
| | | snprintf(buf, sizeof(buf), "%d", status?1:0); |
| | | write(fd, buf, strlen(buf)); |
| | | |
| | | log_debug("pwm[%d] %s\n", channel, status?"enable":"disable"); |
| | | |
| | | return 0; |
| | | } |
| | | |
| | | int term_pwm(int channel) |
| | | { |
| | | if( !check_pwm(channel) ) |
| | | return 0; |
| | | |
| | | turn_pwm(channel, DISABLE); |
| | | export_pwm(channel, 0); |
| | | |
| | | return 0; |
| | | } |
| | | |
| | | int turn_beep(int times) |
| | | { |
| | | int rv; |
| | | |
| | | /* stop beeper beep */ |
| | | if(times == 0) |
| | | { |
| | | log_debug("Stop beeper\n"); |
| | | term_pwm(CHN_BEEPER); |
| | | return 0; |
| | | } |
| | | |
| | | rv = init_pwm(CHN_BEEPER, FRQ_BEEPER, 50); |
| | | if(rv < 0) |
| | | { |
| | | log_error("Initial beeper pwm[%d] failed, rv=%d\n", CHN_BEEPER); |
| | | return -2; |
| | | } |
| | | |
| | | /* turn beeper beep ceaselessly */ |
| | | if( times < 0) |
| | | { |
| | | turn_pwm(CHN_BEEPER, ENABLE); |
| | | return 0; |
| | | } |
| | | |
| | | while( times-- ) |
| | | { |
| | | turn_pwm(CHN_BEEPER, ENABLE); |
| | | msleep(800); |
| | | |
| | | turn_pwm(CHN_BEEPER, DISABLE); |
| | | msleep(800); |
| | | } |
| | | |
| | | term_pwm(CHN_BEEPER); |
| | | return 0; |
| | | } |
| New file |
| | |
| | | /********************************************************************************* |
| | | * Copyright: (C) 2021 LingYun IoT System Studio |
| | | * All rights reserved. |
| | | * |
| | | * Filename: pwm.h |
| | | * Description: This file is used to control PWM buzzer/Led |
| | | * |
| | | * Pin connection: |
| | | * PWM Module Raspberry Pi Board |
| | | * VCC <-----> 5V |
| | | * buzzer <-----> #Pin32(BCM GPIO12) |
| | | * Led <-----> #Pin33(BCM GPIO13) |
| | | * GND <-----> GND |
| | | * |
| | | * /boot/config.txt: |
| | | * |
| | | * dtoverlay=pwm,pin=12,func=4 (Buzzer) |
| | | * dtoverlay=pwm,pin=13,func=4 (Led) |
| | | * |
| | | ********************************************************************************/ |
| | | |
| | | |
| | | #ifndef _PWM_H_ |
| | | #define _PWM_H_ |
| | | |
| | | #define PWMCHIP_PATH "/sys/class/pwm/pwmchip0" |
| | | |
| | | #define ENABLE 1 |
| | | #define DISABLE 0 |
| | | |
| | | #define CHN_BEEPER 0 |
| | | #define FRQ_BEEPER 2700 |
| | | |
| | | #define CHN_RGBLED 1 |
| | | #define FRQ_RGBLED 100 |
| | | |
| | | extern int init_pwm(int channel, int freq, int duty); |
| | | extern int turn_pwm(int channel, int status); |
| | | extern int term_pwm(int channel); |
| | | |
| | | extern int turn_beep(int times); |
| | | |
| | | #endif /* ----- #ifndef _PWM_H_ ----- */ |
| | | |
| New file |
| | |
| | | /********************************************************************************* |
| | | * Copyright: (C) 2021 LingYun IoT System Studio |
| | | * All rights reserved. |
| | | * |
| | | * Filename: relay.c |
| | | * Description: This file is used to control Relay |
| | | * |
| | | * |
| | | * Pin connection: |
| | | * Relay Module Raspberry Pi Board |
| | | * VCC <-----> 5V |
| | | * I <-----> #Pin16(BCM GPIO23) |
| | | * GND <-----> GND |
| | | * |
| | | * System install: |
| | | * sudo apt install -y libgpiod-dev gpiod |
| | | * |
| | | * |
| | | ********************************************************************************/ |
| | | |
| | | #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 <gpiod.h> |
| | | #include "logger.h" |
| | | #include "relay.h" |
| | | |
| | | #define DELAY 500 |
| | | |
| | | static relay_info_t relay_info[RELAY_CNT] = |
| | | { |
| | | {"relay1", 23, 1, NULL }, |
| | | }; |
| | | |
| | | int init_relay(relay_ctx_t *ctx) |
| | | { |
| | | int i, rv; |
| | | relay_info_t *relay; |
| | | |
| | | if( !ctx ) |
| | | { |
| | | log_error("Invalid input arguments\n"); |
| | | return -1; |
| | | } |
| | | |
| | | ctx->relay = relay_info; |
| | | ctx->count = RELAY_CNT; |
| | | |
| | | ctx->chip = gpiod_chip_open_by_name("gpiochip0"); |
| | | if( !ctx->chip ) |
| | | { |
| | | log_error("open gpiochip failure, maybe you need running as root\n"); |
| | | return -2; |
| | | } |
| | | |
| | | |
| | | for(i=0; i<ctx->count; i++) |
| | | { |
| | | relay = &ctx->relay[i]; |
| | | |
| | | relay->line = gpiod_chip_get_line(ctx->chip, relay->gpio); |
| | | if( !relay->line ) |
| | | { |
| | | log_error("open gpioline for %s[%d] failed\n", relay->name, relay->gpio); |
| | | rv = -3; |
| | | goto failed; |
| | | } |
| | | |
| | | rv = gpiod_line_request_output(relay->line, relay->name, !relay->active); |
| | | if( rv ) |
| | | { |
| | | log_error("request gpio output for %5s[%d] failed\n", relay->name, relay->gpio); |
| | | rv = -4; |
| | | goto failed; |
| | | } |
| | | |
| | | log_debug("request %s[%d] for gpio output okay\n", relay->name, relay->gpio); |
| | | } |
| | | |
| | | return 0; |
| | | |
| | | failed: |
| | | term_relay(ctx); |
| | | return rv; |
| | | } |
| | | |
| | | int term_relay(relay_ctx_t *ctx) |
| | | { |
| | | int i; |
| | | relay_info_t *relay; |
| | | |
| | | if( !ctx ) |
| | | { |
| | | log_error("Invalid input arguments\n"); |
| | | return -1; |
| | | } |
| | | |
| | | if( !ctx->chip ) |
| | | return 0; |
| | | |
| | | for(i=0; i<ctx->count; i++) |
| | | { |
| | | relay = &ctx->relay[i]; |
| | | |
| | | if( relay->line ) |
| | | gpiod_line_release(relay->line); |
| | | } |
| | | |
| | | gpiod_chip_close(ctx->chip); |
| | | return 0; |
| | | } |
| | | |
| | | |
| | | int turn_relay(int which, int cmd) |
| | | { |
| | | int rv = 0; |
| | | relay_info_t *relay; |
| | | relay_ctx_t ctx; |
| | | |
| | | if( which<0 || which>=RELAY_CNT ) |
| | | { |
| | | log_error("Invalid input arguments\n"); |
| | | return -1; |
| | | } |
| | | |
| | | if( (rv=init_relay(&ctx)) < 0 ) |
| | | { |
| | | log_error("Initial relay failure, rv=%d\n", rv); |
| | | return -2; |
| | | } |
| | | |
| | | relay = &ctx.relay[which]; |
| | | log_info("turn Led %s %s\n", relay->name, cmd?"on":"off"); |
| | | |
| | | if( OFF == cmd ) |
| | | { |
| | | gpiod_line_set_value(relay->line, !relay->active); |
| | | } |
| | | else |
| | | { |
| | | gpiod_line_set_value(relay->line, relay->active); |
| | | } |
| | | |
| | | term_relay(&ctx); |
| | | |
| | | return 0; |
| | | } |
| | | |
| New file |
| | |
| | | /********************************************************************************* |
| | | * Copyright: (C) 2021 LingYun IoT System Studio |
| | | * All rights reserved. |
| | | * |
| | | * Filename: relay.c |
| | | * Description: This file is used to control Relay |
| | | * |
| | | * |
| | | * Pin connection: |
| | | * Relay Module Raspberry Pi Board |
| | | * VCC <-----> 5V |
| | | * I <-----> #Pin16(BCM GPIO23) |
| | | * GND <-----> GND |
| | | * |
| | | * System install: |
| | | * sudo apt install -y libgpiod-dev gpiod |
| | | * |
| | | * |
| | | ********************************************************************************/ |
| | | |
| | | #ifndef _RELAY_H_ |
| | | #define _RELAY_H_ |
| | | |
| | | #define ON 1 |
| | | #define OFF 0 |
| | | |
| | | /* relay code */ |
| | | enum |
| | | { |
| | | RELAY1 = 0, |
| | | RELAY_CNT, |
| | | }; |
| | | |
| | | /* Relay hardware information */ |
| | | typedef struct relay_info_s |
| | | { |
| | | const char *name; /* Relay name */ |
| | | int gpio; /* Relay BCM pin number */ |
| | | int active;/* Relay active GPIO level: 0->low 1->high */ |
| | | struct gpiod_line *line; /* libgpiod line */ |
| | | } relay_info_t; |
| | | |
| | | /* Relay API context */ |
| | | typedef struct relay_ctx_s |
| | | { |
| | | struct gpiod_chip *chip; |
| | | relay_info_t *relay; |
| | | int count; |
| | | } relay_ctx_t; |
| | | |
| | | extern int init_relay(relay_ctx_t *ctx); |
| | | extern int term_relay(relay_ctx_t *ctx); |
| | | extern int turn_relay(int which, int cmd); |
| | | |
| | | #endif /* ----- #ifndef _RELAY_H_ ----- */ |
| | | |
| File was renamed from project/4.mqttd/booster/sht20.c |
| | |
| | | #include <linux/i2c-dev.h> |
| | | |
| | | #include "logger.h" |
| | | #include "utils.h" |
| | | #include "sht20.h" |
| | | |
| | | #define msleep(x) usleep(1000*(x)); |
| | | |
| | | int i2c_write(int fd, uint8_t slave_addr, uint8_t *data, int len); |
| | | int i2c_read(int fd, uint8_t slave_addr, uint8_t *buf, int size); |
| | |
| | | |
| | | memset(buf, 0, sizeof(buf)); |
| | | i2c_read(fd, SHT20_I2CADDR, buf, 3); |
| | | log_dump(LOG_LEVEL_DEBUG, "Temperature sample data: ", (char *)buf, 3); |
| | | log_dump(LOG_LEVEL_TRACE, "Temperature sample data: ", buf, 3); |
| | | |
| | | if( !sht20_checksum(buf, 2, buf[2]) ) |
| | | { |
| | |
| | | |
| | | memset(buf, 0, sizeof(buf)); |
| | | i2c_read(fd, SHT20_I2CADDR, buf, 3); |
| | | log_dump(LOG_LEVEL_DEBUG, "Relative humidity sample data: ", (char *)buf, 3); |
| | | log_dump(LOG_LEVEL_TRACE, "Relative humidity sample data: ", buf, 3); |
| | | |
| | | if( !sht20_checksum(buf, 2, buf[2]) ) |
| | | { |
| New file |
| | |
| | | /********************************************************************************* |
| | | * Copyright: (C) 2023 LingYun IoT System Studio |
| | | * All rights reserved. |
| | | * |
| | | * Filename: tsl2561.c |
| | | * Description: This file is the Lux sensor TSL2561 code |
| | | * |
| | | * Version: 1.0.0(10/08/23) |
| | | * Author: Guo Wenxue <guowenxue@gmail.com> |
| | | * ChangeLog: 1, Release initial version on "10/08/23 17:52:00" |
| | | * |
| | | * Pin connection: |
| | | * TSL2561 Module Raspberry Pi Board |
| | | * VCC <-----> #Pin1(3.3V) |
| | | * SDA0 <-----> #Pin27(SDA, BCM GPIO0) |
| | | * SCL0 <-----> #Pin28(SCL, BCM GPIO1) |
| | | * GND <-----> GND |
| | | * |
| | | * /boot/config.txt: |
| | | * dtoverlay=i2c0,pins_0_1 |
| | | * |
| | | ********************************************************************************/ |
| | | |
| | | #include <string.h> |
| | | #include <stdio.h> |
| | | #include <stdlib.h> |
| | | #include <unistd.h> |
| | | #include <math.h> |
| | | #include <time.h> |
| | | #include <errno.h> |
| | | #include <libgen.h> |
| | | #include <getopt.h> |
| | | #include <fcntl.h> |
| | | #include <sys/ioctl.h> |
| | | #include <linux/i2c.h> |
| | | #include <linux/i2c-dev.h> |
| | | #include <sys/types.h> |
| | | #include <sys/stat.h> |
| | | |
| | | #include "utils.h" |
| | | #include "logger.h" |
| | | #include "tsl2561.h" |
| | | |
| | | |
| | | #define CONTROL_REG 0x80 |
| | | #define REG_COUNT 4 |
| | | |
| | | #define POWER_UP 0x03 |
| | | #define POWER_DOWN 0x00 |
| | | |
| | | #define OFF 0 |
| | | #define ON 1 |
| | | |
| | | /* Register Address */ |
| | | enum |
| | | { |
| | | /* Channel_0 = DATA0HIGH<<8 + DATA0LOW */ |
| | | DATA0LOW = 0x8C, |
| | | DATA0HIGH, |
| | | |
| | | /* Channel_1 = DATA1HIGH<<8 + DATA1LOW */ |
| | | DATA1LOW, |
| | | DATA1HIGH, |
| | | }; |
| | | |
| | | static const int regs_addr[REG_COUNT]={DATA0LOW, DATA0HIGH, DATA1LOW, DATA1HIGH}; |
| | | |
| | | void tsl2561_power(int fd, int cmd) |
| | | { |
| | | struct i2c_msg msg; |
| | | struct i2c_rdwr_ioctl_data data; |
| | | unsigned char buf[2]; |
| | | |
| | | msg.addr= TSL2561_I2CADDR; |
| | | msg.flags=0; /* write */ |
| | | msg.len= 1; |
| | | msg.buf= buf; |
| | | |
| | | data.nmsgs= 1; |
| | | data.msgs= &msg; |
| | | |
| | | msg.buf[0]=CONTROL_REG; |
| | | if( ioctl(fd, I2C_RDWR, &data) < 0 ) |
| | | { |
| | | log_error("%s() ioctl failure: %s\n", __func__, strerror(errno)); |
| | | return ; |
| | | } |
| | | |
| | | |
| | | if( cmd ) |
| | | msg.buf[0]=POWER_UP; |
| | | else |
| | | msg.buf[0]=POWER_DOWN; |
| | | |
| | | if( ioctl(fd, I2C_RDWR, &data) < 0 ) |
| | | { |
| | | log_error("%s() ioctl failure: %s\n", __func__, strerror(errno)); |
| | | return ; |
| | | } |
| | | |
| | | return ; |
| | | } |
| | | |
| | | int tsl2561_readreg(int fd, unsigned char regaddr, unsigned char *regval) |
| | | { |
| | | struct i2c_msg msg; |
| | | struct i2c_rdwr_ioctl_data data; |
| | | unsigned char buf[2]; |
| | | |
| | | msg.addr= TSL2561_I2CADDR; |
| | | msg.flags=0; /* write */ |
| | | msg.len= 1; |
| | | msg.buf= buf; |
| | | msg.buf[0] = regaddr; |
| | | |
| | | data.nmsgs= 1; |
| | | data.msgs= &msg; |
| | | |
| | | if( ioctl(fd, I2C_RDWR, &data) < 0 ) |
| | | { |
| | | log_error("%s() ioctl failure: %s\n", __func__, strerror(errno)); |
| | | return -1; |
| | | } |
| | | |
| | | memset(buf, 0, sizeof(buf)); |
| | | |
| | | msg.addr= TSL2561_I2CADDR; |
| | | msg.flags=I2C_M_RD; /* read */ |
| | | msg.len= 1; |
| | | msg.buf= buf; |
| | | |
| | | data.nmsgs= 1; |
| | | data.msgs= &msg; |
| | | |
| | | if( ioctl(fd, I2C_RDWR, &data) < 0 ) |
| | | { |
| | | log_error("%s() ioctl failure: %s\n", __func__, strerror(errno)); |
| | | return -1; |
| | | } |
| | | |
| | | *regval = msg.buf[0]; |
| | | return 0; |
| | | } |
| | | |
| | | int tsl2561_get_lux(float *lux) |
| | | { |
| | | int i, fd; |
| | | int rv = 0; |
| | | char *dev = TSL2561_I2CDEV; |
| | | float div = 0.0; |
| | | |
| | | unsigned char reg_data[REG_COUNT]; |
| | | int chn0_data = 0; |
| | | int chn1_data = 0; |
| | | |
| | | if( !lux ) |
| | | { |
| | | log_error("Invalid input arguments\n"); |
| | | return -1; |
| | | } |
| | | |
| | | if( (fd=open(dev, O_RDWR)) < 0) |
| | | { |
| | | log_error("i2c device '%s' open failed: %s\n", dev, strerror(errno)); |
| | | return -2; |
| | | } |
| | | |
| | | tsl2561_power(fd, ON); |
| | | |
| | | msleep(410); /* t(CONV) MAX 400ms */ |
| | | |
| | | /* Read register Channel0 and channel1 data from register */ |
| | | for(i=0; i<REG_COUNT; i++) |
| | | { |
| | | rv = tsl2561_readreg(fd, regs_addr[i], ®_data[i]); |
| | | if( rv < 0) |
| | | goto failed; |
| | | } |
| | | |
| | | chn0_data = reg_data[1]*256 + reg_data[0]; /* Channel0 = DATA0HIGH<<8 + DATA0LOW */ |
| | | chn1_data = reg_data[3]*256 + reg_data[2]; /* channel1 = DATA1HIGH<<8 + DATA1LOW */ |
| | | |
| | | if( chn0_data<=0 || chn1_data<0 ) |
| | | { |
| | | rv = -2; |
| | | goto cleanup; |
| | | } |
| | | |
| | | div = (float)chn1_data / (float)chn0_data; |
| | | |
| | | if( div>0 && div<=0.5 ) |
| | | *lux = 0.304*chn0_data-0.062*chn0_data*pow(div,1.4); |
| | | |
| | | else if( div>0.5 && div<=0.61 ) |
| | | *lux = 0.0224*chn0_data-0.031*chn1_data; |
| | | |
| | | else if( div>0.61 && div<=0.8 ) |
| | | *lux = 0.0128*chn0_data-0.0153*chn1_data; |
| | | |
| | | else if( div>0.8 && div<=1.3 ) |
| | | *lux = 0.00146*chn0_data-0.00112*chn1_data; |
| | | |
| | | else if( div>1.3 ) |
| | | *lux = 0.0; |
| | | |
| | | cleanup: |
| | | tsl2561_power(fd, OFF); |
| | | |
| | | failed: |
| | | close(fd); |
| | | return rv; |
| | | } |
| New file |
| | |
| | | /********************************************************************************* |
| | | * Copyright: (C) 2023 LingYun IoT System Studio |
| | | * All rights reserved. |
| | | * |
| | | * Filename: tsl2561.h |
| | | * Description: This file is the Lux sensor TSL2561 code |
| | | * |
| | | * Version: 1.0.0(10/08/23) |
| | | * Author: Guo Wenxue <guowenxue@gmail.com> |
| | | * ChangeLog: 1, Release initial version on "10/08/23 17:52:00" |
| | | * |
| | | * Pin connection: |
| | | * TSL2561 Module Raspberry Pi Board |
| | | * VCC <-----> #Pin1(3.3V) |
| | | * SDA0 <-----> #Pin27(SDA, BCM GPIO0) |
| | | * SCL0 <-----> #Pin28(SCL, BCM GPIO1) |
| | | * GND <-----> GND |
| | | * |
| | | * /boot/config.txt: |
| | | * dtoverlay=i2c0,pins_0_1 |
| | | * |
| | | ********************************************************************************/ |
| | | |
| | | #ifndef _TSL2561_H_ |
| | | #define _TSL2561_H_ |
| | | |
| | | #define TSL2561_I2CDEV "/dev/i2c-0" |
| | | #define TSL2561_I2CADDR 0x39 |
| | | |
| | | extern int tsl2561_get_lux(float *lux); |
| | | |
| | | #endif /* ----- #ifndef _TSL2561_H_ ----- */ |
| | | |
| | | |