Initial implementation of MQTT
Initial implementation of MQTT alongside ethernet gateway.
This commit is contained in:
@@ -33,7 +33,9 @@ ifeq ($(PIREV),$(filter $(PIREV),a01041 a21041 0010))
|
|||||||
CXXFLAGS+=-D__PI_BPLUS
|
CXXFLAGS+=-D__PI_BPLUS
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
COMPILER_FLAGS=examples_RPi/compiler_flags
|
||||||
GATEWAY=examples_RPi/PiGateway
|
GATEWAY=examples_RPi/PiGateway
|
||||||
|
GATEWAYMQTT=examples_RPi/PiGatewayMQTT
|
||||||
GATEWAY_SOURCES=examples_RPi/PiGateway.cpp $(wildcard ./utility/*.cpp)
|
GATEWAY_SOURCES=examples_RPi/PiGateway.cpp $(wildcard ./utility/*.cpp)
|
||||||
GATEWAY_OBJECTS=$(patsubst %.cpp,%.o,$(GATEWAY_SOURCES))
|
GATEWAY_OBJECTS=$(patsubst %.cpp,%.o,$(GATEWAY_SOURCES))
|
||||||
DEPS+=$(patsubst %.cpp,%.d,$(GATEWAY_SOURCES))
|
DEPS+=$(patsubst %.cpp,%.d,$(GATEWAY_SOURCES))
|
||||||
@@ -41,24 +43,37 @@ DEPS+=$(patsubst %.cpp,%.d,$(GATEWAY_SOURCES))
|
|||||||
RF24H = /usr/local/include/RF24
|
RF24H = /usr/local/include/RF24
|
||||||
CINCLUDE=-I. -I./core -I$(RF24H)
|
CINCLUDE=-I. -I./core -I$(RF24H)
|
||||||
|
|
||||||
.PHONY: all clean install uninstall
|
.PHONY: all gateway gatewaymqtt clean install uninstall force
|
||||||
|
|
||||||
all: $(GATEWAY)
|
all: $(GATEWAY)
|
||||||
|
|
||||||
|
mqtt: $(GATEWAYMQTT)
|
||||||
|
|
||||||
|
$(COMPILER_FLAGS): force
|
||||||
|
echo '$(CXXFLAGS)' | cmp -s - $@ || echo '$(CXXFLAGS)' > $@
|
||||||
|
|
||||||
|
$(GATEWAY_OBJECTS): $(COMPILER_FLAGS)
|
||||||
|
|
||||||
# Basic Gateway Build
|
# Basic Gateway Build
|
||||||
$(GATEWAY): LDFLAGS += -pthread
|
$(GATEWAY): LDFLAGS += -pthread
|
||||||
$(GATEWAY): $(GATEWAY_OBJECTS)
|
$(GATEWAY): $(GATEWAY_OBJECTS)
|
||||||
$(CXX) $(LDFLAGS) -o $@ $(GATEWAY_OBJECTS)
|
$(CXX) $(LDFLAGS) -o $@ $(GATEWAY_OBJECTS)
|
||||||
|
|
||||||
|
# MQTT Gateway Build
|
||||||
|
$(GATEWAYMQTT): CXXFLAGS += -DMY_GATEWAY_MQTT_CLIENT
|
||||||
|
$(GATEWAYMQTT): LDFLAGS += -pthread -lmosquitto
|
||||||
|
$(GATEWAYMQTT): $(GATEWAY_OBJECTS)
|
||||||
|
$(CXX) $(LDFLAGS) -o $@ $(GATEWAY_OBJECTS)
|
||||||
|
|
||||||
# Include all .d files
|
# Include all .d files
|
||||||
-include $(DEPS)
|
-include $(DEPS)
|
||||||
|
|
||||||
%.o: %.cpp
|
%.o: %.cpp
|
||||||
$(CXX) $(CXXFLAGS) $(CINCLUDE) -MMD -c -o $@ $<
|
$(CXX) $(CXXFLAGS) $(CINCLUDE) -MMD -c -o $@ $<
|
||||||
|
|
||||||
# The Cleaner
|
# The Cleaner
|
||||||
clean:
|
clean:
|
||||||
rm -rf build $(GATEWAY_OBJECTS) $(GATEWAY) $(DEPS)
|
rm -rf build $(GATEWAY_OBJECTS) $(GATEWAY) $(GATEWAYMQTT) $(DEPS) $(COMPILER_FLAGS)
|
||||||
|
|
||||||
install: all install-gatewaybasic install-gatewayserial install-gatewayethernet install-initscripts
|
install: all install-gatewaybasic install-gatewayserial install-gatewayethernet install-initscripts
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,26 @@
|
|||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <list>
|
#include <list>
|
||||||
|
|
||||||
|
#ifdef MY_GATEWAY_MQTT_CLIENT
|
||||||
|
#include <mosquitto.h>
|
||||||
|
|
||||||
|
#ifndef MQTT_IP
|
||||||
|
#define MQTT_IP "127.0.0.1"
|
||||||
|
#endif
|
||||||
|
#ifndef MQTT_PORT
|
||||||
|
#define MQTT_PORT 1883
|
||||||
|
#endif
|
||||||
|
#ifndef MQTT_KEEPALIVE
|
||||||
|
#define MQTT_KEEPALIVE 60
|
||||||
|
#endif
|
||||||
|
#ifndef MY_MQTT_PUBLISH_TOPIC_PREFIX
|
||||||
|
#define MY_MQTT_PUBLISH_TOPIC_PREFIX "mygateway1-out"
|
||||||
|
#endif
|
||||||
|
#ifndef MY_MQTT_SUBSCRIBE_TOPIC_PREFIX
|
||||||
|
#define MY_MQTT_SUBSCRIBE_TOPIC_PREFIX "mygateway1-in"
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
//TODO
|
//TODO
|
||||||
#ifdef MY_USE_UDP
|
#ifdef MY_USE_UDP
|
||||||
#error UDP not supported for this type of gateway
|
#error UDP not supported for this type of gateway
|
||||||
@@ -49,10 +69,19 @@ void *get_in_addr(struct sockaddr *sa);
|
|||||||
void *waiting_controllers(void* thread_arg);
|
void *waiting_controllers(void* thread_arg);
|
||||||
void *connected_controller(void* thread_arg);
|
void *connected_controller(void* thread_arg);
|
||||||
|
|
||||||
|
#ifdef MY_GATEWAY_MQTT_CLIENT
|
||||||
|
void *mqtt_thread(void *);
|
||||||
|
void parsemqtt_and_send(char *topic, const char *payload);
|
||||||
|
struct mosquitto *mosq;
|
||||||
|
#endif
|
||||||
|
|
||||||
bool gatewayTransportInit() {
|
bool gatewayTransportInit() {
|
||||||
long int sockfd;
|
long int sockfd;
|
||||||
pthread_t thread_id;
|
pthread_t thread_id;
|
||||||
pthread_attr_t attr;
|
pthread_attr_t attr;
|
||||||
|
#ifdef MY_GATEWAY_MQTT_CLIENT
|
||||||
|
pthread_attr_t mqtt_attr;
|
||||||
|
#endif
|
||||||
char *ip = NULL;
|
char *ip = NULL;
|
||||||
|
|
||||||
// ignore SIGPIPE generated by send() to a disconnected client
|
// ignore SIGPIPE generated by send() to a disconnected client
|
||||||
@@ -84,6 +113,14 @@ bool gatewayTransportInit() {
|
|||||||
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
||||||
pthread_create(&thread_id, &attr, &waiting_controllers, (void *)sockfd);
|
pthread_create(&thread_id, &attr, &waiting_controllers, (void *)sockfd);
|
||||||
pthread_attr_destroy(&attr);
|
pthread_attr_destroy(&attr);
|
||||||
|
|
||||||
|
#ifdef MY_GATEWAY_MQTT_CLIENT
|
||||||
|
/* mqtt thread */
|
||||||
|
pthread_attr_init(&mqtt_attr);
|
||||||
|
pthread_attr_setdetachstate(&mqtt_attr, PTHREAD_CREATE_DETACHED);
|
||||||
|
pthread_create(&thread_id, &mqtt_attr, &mqtt_thread, NULL);
|
||||||
|
pthread_attr_destroy(&mqtt_attr);
|
||||||
|
#endif
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -98,6 +135,13 @@ bool gatewayTransportSend(MyMessage &message)
|
|||||||
if (send(controllers[i], ethernetMsg, strlen(ethernetMsg), 0) == -1)
|
if (send(controllers[i], ethernetMsg, strlen(ethernetMsg), 0) == -1)
|
||||||
perror("send");
|
perror("send");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef MY_GATEWAY_MQTT_CLIENT
|
||||||
|
char *mqttMsg = protocolFormatMQTTTopic(MY_MQTT_PUBLISH_TOPIC_PREFIX, message);
|
||||||
|
//connection, message id, topic, bytes, data, QOS, retain
|
||||||
|
mosquitto_publish(mosq, NULL, mqttMsg, strlen(message.getString(_convBuffer)), message.getString(_convBuffer), 0, false);
|
||||||
|
#endif
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -302,3 +346,177 @@ void *connected_controller(void* thread_arg)
|
|||||||
close(sockfd);
|
close(sockfd);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef MY_GATEWAY_MQTT_CLIENT
|
||||||
|
void mqtt_message_callback(struct mosquitto *mosq, void *userdata, const struct mosquitto_message *message)
|
||||||
|
{
|
||||||
|
(void)mosq;
|
||||||
|
(void)userdata;
|
||||||
|
|
||||||
|
if(message->payloadlen){
|
||||||
|
printf("Got a MQTT message %s %s\n", message->topic, (char*)message->payload);
|
||||||
|
|
||||||
|
std::string strpayload((char *)message->payload);
|
||||||
|
strpayload = strpayload.substr(0,(int)message->payloadlen);
|
||||||
|
|
||||||
|
parsemqtt_and_send(message->topic, strpayload.c_str());
|
||||||
|
}else{
|
||||||
|
printf("%s (null)\n", message->topic);
|
||||||
|
}
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void mqtt_connect_callback(struct mosquitto *mosq, void *userdata, int result)
|
||||||
|
{
|
||||||
|
(void)mosq;
|
||||||
|
(void)userdata;
|
||||||
|
|
||||||
|
if(!result){
|
||||||
|
printf("Connected!\n");
|
||||||
|
/* Subscribe to broker information topics on successful connect. */
|
||||||
|
//connection, message id, topic, qos
|
||||||
|
mosquitto_subscribe(mosq, NULL, protocolFormatMQTTSubscribe(MY_MQTT_SUBSCRIBE_TOPIC_PREFIX), 0);
|
||||||
|
}else{
|
||||||
|
fprintf(stderr, "Connect failed\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void mqtt_subscribe_callback(struct mosquitto *mosq, void *userdata, int mid, int qos_count, const int *granted_qos)
|
||||||
|
{
|
||||||
|
(void)mosq;
|
||||||
|
(void)userdata;
|
||||||
|
|
||||||
|
int i;
|
||||||
|
|
||||||
|
printf("Subscribed (mid: %d): %d", mid, granted_qos[0]);
|
||||||
|
for(i=1; i<qos_count; i++){
|
||||||
|
printf(", %d", granted_qos[i]);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void mqtt_log_callback(struct mosquitto *mosq, void *userdata, int level, const char *str)
|
||||||
|
{
|
||||||
|
(void)mosq;
|
||||||
|
(void)userdata;
|
||||||
|
(void)level;
|
||||||
|
/* Pring all log messages regardless of level. */
|
||||||
|
printf("Log: %s\n", str);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *mqtt_thread(void *)
|
||||||
|
{
|
||||||
|
mosquitto_lib_init();
|
||||||
|
bool clean_session = true; //will clean out subscriptions on disconnect
|
||||||
|
mosq = mosquitto_new(NULL, clean_session, NULL);
|
||||||
|
if(!mosq){
|
||||||
|
fprintf(stderr, "Error: Out of memory.\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
mosquitto_log_callback_set(mosq, mqtt_log_callback);
|
||||||
|
mosquitto_connect_callback_set(mosq, mqtt_connect_callback);
|
||||||
|
mosquitto_message_callback_set(mosq, mqtt_message_callback);
|
||||||
|
mosquitto_subscribe_callback_set(mosq, mqtt_subscribe_callback);
|
||||||
|
|
||||||
|
if(mosquitto_connect(mosq, MQTT_IP, MQTT_PORT, MQTT_KEEPALIVE)){
|
||||||
|
fprintf(stderr, "Unable to connect.\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
mosquitto_loop_forever(mosq, -1, 1);
|
||||||
|
|
||||||
|
mosquitto_destroy(mosq);
|
||||||
|
mosquitto_lib_cleanup();
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void parsemqtt_and_send(char *topic, const char *payload)
|
||||||
|
{
|
||||||
|
//Buffer for parsemqtt_and_send. Do not want to re-create it every time we get a message
|
||||||
|
MyMessage msg;
|
||||||
|
char *str, *p;
|
||||||
|
|
||||||
|
char* topic_copy = strdup(topic);
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: Check if we should send ack or not.
|
||||||
|
int i = 0;
|
||||||
|
for (str = strtok_r(topic_copy,"/",&p) ; str && i<4 ; str = strtok_r(NULL,"/",&p))
|
||||||
|
{
|
||||||
|
// Example: /mySensors/105/4/V_LIGHT
|
||||||
|
// 0: mySensors - Prefix
|
||||||
|
// 1: 105 - node
|
||||||
|
// 2: 4 - sensor
|
||||||
|
// 3: V_LIGHT - type
|
||||||
|
if (i == 0) {
|
||||||
|
//TODO: Add warning when we receive a message from an unexpected broker prefix.
|
||||||
|
//if (strcmp_P(str,broker)!=0) { //look for MQTT_BROKER_PREFIX
|
||||||
|
// return; //Message not for us or malformatted!
|
||||||
|
//}
|
||||||
|
} else if (i==1) {
|
||||||
|
printf("Destination parse %s\n", str);
|
||||||
|
msg.destination = atoi(str); //NodeID
|
||||||
|
} else if (i==2) {
|
||||||
|
msg.sensor = atoi(str); //SensorID
|
||||||
|
} else if (i==3) {
|
||||||
|
unsigned char match=255; //SensorType
|
||||||
|
|
||||||
|
//Support for numeric types
|
||||||
|
if ( atoi(str)!=0 || (str[0]=='0' && str[1] =='\0') ) {
|
||||||
|
match=atoi(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
//Check through all the data type string attary to find a match.
|
||||||
|
//? Not sure if there is any need to be checking for other types like sensor and internal
|
||||||
|
//? as those don't seem like they should be outside settable thought MQTT.
|
||||||
|
for (uint8_t j=0; match == 255 && j<sizeof(mysensor_data_str)/sizeof(mysensor_data_str[0]); j++) {
|
||||||
|
if (strcmp(str,mysensor_data_str[j])==0) { //compare sensors
|
||||||
|
match=j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
msg.type = match;
|
||||||
|
} else if (i==4) {
|
||||||
|
mSetAck(msg, atoi(str)); //Ack
|
||||||
|
}
|
||||||
|
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.sender = GATEWAY_ADDRESS;
|
||||||
|
msg.last = GATEWAY_ADDRESS;
|
||||||
|
mSetCommand(msg, C_SET);
|
||||||
|
mSetRequestAck(msg, 0);
|
||||||
|
msg.set(payload);
|
||||||
|
|
||||||
|
if(msg.destination != 0 && msg.sensor != 0 && msg.type != 255)
|
||||||
|
{
|
||||||
|
ethernetMsg_q.push_back(msg);
|
||||||
|
printf("Message not sent!\n");
|
||||||
|
|
||||||
|
// Forward the data to Ethernet
|
||||||
|
// Likely this is a duplicate from a C_SET that we received and published
|
||||||
|
// in msg_callback. Not much we can do to avoid the duplicate as there is
|
||||||
|
// no way to tell if we performed the publish that is triggering this callback.
|
||||||
|
char *ethernetMsg = protocolFormat(msg);
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < MY_GATEWAY_MAX_CLIENTS; i++) {
|
||||||
|
if (controllers[i] == -1)
|
||||||
|
continue;
|
||||||
|
if (send(controllers[i], ethernetMsg, strlen(ethernetMsg), 0) == -1)
|
||||||
|
perror("send");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("Recieved a bad MQTT message: '%s':'%s'\n destination:%i, sensor:%i, type:%i\n",
|
||||||
|
topic, payload, msg.destination, msg.sensor, msg.type);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(topic_copy);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -39,6 +39,19 @@
|
|||||||
// How many clients should be able to connect to this gateway (default 1)
|
// How many clients should be able to connect to this gateway (default 1)
|
||||||
#define MY_GATEWAY_MAX_CLIENTS 2
|
#define MY_GATEWAY_MAX_CLIENTS 2
|
||||||
|
|
||||||
|
/* MQTT BUILD */
|
||||||
|
#ifdef MY_GATEWAY_MQTT_CLIENT
|
||||||
|
//Linux MQTT Defines - Uncomment and modify as needed. Written as defaults.
|
||||||
|
//#define MQTT_IP "127.0.0.1"
|
||||||
|
//#define MQTT_PORT 1883
|
||||||
|
//#define MQTT_KEEPALIVE 60
|
||||||
|
// Enable these if your MQTT broker requires usenrame/password
|
||||||
|
//#define MY_MQTT_USER "username"
|
||||||
|
//#define MY_MQTT_PASSWORD "password"
|
||||||
|
//#define MY_MQTT_PUBLISH_TOPIC_PREFIX "mygateway1-out"
|
||||||
|
//#define MY_MQTT_SUBSCRIBE_TOPIC_PREFIX "mygateway1-in"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <MySensor.h>
|
#include <MySensor.h>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|||||||
Reference in New Issue
Block a user