Inicializacny commit
This commit is contained in:
38
Makefile
Normal file
38
Makefile
Normal file
@@ -0,0 +1,38 @@
|
||||
VERSION = 0.0.7
|
||||
# Global target; when 'make' is run without arguments, this is what it should do
|
||||
all: bell-mqtt-recv
|
||||
|
||||
# These variables hold the name of the compilation tool, the compilation flags and the link flags
|
||||
# We make use of these variables in the package manifest
|
||||
CC = gcc
|
||||
CFLAGS = -Wall
|
||||
LDFLAGS += -lmosquitto
|
||||
|
||||
# This variable identifies all header files in the directory; we use it to create a dependency chain between the object files and the source files
|
||||
# This approach will re-build your application whenever any header file changes. In a more complex application, such behavior is often undesirable
|
||||
DEPS = $(wildcard *.h)
|
||||
|
||||
# This variable holds all source files to consider for the build; we use a wildcard to pick all files
|
||||
SRC = $(wildcard *.c)
|
||||
|
||||
# This variable holds all object file names, constructed from the source file names using pattern substitution
|
||||
OBJ = $(patsubst %.c, %.o, $(SRC))
|
||||
|
||||
# This rule builds individual object files, and depends on the corresponding C source files and the header files
|
||||
%.o: %.c $(DEPS)
|
||||
$(CC) -c -o $@ $< $(CFLAGS)
|
||||
|
||||
# To build 'helloworld', we depend on the object files, and link them all into a single executable using the compilation tool
|
||||
# We use automatic variables to specify the final executable name 'helloworld', using '$@' and the '$^' will hold the names of all the
|
||||
# dependencies of this rule
|
||||
bell-mqtt-recv: $(OBJ)
|
||||
$(CC) -o $@ $^ $(LDFLAGS)
|
||||
|
||||
# To clean build artifacts, we specify a 'clean' rule, and use PHONY to indicate that this rule never matches with a potential file in the directory
|
||||
.PHONY: clean
|
||||
|
||||
clean:
|
||||
rm bell-mqtt-recv -f *.o
|
||||
|
||||
pack:
|
||||
tar cvzf ../bell-rspro-$(VERSION).tar.gz $(DEPS) $(SRC) Makefile bell-rspro.init
|
||||
308
bell-mqtt.c
Normal file
308
bell-mqtt.c
Normal file
@@ -0,0 +1,308 @@
|
||||
/**************************************************************/
|
||||
/* Multicast listener (server) */
|
||||
/* */
|
||||
/* Activation using: {program name} {Multicast IP} {port} */
|
||||
/* {program name} - This program name */
|
||||
/* {Multicast IP} - The IP address to listen to (Class D) */
|
||||
/* {port} - The port hnumber to listen on */
|
||||
/* {gpio} - gpio pin number for output */
|
||||
/* */
|
||||
/* */
|
||||
/* */
|
||||
/* */
|
||||
/* */
|
||||
/* */
|
||||
/* */
|
||||
/* */
|
||||
/* This is free software released under the GPL license. */
|
||||
/* See the GNU GPL for details. */
|
||||
/* */
|
||||
/* (c) Juan-Mariano de Goyeneche. 1998, 1999. */
|
||||
/**************************************************************/
|
||||
|
||||
|
||||
#include <stdio.h> /* printf(), snprintf() */
|
||||
#include <stdlib.h> /* strtol(), exit() */
|
||||
#include <sys/types.h>
|
||||
#include <errno.h> /* perror() */
|
||||
#include <unistd.h> /* fork(), sleep() */
|
||||
#include <sys/utsname.h> /* uname() */
|
||||
#include <string.h> /* memset() */
|
||||
#include <syslog.h> /* openlog(), syslog() */
|
||||
#include "gpiosysfs.h"
|
||||
#include <signal.h> //signal(3)
|
||||
#include <sys/stat.h> //umask(3)
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <assert.h>
|
||||
#include <err.h>
|
||||
|
||||
#include "mosquitto.h"
|
||||
|
||||
#define DEBUG
|
||||
#define MAXLEN 1024
|
||||
|
||||
#ifdef DEBUG
|
||||
#define LOG(...) do { printf(__VA_ARGS__); } while (0)
|
||||
#else
|
||||
#define LOG(...)
|
||||
#endif
|
||||
|
||||
/* How many seconds the broker should wait between sending out
|
||||
* keep-alive messages. */
|
||||
#define KEEPALIVE_SECONDS 60
|
||||
|
||||
/* Hostname and port for the MQTT broker. */
|
||||
char *mqtt_host;
|
||||
int port;
|
||||
char *topic;
|
||||
char *pin;
|
||||
int timeout;
|
||||
bool debug=false;
|
||||
|
||||
int daemonize(char* name, char* path, char* outfile, char* errfile, char* infile , char* pidfile)
|
||||
{
|
||||
char filename[64];
|
||||
FILE *pf;
|
||||
|
||||
if(!path) { path="/"; }
|
||||
if(!name) { name="medaemon"; }
|
||||
if(!infile) { infile="/dev/null"; }
|
||||
if(!outfile) { outfile="/dev/null"; }
|
||||
if(!errfile) { errfile="/dev/null"; }
|
||||
if(!pidfile) {
|
||||
snprintf(filename,63,"/var/run/%s.pid",name);
|
||||
pidfile = filename;
|
||||
}
|
||||
|
||||
//printf("%s %s %s %s\n",name,path,outfile,infile);
|
||||
pid_t child;
|
||||
//fork, detach from process group leader
|
||||
if( (child=fork())<0 ) { //failed fork
|
||||
fprintf(stderr,"error: failed fork\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (child>0) { //parent
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
if( setsid()<0 ) { //failed to become session leader
|
||||
fprintf(stderr,"error: failed setsid\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
//catch/ignore signals
|
||||
signal(SIGCHLD,SIG_IGN);
|
||||
signal(SIGHUP,SIG_IGN);
|
||||
|
||||
//fork second time
|
||||
if ( (child=fork())<0) { //failed fork
|
||||
fprintf(stderr,"error: failed fork\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if( child>0 ) { //parent
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
//new file permissions
|
||||
umask(0);
|
||||
//change to path directory
|
||||
chdir(path);
|
||||
|
||||
// open pid file
|
||||
pf = fopen(pidfile,"w+");
|
||||
if (pf == NULL) {
|
||||
fprintf(stderr,"error: failed open pid file\n");
|
||||
exit(EXIT_FAILURE);
|
||||
} else {
|
||||
fprintf(pf,"%d",getpid());
|
||||
fclose(pf);
|
||||
}
|
||||
|
||||
//Close all open file descriptors
|
||||
int fd;
|
||||
for( fd=sysconf(_SC_OPEN_MAX); fd>0; --fd )
|
||||
{
|
||||
close(fd);
|
||||
}
|
||||
|
||||
//reopen stdin, stdout, stderr
|
||||
freopen(infile,"r",stdin); //fd=0
|
||||
freopen(outfile,"w+",stdout); //fd=1
|
||||
freopen(errfile,"w+",stderr); //fd=2
|
||||
|
||||
//open syslog
|
||||
openlog(name,LOG_PID,LOG_DAEMON);
|
||||
|
||||
syslog (LOG_DAEMON, "Program started by User %d", getuid ());
|
||||
return(0);
|
||||
}
|
||||
|
||||
void beep()
|
||||
{
|
||||
gpioWrite(pin,"1");
|
||||
msleep(timeout);
|
||||
gpioWrite(pin,"0");
|
||||
msleep(timeout);
|
||||
}
|
||||
|
||||
struct client_info {
|
||||
struct mosquitto *m;
|
||||
pid_t pid;
|
||||
uint32_t tick_ct;
|
||||
};
|
||||
|
||||
static void die(const char *msg);
|
||||
static struct mosquitto *init(struct client_info *info);
|
||||
static bool set_callbacks(struct mosquitto *m);
|
||||
static bool connect(struct mosquitto *m);
|
||||
static int run_loop(struct client_info *info);
|
||||
|
||||
/* Fail with an error message. */
|
||||
static void die(const char *msg) {
|
||||
fprintf(stderr, "%s", msg);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Initialize a mosquitto client. */
|
||||
static struct mosquitto *init(struct client_info *info) {
|
||||
void *udata = (void *)info;
|
||||
size_t buf_sz = 32;
|
||||
char buf[buf_sz];
|
||||
if (buf_sz < snprintf(buf, buf_sz, "bellrecv_%d", info->pid)) {
|
||||
return NULL; /* snprintf buffer failure */
|
||||
}
|
||||
/* Create a new mosquitto client, with the name "client_#{PID}". */
|
||||
LOG("Init\n");
|
||||
mosquitto_lib_init();
|
||||
struct mosquitto *m = mosquitto_new(buf, true, udata);
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
/* Callback for successful connection: add subscriptions. */
|
||||
static void on_connect(struct mosquitto *m, void *udata, int res) {
|
||||
if (res == 0) { /* success */
|
||||
LOG("connecion successfull\n");
|
||||
|
||||
//struct client_info *info = (struct client_info *)udata;
|
||||
mosquitto_subscribe(m, NULL, topic, 0);
|
||||
} else {
|
||||
die("connection refused\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* A message was successfully published. */
|
||||
static void on_publish(struct mosquitto *m, void *udata, int m_id) {
|
||||
LOG("-- published successfully\n");
|
||||
}
|
||||
|
||||
static bool match(const char *topic, const char *key) {
|
||||
return 0 == strncmp(topic, key, strlen(key));
|
||||
}
|
||||
|
||||
/* Handle a message that just arrived via one of the subscriptions. */
|
||||
static void on_message(struct mosquitto *m, void *udata,
|
||||
const struct mosquitto_message *msg) {
|
||||
if (msg == NULL) {
|
||||
LOG("-- message NULL\n");
|
||||
return;
|
||||
}
|
||||
LOG("-- got message @ %s: (%d, QoS %d, %s) '%s'\n",
|
||||
msg->topic, msg->payloadlen, msg->qos, msg->retain ? "R" : "!r",
|
||||
(char *)msg->payload);
|
||||
|
||||
struct client_info *info = (struct client_info *)udata;
|
||||
|
||||
syslog(LOG_DAEMON, "Bell message recived: '%s' -> '%s'", msg->topic, (char *)msg->payload);
|
||||
if (match(msg->topic, topic)) {
|
||||
if (0 == strncmp(msg->payload, "BELL:", 5)) {
|
||||
LOG("%s %d\n",(char *)msg->payload, info->pid);
|
||||
syslog (LOG_DAEMON, "Bell ringing by message: %s",(char *)msg->payload);
|
||||
if (debug == false) beep();
|
||||
} else {
|
||||
LOG("invalid message: '%s' excepted 'BELL:'\n",(char *)msg->payload);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Successful subscription hook. */
|
||||
static void on_subscribe(struct mosquitto *m, void *udata, int mid,
|
||||
int qos_count, const int *granted_qos) {
|
||||
LOG("-- subscribed successfully on topic: '%s'\n",topic);
|
||||
syslog (LOG_DAEMON, "Bell subscibed on topic %s" ,topic);
|
||||
}
|
||||
|
||||
/* Register the callbacks that the mosquitto connection will use. */
|
||||
static bool set_callbacks(struct mosquitto *m) {
|
||||
mosquitto_connect_callback_set(m, on_connect);
|
||||
mosquitto_publish_callback_set(m, on_publish);
|
||||
mosquitto_subscribe_callback_set(m, on_subscribe);
|
||||
mosquitto_message_callback_set(m, on_message);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Connect to the network. */
|
||||
static bool connect(struct mosquitto *m) {
|
||||
int res = mosquitto_connect(m, mqtt_host, port, KEEPALIVE_SECONDS);
|
||||
return res == MOSQ_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
/* Loop until it is explicitly halted or the network is lost, then clean up. */
|
||||
static int run_loop(struct client_info *info) {
|
||||
int res = mosquitto_loop_forever(info->m, 1000, 1000 /* unused */);
|
||||
|
||||
mosquitto_destroy(info->m);
|
||||
(void)mosquitto_lib_cleanup();
|
||||
|
||||
closelog();
|
||||
if (res == MOSQ_ERR_SUCCESS) {
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
int res;
|
||||
|
||||
if (argc < 6) {
|
||||
fprintf(stderr, "Usage: %s mqtt_host port topic gpio seconds [debug]\n", argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
mqtt_host = argv[1];
|
||||
port = atoi(argv[2]);
|
||||
topic = argv[3];
|
||||
pin = argv[4];
|
||||
timeout = atoi(argv[5])*1000; //To milis
|
||||
|
||||
if (argc == 6) {
|
||||
|
||||
if( (res=daemonize("bellring","/tmp",NULL,NULL,NULL,NULL)) != 0 ) {
|
||||
fprintf(stderr,"error: daemonize failed\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
} else { debug = true ;};
|
||||
|
||||
if (debug == false) gpioSetup(pin);
|
||||
|
||||
pid_t pid = getpid();
|
||||
|
||||
struct client_info info;
|
||||
memset(&info, 0, sizeof(info));
|
||||
info.pid = pid;
|
||||
|
||||
struct mosquitto *m = init(&info);
|
||||
if (m == NULL) { die("init() failure\n"); }
|
||||
info.m = m;
|
||||
|
||||
if (!set_callbacks(m)) { die("set_callbacks() failure\n"); }
|
||||
|
||||
if (!connect(m)) { die("connect() failure\n"); }
|
||||
|
||||
return run_loop(&info);
|
||||
}
|
||||
|
||||
22
bell-rspro.init
Normal file
22
bell-rspro.init
Normal file
@@ -0,0 +1,22 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
# "new(er)" style init script
|
||||
# Look at /lib/functions/service.sh on a running system for explanations of what other SERVICE_
|
||||
# options you can use, and when you might want them.
|
||||
|
||||
START=80
|
||||
APP=bell-mqtt-recv
|
||||
SERVICE_WRITE_PID=1
|
||||
SERVICE_DAEMONIZE=1
|
||||
HOST=172.16.1.254
|
||||
PORT=1883
|
||||
TOPIC=home/bell/ring
|
||||
GPIO=7
|
||||
SECONDS=1
|
||||
|
||||
start() {
|
||||
service_start /usr/sbin/$APP $HOST $PORT $TOPIC $GPIO $SECONDS
|
||||
}
|
||||
|
||||
stop() {
|
||||
service_stop /usr/sbin/$APP
|
||||
}
|
||||
85
gpiosysfs.c
Normal file
85
gpiosysfs.c
Normal file
@@ -0,0 +1,85 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h> /* printf(), snprintf() */
|
||||
#include <stdlib.h> /* strtol(), exit() */
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
#include "gpiosysfs.h"
|
||||
|
||||
static void writeToFile(const char *absoluteFileName, const char *contents) {
|
||||
int fd = open(absoluteFileName, O_WRONLY);
|
||||
|
||||
if (-1 == fd) {
|
||||
fprintf(stderr, "Couldn't open %s for writing!\n", absoluteFileName);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int contentsLength = strlen(contents);
|
||||
|
||||
if (write(fd, contents, contentsLength) != contentsLength) {
|
||||
fprintf(stderr, "Failed to write entire value %s to %s!\n", contents, absoluteFileName);
|
||||
close(fd);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
close(fd);
|
||||
}
|
||||
|
||||
void gpioSetup(const char *pin) {
|
||||
// TODO only export if not already exported...
|
||||
if (access("/sys/class/gpio/unexport",W_OK) == 0)
|
||||
writeToFile("/sys/class/gpio/unexport", pin);
|
||||
writeToFile("/sys/class/gpio/export", pin);
|
||||
|
||||
char buf[33];
|
||||
struct stat statBuf;
|
||||
int pinExported = -1;
|
||||
|
||||
sprintf(buf, "/sys/class/gpio/gpio%s/direction", pin);
|
||||
|
||||
// May have to briefly wait for OS to make symlink!
|
||||
while (pinExported != 0) {
|
||||
msleep(1000);
|
||||
pinExported = stat(buf, &statBuf);
|
||||
}
|
||||
|
||||
writeToFile(buf, "out");
|
||||
}
|
||||
|
||||
void gpioCleanup(const char *pin) {
|
||||
writeToFile("/sys/class/gpio/unexport", pin);
|
||||
}
|
||||
|
||||
void gpioWrite(const char *pin, const char *val) {
|
||||
char buf[29];
|
||||
sprintf(buf, "/sys/class/gpio/gpio%s/value", pin);
|
||||
writeToFile(buf, val);
|
||||
}
|
||||
|
||||
/* msleep(): Sleep for the requested number of milliseconds. */
|
||||
int msleep(long msec)
|
||||
{
|
||||
struct timespec ts;
|
||||
int res;
|
||||
|
||||
if (msec < 0)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
ts.tv_sec = msec / 1000;
|
||||
ts.tv_nsec = (msec % 1000) * 1000000;
|
||||
|
||||
do {
|
||||
res = nanosleep(&ts, &ts);
|
||||
} while (res && errno == EINTR);
|
||||
|
||||
return res;
|
||||
}
|
||||
13
gpiosysfs.h
Normal file
13
gpiosysfs.h
Normal file
@@ -0,0 +1,13 @@
|
||||
/* gpiosysfs.h */
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
void gpioSetup(const char *pin);
|
||||
void gpioCleanup(const char *pin);
|
||||
void gpioWrite(const char *pin, const char *val);
|
||||
int msleep(long msec);
|
||||
Reference in New Issue
Block a user