From 6b756335d0d57a7f6150e86cfb65c6bff805cf4a Mon Sep 17 00:00:00 2001 From: Marcelo Aquino Date: Mon, 8 Aug 2016 01:46:15 -0300 Subject: [PATCH] Refactor RaspberryPi port --- Makefile | 63 +++++ MyConfig.h | 25 +- MySensors.h | 69 ++---- configure | 322 ++++++++++++++++++++++++++ core/MyGatewayTransportEthernet.cpp | 79 ++++--- core/MyGatewayTransportMQTTClient.cpp | 101 +------- core/MyHwATMega328.h | 8 +- core/MyHwESP8266.h | 8 +- core/MyHwLinuxGeneric.cpp | 190 +++++++++++++++ core/MyHwLinuxGeneric.h | 75 ++++++ core/MyMainLinux.cpp | 39 ++++ core/MyMessage.cpp | 4 + core/MyMessage.h | 1 - core/MyProtocolMySensors.cpp | 69 +++--- core/MySensorsCore.cpp | 12 +- core/MyTransport.cpp | 4 +- core/MyTransportNRF24.cpp | 8 +- core/MyTransportRS485.cpp | 5 - drivers/Linux/Arduino.h | 212 +++++++++++++++++ drivers/Linux/Client.h | 46 ++++ drivers/Linux/EthernetClient.cpp | 253 ++++++++++++++++++++ drivers/Linux/EthernetClient.h | 80 +++++++ drivers/Linux/EthernetServer.cpp | 241 +++++++++++++++++++ drivers/Linux/EthernetServer.h | 64 +++++ drivers/Linux/IPAddress.cpp | 105 +++++++++ drivers/Linux/IPAddress.h | 87 +++++++ drivers/Linux/Server.h | 28 +++ drivers/Linux/Stream.h | 15 ++ drivers/RF24/RF24_RPi.cpp | 210 +++++++++++++++++ examples_RPi/MySGateway.cpp | 93 ++++++++ initscripts/mysgateway.systemd | 9 + initscripts/mysgateway.sysvinit | 159 +++++++++++++ 32 files changed, 2460 insertions(+), 224 deletions(-) create mode 100644 Makefile create mode 100755 configure create mode 100644 core/MyHwLinuxGeneric.cpp create mode 100644 core/MyHwLinuxGeneric.h create mode 100644 core/MyMainLinux.cpp create mode 100644 drivers/Linux/Arduino.h create mode 100644 drivers/Linux/Client.h create mode 100644 drivers/Linux/EthernetClient.cpp create mode 100644 drivers/Linux/EthernetClient.h create mode 100644 drivers/Linux/EthernetServer.cpp create mode 100644 drivers/Linux/EthernetServer.h create mode 100644 drivers/Linux/IPAddress.cpp create mode 100644 drivers/Linux/IPAddress.h create mode 100644 drivers/Linux/Server.h create mode 100644 drivers/Linux/Stream.h create mode 100644 drivers/RF24/RF24_RPi.cpp create mode 100644 examples_RPi/MySGateway.cpp create mode 100644 initscripts/mysgateway.systemd create mode 100644 initscripts/mysgateway.sysvinit diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..825d83d --- /dev/null +++ b/Makefile @@ -0,0 +1,63 @@ +############################################################################# +# +# Makefile for MySensors +# +# Description: +# ------------ +# use make all and make install to install the gateway +# + +CONFIG_FILE=Makefile.inc + +include $(CONFIG_FILE) + +GATEWAY=examples_RPi/MySGateway +GATEWAY_SOURCES=examples_RPi/MySGateway.cpp +GATEWAY_OBJECTS=$(patsubst %.cpp,%.o,$(GATEWAY_SOURCES)) +DEPS+=$(patsubst %.cpp,%.d,$(GATEWAY_SOURCES)) + +CINCLUDE=-I. -I./core -I./drivers/Linux -I$(RF24H_LIB_DIR) + +.PHONY: all gateway cleanconfig clean install uninstall force + +all: $(GATEWAY) + +# Gateway Build +$(GATEWAY): $(GATEWAY_OBJECTS) + $(CXX) $(LDFLAGS) -o $@ $(GATEWAY_OBJECTS) + +# Include all .d files +-include $(DEPS) + +%.o: %.cpp + $(CXX) $(CXXFLAGS) $(CINCLUDE) -MMD -c -o $@ $< + +# clear configuration files +cleanconfig: + @echo "[Cleaning configuration]" + rm -rf $(CONFIG_FILE) + +# clear build files +clean: + @echo "[Cleaning]" + rm -rf build $(GATEWAY_OBJECTS) $(GATEWAY) $(DEPS) + +$(CONFIG_FILE): + @echo "[Running configure]" + @./configure --no-clean + +install: all install-gateway install-initscripts + +install-gateway: + @echo "Installing $(GATEWAY) to $(GATEWAY_DIR)" + @install -m 0755 $(GATEWAY) $(GATEWAY_DIR) + +install-initscripts: + if [[ `systemctl` =~ -\.mount ]]; then \ + install -m0644 initscripts/mysgateway.systemd /etc/systemd/system/mysgateway.service && \ + sed -i -e "s|%gateway_dir%|${GATEWAY_DIR}|g" /etc/systemd/system/mysgateway.service && \ + systemctl daemon-reload; \ + elif [[ -f /etc/init.d/cron && ! -h /etc/init.d/cron ]]; then \ + install -m0755 initscripts/mysgateway.sysvinit /etc/init.d/mysgateway && \ + sed -i -e "s|%gateway_dir%|${GATEWAY_DIR}|g" /etc/init.d/mysgateway; \ + fi diff --git a/MyConfig.h b/MyConfig.h index cb775ce..5d4cec8 100644 --- a/MyConfig.h +++ b/MyConfig.h @@ -413,7 +413,7 @@ #elif defined(RASPBERRYPI_ARCH) #include #include - #ifdef __PI_BPLUS + #ifdef __RPI_BPLUS #define MY_RF24_CE_PIN RPI_BPLUS_GPIO_J8_22 #define MY_RF24_CS_PIN RPI_BPLUS_GPIO_J8_24 #else @@ -716,25 +716,34 @@ #endif /************************************** -* RaspberryPi Settings +* Linux Settings ***************************************/ /** - * @def MY_RASPBERRYPI_TTY_NAME + * @def MY_LINUX_TTY_NAME * @brief Set the name of predictable tty */ -#ifndef MY_RASPBERRYPI_TTY_NAME -#define MY_RASPBERRYPI_TTY_NAME /dev/ttyMySensorsGateway +#ifndef MY_LINUX_TTY_NAME +#define MY_LINUX_TTY_NAME "/dev/ttyMySensorsGateway" #endif /** - * @def MY_RASPBERRYPI_TTY_GROUPNAME + * @def MY_LINUX_TTY_GROUPNAME * @brief Set the group name for the raw tty */ -#ifndef MY_RASPBERRYPI_TTY_GROUPNAME -#define MY_RASPBERRYPI_TTY_GROUPNAME tty +#ifndef MY_LINUX_TTY_GROUPNAME +#define MY_LINUX_TTY_GROUPNAME tty #endif +/** + * @def MY_LINUX_CONFIG_FILE + * @brief Set the filepath for the gateway config file + * + * For now the configuration file is only used to store the emulated eeprom state + */ + #ifndef MY_LINUX_CONFIG_FILE + #define MY_LINUX_CONFIG_FILE "/etc/MySensorGateway.cfg" + #endif // Doxygen specific constructs, not included when built normally // This is used to enable disabled macros/definitions to be included in the documentation as well. diff --git a/MySensors.h b/MySensors.h index d849874..05d145f 100644 --- a/MySensors.h +++ b/MySensors.h @@ -52,6 +52,10 @@ #define MY_NODE_TYPE "NODE" #endif +#if defined(MY_GATEWAY_RASPBERRYPI) + #define MY_GATEWAY_LINUX +#endif + // Enable radio "feature" if one of the radio types was enabled #if defined(MY_RADIO_NRF24) || defined(MY_RADIO_RFM69) || defined(MY_RS485) #define MY_RADIO_FEATURE @@ -68,7 +72,7 @@ #elif defined(ARDUINO_ARCH_AVR) #include "core/MyHwATMega328.cpp" #elif defined(ARDUINO_ARCH_SAMD) - #include "core/MyHwSAMD.cpp" + #include "core/MyHwSAMD.cpp" #elif defined(LINUX_ARCH_GENERIC) // Remove PSTR macros from debug prints #undef PSTR @@ -80,6 +84,7 @@ #define snprintf_P(...) snprintf( __VA_ARGS__ ) #define memcpy_P memcpy #define pgm_read_dword(x) (*x) + #define pgm_read_byte_near(x) (*x) #include "core/MyHwLinuxGeneric.cpp" #endif @@ -197,7 +202,7 @@ #define MY_REPEATER_FEATURE #endif // GATEWAY - COMMON FUNCTIONS - // We only support MQTT Client using W5100 and ESP8266 at the moment + // We support MQTT Client using W5100, ESP8266 and Linux #if !(defined(MY_CONTROLLER_URL_ADDRESS) || defined(MY_CONTROLLER_IP_ADDRESS)) #error You must specify MY_CONTROLLER_IP_ADDRESS or MY_CONTROLLER_URL_ADDRESS #endif @@ -208,18 +213,25 @@ #if !defined(MY_MQTT_SUBSCRIBE_TOPIC_PREFIX) #error You must specify a topic subscribe prefix MY_MQTT_SUBSCRIBE_TOPIC_PREFIX for this MQTT client #endif - #if !defined(MY_MQTT_CLIENT_ID) + + #if !defined(MY_MQTT_CLIENT_ID) #error You must define a unique MY_MQTT_CLIENT_ID for this MQTT client #endif - #include "drivers/PubSubClient/PubSubClient.cpp" #include "core/MyGatewayTransport.cpp" + #include "core/MyProtocolMySensors.cpp" + + #if defined(MY_GATEWAY_LINUX) + #include "drivers/Linux/EthernetClient.cpp" + #include "drivers/Linux/EthernetServer.cpp" + #include "drivers/Linux/IPAddress.cpp" + #endif + #include "drivers/PubSubClient/PubSubClient.cpp" #include "core/MyGatewayTransportMQTTClient.cpp" #elif defined(MY_GATEWAY_FEATURE) // GATEWAY - COMMON FUNCTIONS #include "core/MyGatewayTransport.cpp" - // We currently only support one protocol at the moment, enable it. #include "core/MyProtocolMySensors.cpp" // GATEWAY - CONFIGURATION @@ -227,9 +239,6 @@ // We assume that a gateway having a radio also should act as repeater #define MY_REPEATER_FEATURE #endif - #if defined(MY_GATEWAY_RASPBERRYPI) - #define MY_GATEWAY_LINUX - #endif #if defined(MY_CONTROLLER_IP_ADDRESS) #define MY_GATEWAY_CLIENT_MODE #endif @@ -238,23 +247,16 @@ #endif #if defined(MY_GATEWAY_ESP8266) // GATEWAY - ESP8266 - #if defined(MY_GATEWAY_MQTT_CLIENT) - #include "drivers/pubsubclient/src/PubSubClient.cpp" - #include "core/MyGatewayTransportMQTTClient.cpp" - #else - #include "core/MyGatewayTransportEthernet.cpp" - #endif + #include "core/MyGatewayTransportEthernet.cpp" #elif defined(MY_GATEWAY_LINUX) - // GATEWAY - Generic Linux (RaspberryPi, BBB) - #include "core/MyGatewayTransportEthernetLinux.cpp" + // GATEWAY - Generic Linux (RaspberryPi, BBB, ...) + #include "drivers/Linux/EthernetClient.cpp" + #include "drivers/Linux/EthernetServer.cpp" + #include "drivers/Linux/IPAddress.cpp" + #include "core/MyGatewayTransportEthernet.cpp" #elif defined(MY_GATEWAY_W5100) // GATEWAY - W5100 - #if defined(MY_GATEWAY_MQTT_CLIENT) - #include "drivers/pubsubclient/src/PubSubClient.cpp" - #include "core/MyGatewayTransportMQTTClient.cpp" - #else - #include "core/MyGatewayTransportEthernet.cpp" - #endif + #include "core/MyGatewayTransportEthernet.cpp" #elif defined(MY_GATEWAY_ENC28J60) // GATEWAY - ENC28J60 #if defined(MY_USE_UDP) @@ -264,32 +266,9 @@ #elif defined(MY_GATEWAY_SERIAL) // GATEWAY - SERIAL #include "core/MyGatewayTransportSerial.cpp" - #endif #endif -// GATEWAY - MQTT -#if defined(MY_GATEWAY_MQTT_CLIENT) && !defined(MY_GATEWAY_LINUX) - // GATEWAY - COMMON FUNCTIONS - // We only support MQTT Client using W5100, ESP8266 or Linux/RasperryPi at the moment - #if !(defined(MY_GATEWAY_ESP8266) || defined(MY_GATEWAY_W5100) || defined(MY_GATEWAY_LINUX)) - #error We only support MQTT Client using W5100, ESP8266 or Linux/RasperryPi at the moment - #endif - - #if !(defined(MY_CONTROLLER_URL_ADDRESS) || defined(MY_CONTROLLER_IP_ADDRESS)) - #error You must specify MY_CONTROLLER_IP_ADDRESS or MY_CONTROLLER_URL_ADDRESS - #endif - - #if !defined(MY_MQTT_PUBLISH_TOPIC_PREFIX) - #error You must specify a topic publish prefix MY_MQTT_PUBLISH_TOPIC_PREFIX for this MQTT client - #endif - #if !defined(MY_MQTT_SUBSCRIBE_TOPIC_PREFIX) - #error You must specify a topic subscribe prefix MY_MQTT_SUBSCRIBE_TOPIC_PREFIX for this MQTT client - #endif - #if !defined(MY_MQTT_CLIENT_ID) - #error You must define a unique MY_MQTT_CLIENT_ID for this MQTT client - #endif -#endif // RADIO #if defined(MY_RADIO_NRF24) || defined(MY_RADIO_RFM69) || defined(MY_RS485) diff --git a/configure b/configure new file mode 100755 index 0000000..ad46759 --- /dev/null +++ b/configure @@ -0,0 +1,322 @@ +#!/bin/bash +# This version is heavily based on the work of mz-fuzzy (https://github.com/mz-fuzzy) +# adapted to work with MySensors project. +# Original work: https://github.com/TMRh20/RF24/blob/master/configure + +function help { +cat < CPU defining/optimizing flags to be used. [configure autodetected] + --extra-cxxflags= Extra C flags passed to C/C++ compilation. [] + --extra-ldflags= Extra C flags passed to linking. [] + --libname= Library name. [rf24] + --cxx_compiler= C++ compiler [arm-linux-gnueabihf-g++][g++] + --no-clean Don't clean previous build artifacts. + --rf24h-lib-dir RF24 library files directory. + +Installation options: + --prefix= Installation prefix path. [/usr/local] + --gateway-dir= Gateway files installation directory. [PREFIX/bin] + +MySensors options: + --my-debug Enables debug. + --my-gateway-type=[ethernet|mqtt] + Gateway type, default to ethernet. + --my-config-file= Config file path. [/etc/MySensorGateway.cfg] + --my-rf24-enable Enables RF24 radio. + --my-rf24-channel=<0-125> RF channel for the sensor net, 0-125. + --my-rf24-pa-level=[RF24_PA_MAX|RF24_PA_LOW] + RF24 PA level. + --my-controller-url-address= + Controller or MQTT broker url. + --my-controller-ip-address= + Controller or MQTT broker ip. + Use commas instead of points. Example: 127,0,0,1 + --my-port Controller or MQTT broker port. + --my-mqtt-client-id= MQTT client id. + --my-mqtt-publish-topic-prefix= + MQTT publish topic prefix. + --my-mqtt-subscribe-topic-prefix= + MQTT subscribe topic prefix. + --my-rf24-irq-pin Pin number connected to nRF24L01 IRQ pin. + --my-rx-message-buffer-size Buffer size for incoming messages when using rf24 interrupts. + Default set to 20. +EOF +} + +function die { + echo "[ERROR] $1" + exit $2 +} + +function detect_rpi_revision { + # get PI Revision from cpuinfo + local pirev=$(eval "cat /proc/cpuinfo 2>/dev/null | grep Revision | cut -f 2 -d ':' | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$$//'") + echo ${pirev} +} + +function detect_machine { + local cpu=$(eval "uname -m 2>/dev/null") + local machine=$(eval "cat /sys/firmware/devicetree/base/model 2>/dev/null") + local hardware=$(eval "grep sunxi_platform /sys/class/sunxi_info/sys_info 2>/dev/null | sed 's/^.*: \(.*\)$/\1/'") + if [ -z "$hardware" ]; then + local hardware=$(eval "grep Hardware /proc/cpuinfo 2>/dev/null | sed 's/^.*: \(.*\)$/\1/'") + fi + local soc="unknown" + local tp="unknown" + local rev="unknown" + + if [ -z "$cpu" ]; then + cpu="unknown" + fi + + case $hardware in + BCM2708) + soc="BCM2835" + if [[ $machine == "Raspberry"* ]]; then + tp="RPi" + rev=($(detect_rpi_revision)) + fi + ;; + BCM2709) + soc="BCM2836" + if [[ $machine == "Raspberry"* ]]; then + rev=($(detect_rpi_revision)) + if [[ $rev == "a02082" || $rev == "a22082" ]]; then + tp="RPi3" + else + tp="Rpi2" + fi + fi + ;; + sun4i|Sun4iw1p1) + soc="A10" + ;; + sun5i|Sun4iw2p1) + soc="A13" + ;; + Sun4iw2p2) + soc="A12" + ;; + Sun4iw2p3) + soc="A10s" + ;; + sun6i|Sun8iw1p1) + soc="A31" + ;; + Sun8iw1p2) + soc="A31s" + ;; + sun7i|Sun8iw2p1) + soc="A20" + if [[ $machine == "Banana Pi"* ]]; then + tp="BananaPi" + elif [[ $machine == "Banana Pro"* ]]; then + tp="BananaPro" + fi + ;; + sun8i|Sun8iw7p1) + soc="H3" + ;; + Sun8iw3p1) + soc="A23" + ;; + Sun8iw5p1) + soc="A33" + ;; + Sun8iw6p1) + soc="A83t" + ;; + sun9i|Sun9iw1p1) + soc="A80" + ;; + Sun9iw1p2) + soc="A80t" + ;; + sun50i|Sun50iw1p1) + soc="A64" + ;; + 'Generic AM33XX'*) + soc="AM33XX" + ;; + *) + soc="unknown" + esac + echo "${soc} ${tp} ${cpu} ${rev}" +} + +function gcc_cpu_flags { + local soc=$1 + case $soc in + BCM2835) + flags="-march=armv6zk -mtune=arm1176jzf-s -mfpu=vfp -mfloat-abi=hard -DRASPBERRYPI_ARCH" + ;; + BCM2836) + flags="-march=armv7-a -mtune=cortex-a7 -mfpu=neon-vfpv4 -mfloat-abi=hard -DRASPBERRYPI_ARCH" + ;; + AM33XX) + flags="-march=armv7-a -mtune=cortex-a8 -mfpu=neon -mfloat-abi=hard" + ;; + A10) + flags="-march=armv7-a -mtune=cortex-a8 -mfpu=neon -mfloat-abi=hard" + ;; + A13) + flags="-march=armv7-a -mtune=cortex-a8 -mfpu=neon -mfloat-abi=hard" + ;; + A20) + flags="-march=armv7-a -mtune=cortex-a7 -mfpu=neon-vfpv4 -mfloat-abi=hard" + ;; + H3) + flags="-march=armv8-a -mtune=cortex-a53 -mfpu=neon-vfpv4 -mfloat-abi=hard" + ;; + *) + flags="" + esac + echo ${flags} +} + +params="OS SOC CPUFLAGS CXXFLAGS LDFLAGS PREFIX CXX RF24H_LIB_DIR GATEWAY_DIR" + +for opt do + if [ "$opt" = "-h" ] || [ "$opt" = "--help" ]; then + help + exit 0 + fi + optarg="${opt#*=}" + case "$opt" in + --os=*) + OS="$optarg" + ;; + --soc=*) + SOC="$optarg" + ;; + --cpu-flags=*) + CPUFLAGS="$optarg" + ;; + --extra-cxxflags=*) + CXXFLAGS="$optarg" + ;; + --extra-ldflags=*) + LDFLAGS="$optarg" + ;; + --cxx_compiler=*) + CXX="$optarg" + ;; + --no-clean*) + NO_CLEAN="1" + ;; + --prefix=*) + PREFIX="$optarg" + ;; + --rf24h-lib-dir=*) + RF24H_LIB_DIR="$optarg" + ;; + --gateway-dir=*) + GATEWAY_DIR="$optarg" + ;; + --my-debug*) + CXXFLAGS="-DMY_DEBUG $CXXFLAGS" + ;; + --my-gateway-type=*) + if [[ ${optarg} == "mqtt" ]]; then + LDFLAGS="-lmosquitto $LDFLAGS" + CXXFLAGS="-DMY_GATEWAY_MQTT_CLIENT $CXXFLAGS" + fi + ;; + --my-config-file=*) + CXXFLAGS="-DMY_CONFIG_FILE=${optarg} $CXXFLAGS" + ;; + --my-rf24-enable*) + CXXFLAGS="-DMY_RADIO_NRF24 $CXXFLAGS" + ;; + --my-rf24-channel=*) + CXXFLAGS="-DMY_RF24_CHANNEL=${optarg} $CXXFLAGS" + ;; + --my-rf24-pa-level=*) + CXXFLAGS="-DMY_RF24_PA_LEVEL=${optarg} $CXXFLAGS" + ;; + --my-controller-url-address=*) + CXXFLAGS="-DMY_CONTROLLER_URL_ADDRESS=${optarg} $CXXFLAGS" + ;; + --my-controller-ip-address=*) + CXXFLAGS="-DMY_CONTROLLER_IP_ADDRESS=${optarg} $CXXFLAGS" + ;; + --my-port=*) + CXXFLAGS="-DMY_PORT=${optarg} $CXXFLAGS" + ;; + --my-mqtt-client-id=*) + CXXFLAGS="-DMY_MQTT_CLIENT_ID=${optarg} $CXXFLAGS" + ;; + --my-mqtt-publish-topic-prefix=*) + CXXFLAGS="-DMY_MQTT_PUBLISH_TOPIC_PREFIX=${optarg} $CXXFLAGS" + ;; + --my-mqtt-subscribe-topic-prefix=*) + CXXFLAGS="-DMY_MQTT_SUBSCRIBE_TOPIC_PREFIX=${optarg} $CXXFLAGS" + ;; + --my-rf24-irq-pin=*) + CXXFLAGS="-DMY_RF24_IRQ_PIN=${optarg} $CXXFLAGS" + ;; + --my-rx-message-buffer-size=*) + CXXFLAGS="-DMY_RX_MESSAGE_BUFFER_SIZE=${optarg} $CXXFLAGS" + ;; + *) + echo "[WARNING] Unknown option detected:$opt, ignored" + ;; + esac +done + +PREFIX=${PREFIX:-/usr/local} +RF24H_LIB_DIR=${PREFIX}/include/RF24 +GATEWAY_DIR=${GATEWAY_DIR:-${PREFIX}/bin} +CXX=${CXX:-g++} + +if [ -z "${SOC}" ]; then + echo "[SECTION] Detecting target machine." + info=($(detect_machine)) + SOC=${info[0]} + TYPE=${info[1]} + CPU=${info[2]} + REV=${info[3]} + echo "[OK] machine detected: SoC=${SOC}, Type=${TYPE}, CPU=${CPU}, REV=${REV}." +fi + +if [ -z "${CPUFLAGS}" ]; then + CPUFLAGS=$(gcc_cpu_flags $SOC) +fi + +CXXFLAGS="$CPUFLAGS -Ofast -g -Wall -Wextra -DLINUX_ARCH_GENERIC $CXXFLAGS" + +if [[ $TYPE == "RPi2" || $TYPE == "RPi3" || $REV == "0010" ]]; then + CXXFLAGS+="-D__RPI_BPLUS" +fi + +LDFLAGS="-pthread -lrf24-bcm $LDFLAGS" + +echo "[SECTION] Saving configuration." +echo -n "" > Makefile.inc +for param in ${params}; do + if [[ ${!param} ]]; then + echo "${param}=${!param}" >> Makefile.inc + fi +done + +if [ -z "${NO_CLEAN}" ]; then + echo "[SECTION] Cleaning previous builds." + make clean >/dev/null +fi + + +echo "[OK] Finished." diff --git a/core/MyGatewayTransportEthernet.cpp b/core/MyGatewayTransportEthernet.cpp index 6c930e7..a8f6599 100644 --- a/core/MyGatewayTransportEthernet.cpp +++ b/core/MyGatewayTransportEthernet.cpp @@ -26,7 +26,7 @@ #if defined(MY_IP_ADDRESS) IPAddress _ethernetGatewayIP(MY_IP_ADDRESS); #endif -byte _ethernetGatewayMAC[] = { MY_MAC_ADDRESS }; +uint8_t _ethernetGatewayMAC[] = { MY_MAC_ADDRESS }; uint16_t _ethernetGatewayPort = MY_PORT; MyMessage _ethernetMsg; @@ -36,8 +36,8 @@ MyMessage _ethernetMsg; typedef struct { - char string[MY_GATEWAY_MAX_RECEIVE_LENGTH]; - uint8_t idx; + char string[MY_GATEWAY_MAX_RECEIVE_LENGTH]; + uint8_t idx; } inputBuffer; #if defined(MY_GATEWAY_ESP8266) @@ -50,7 +50,6 @@ typedef struct IPAddress _gatewayIp(MY_IP_GATEWAY_ADDRESS); IPAddress _subnetIp(MY_IP_SUBNET_ADDRESS); #endif - static bool clientsConnected[MY_GATEWAY_MAX_CLIENTS]; #endif #if defined(MY_USE_UDP) @@ -60,8 +59,9 @@ typedef struct #endif -#if defined(MY_GATEWAY_ESP8266) +#if defined(MY_GATEWAY_ESP8266) || defined(MY_GATEWAY_LINUX) static EthernetClient clients[MY_GATEWAY_MAX_CLIENTS]; + static bool clientsConnected[MY_GATEWAY_MAX_CLIENTS]; static inputBuffer inputString[MY_GATEWAY_MAX_CLIENTS]; #else static EthernetClient client = EthernetClient(); @@ -78,18 +78,18 @@ typedef struct #ifdef MY_W5100_SPI_EN void _w5100_spi_en(bool enable) { - if (enable) - { - // Pull up pin - pinMode(MY_W5100_SPI_EN, INPUT); - digitalWrite(MY_W5100_SPI_EN, HIGH); - } - else - { - // Ground pin - pinMode(MY_W5100_SPI_EN, OUTPUT); - digitalWrite(MY_W5100_SPI_EN, LOW); - } + if (enable) + { + // Pull up pin + pinMode(MY_W5100_SPI_EN, INPUT); + digitalWrite(MY_W5100_SPI_EN, HIGH); + } + else + { + // Ground pin + pinMode(MY_W5100_SPI_EN, OUTPUT); + digitalWrite(MY_W5100_SPI_EN, LOW); + } } #else #define _w5100_spi_en(x) @@ -119,7 +119,8 @@ bool gatewayTransportInit() { MY_SERIALDEVICE.print(F("IP: ")); MY_SERIALDEVICE.println(WiFi.localIP()); #endif - + #elif defined(MY_GATEWAY_LINUX) + // Nothing to do here #else #ifdef MY_IP_ADDRESS Ethernet.begin(_ethernetGatewayMAC, _ethernetGatewayIP); @@ -140,8 +141,12 @@ bool gatewayTransportInit() { #ifdef MY_USE_UDP _ethernetServer.begin(_ethernetGatewayPort); #else - // we have to use pointers due to the constructor of EthernetServer - _ethernetServer.begin(); + #if defined(MY_GATEWAY_LINUX) && defined(MY_IP_ADDRESS) + _ethernetServer.begin(_ethernetGatewayIP); + #else + // we have to use pointers due to the constructor of EthernetServer + _ethernetServer.begin(); + #endif #endif /* USE_UDP */ _w5100_spi_en(false); return true; @@ -152,7 +157,7 @@ bool gatewayTransportSend(MyMessage &message) bool ret = true; char *_ethernetMsg = protocolFormat(message); - setIndication(INDICATION_GW_TX); + setIndication(INDICATION_GW_TX); _w5100_spi_en(true); #if defined(MY_CONTROLLER_IP_ADDRESS) @@ -164,16 +169,16 @@ bool gatewayTransportSend(MyMessage &message) #else EthernetClient client; #if defined(MY_CONTROLLER_URL_ADDRESS) - if (client.connected() || client.connect(MY_CONTROLLER_URL_ADDRESS, MY_PORT)) { - #else - if (client.connected() || client.connect(_ethernetControllerIP, MY_PORT)) { - #endif - client.write(_ethernetMsg, strlen(_ethernetMsg)); - } - else { - // connecting to the server failed! - ret = false; - } + if (client.connected() || client.connect(MY_CONTROLLER_URL_ADDRESS, MY_PORT)) { + #else + if (client.connected() || client.connect(_ethernetControllerIP, MY_PORT)) { + #endif + client.write(_ethernetMsg, strlen(_ethernetMsg)); + } + else { + // connecting to the server failed! + ret = false; + } #endif #else // Send message to connected clients @@ -195,7 +200,7 @@ bool gatewayTransportSend(MyMessage &message) } -#if defined(MY_GATEWAY_ESP8266) +#if defined(MY_GATEWAY_ESP8266) || defined(MY_GATEWAY_LINUX) bool _readFromClient(uint8_t i) { while (clients[i].connected() && clients[i].available()) { char inChar = clients[i].read(); @@ -270,7 +275,7 @@ bool gatewayTransportAvailable() if (packet_size) { //debug(PSTR("UDP packet available. Size:%d\n"), packet_size); - setIndication(INDICATION_GW_RX); + setIndication(INDICATION_GW_RX); #if defined(MY_GATEWAY_ESP8266) _ethernetServer.read(inputString[0].string, MY_GATEWAY_MAX_RECEIVE_LENGTH); inputString[0].string[packet_size] = 0; @@ -285,7 +290,7 @@ bool gatewayTransportAvailable() #endif } #else - #if defined(MY_GATEWAY_ESP8266) + #if defined(MY_GATEWAY_ESP8266) || defined(MY_GATEWAY_LINUX) // ESP8266: Go over list of clients and stop any that are no longer connected. // If the server has a new client connection it will be assigned to a free slot. bool allSlotsOccupied = true; @@ -318,7 +323,7 @@ bool gatewayTransportAvailable() // Loop over clients connect and read available data for (uint8_t i = 0; i < ARRAY_SIZE(clients); i++) { if (_readFromClient(i)) { - setIndication(INDICATION_GW_RX); + setIndication(INDICATION_GW_RX); _w5100_spi_en(false); return true; } @@ -344,7 +349,7 @@ bool gatewayTransportAvailable() client.stop(); } else { if (_readFromClient()) { - setIndication(INDICATION_GW_RX); + setIndication(INDICATION_GW_RX); _w5100_spi_en(false); return true; } @@ -363,7 +368,7 @@ MyMessage& gatewayTransportReceive() } -#if !defined(MY_IP_ADDRESS) && !defined(MY_GATEWAY_ESP8266) +#if !defined(MY_IP_ADDRESS) && !defined(MY_GATEWAY_ESP8266) && !defined(MY_GATEWAY_LINUX) void gatewayTransportRenewIP() { /* renew/rebind IP address diff --git a/core/MyGatewayTransportMQTTClient.cpp b/core/MyGatewayTransportMQTTClient.cpp index b6ca377..85f5186 100644 --- a/core/MyGatewayTransportMQTTClient.cpp +++ b/core/MyGatewayTransportMQTTClient.cpp @@ -32,8 +32,10 @@ IPAddress _gatewayIp(MY_IP_GATEWAY_ADDRESS); IPAddress _subnetIp(MY_IP_SUBNET_ADDRESS); #endif +#elif defined(MY_GATEWAY_LINUX) + // Nothing to do here #else - byte _MQTT_clientMAC[] = { MY_MAC_ADDRESS }; + uint8_t _MQTT_clientMAC[] = { MY_MAC_ADDRESS }; #endif #if defined(MY_IP_ADDRESS) @@ -46,98 +48,20 @@ static bool _MQTT_connecting = true; static bool _MQTT_available = false; static MyMessage _MQTT_msg; -uint8_t protocolH2i(char c) { - uint8_t i = 0; - if (c <= '9') - i += c - '0'; - else if (c >= 'a') - i += c - 'a' + 10; - else - i += c - 'A' + 10; - return i; -} - - bool gatewayTransportSend(MyMessage &message) { if (!_MQTT_client.connected()) return false; setIndication(INDICATION_GW_TX); - char _fmtBuffer[MY_GATEWAY_MAX_SEND_LENGTH]; - char _convBuffer[MAX_PAYLOAD * 2 + 1]; - snprintf_P(_fmtBuffer, MY_GATEWAY_MAX_SEND_LENGTH, PSTR(MY_MQTT_PUBLISH_TOPIC_PREFIX "/%d/%d/%d/%d/%d"), message.sender, message.sensor, mGetCommand(message), mGetAck(message), message.type); - debug(PSTR("Sending message on topic: %s\n"), _fmtBuffer); - return _MQTT_client.publish(_fmtBuffer, message.getString(_convBuffer)); + char *topic = protocolFormatMQTTTopic(MY_MQTT_PUBLISH_TOPIC_PREFIX, message); + debug(PSTR("Sending message on topic: %s\n"), topic); + return _MQTT_client.publish(topic, message.getString(_convBuffer)); } -void incomingMQTT(char* topic, byte* payload, unsigned int length) { +void incomingMQTT(char* topic, uint8_t* payload, unsigned int length) { debug(PSTR("Message arrived on topic: %s\n"), topic); - char *str, *p; - uint8_t i = 0; - uint8_t bvalue[MAX_PAYLOAD]; - uint8_t blen = 0; - uint8_t command = 0; - for (str = strtok_r(topic, "/", &p); str && i <= 5; - str = strtok_r(NULL, "/", &p)) { - switch (i) { - case 0: { - // Topic prefix - if (strcmp(str, MY_MQTT_SUBSCRIBE_TOPIC_PREFIX) != 0) { - // Message not for us or malformed! - return; - } - break; - } - case 1: { - // Node id - _MQTT_msg.destination = atoi(str); - break; - } - case 2: { - // Sensor id - _MQTT_msg.sensor = atoi(str); - break; - } - case 3: { - // Command type - command = atoi(str); - mSetCommand(_MQTT_msg, command); - break; - } - case 4: { - // Ack flag - mSetRequestAck(_MQTT_msg, atoi(str) ? 1 : 0); - break; - } - case 5: { - // Sub type - _MQTT_msg.type = atoi(str); - // Add payload - if (command == C_STREAM) { - blen = 0; - uint8_t val; - while (*payload) { - val = protocolH2i(*payload++) << 4; - val += protocolH2i(*payload++); - bvalue[blen] = val; - blen++; - } - _MQTT_msg.set(bvalue, blen); - } - else { - char* ca; - ca = (char *)payload; - ca += length; - *ca = '\0'; - _MQTT_msg.set((const char*)payload); - } - _MQTT_available = true; - } - } - i++; - } + _MQTT_available = protocolMQTTParse(_MQTT_msg, topic, payload, length); } - bool reconnectMQTT() { debug(PSTR("Attempting MQTT connection...\n")); // Attempt to connect @@ -168,6 +92,10 @@ bool gatewayTransportConnect() { } MY_SERIALDEVICE.print("IP: "); MY_SERIALDEVICE.println(WiFi.localIP()); + #elif defined(MY_GATEWAY_LINUX) + #if defined(MY_IP_ADDRESS) + //TODO + #endif #else #ifdef MY_IP_ADDRESS Ethernet.begin(_MQTT_clientMAC, _MQTT_clientIp); @@ -215,8 +143,6 @@ bool gatewayTransportInit() { return true; } - - bool gatewayTransportAvailable() { if (_MQTT_connecting) @@ -239,6 +165,3 @@ MyMessage & gatewayTransportReceive() { _MQTT_available = false; return _MQTT_msg; } - - - diff --git a/core/MyHwATMega328.h b/core/MyHwATMega328.h index 157ecb1..c486239 100644 --- a/core/MyHwATMega328.h +++ b/core/MyHwATMega328.h @@ -57,7 +57,13 @@ do { \ // Define these as macros to save valuable space #define hwDigitalWrite(__pin, __value) (digitalWrite(__pin, __value)) -#define hwInit() MY_SERIALDEVICE.begin(MY_BAUD_RATE) + +#if defined(MY_DISABLED_SERIAL) + #define hwInit() +#else + #define hwInit() MY_SERIALDEVICE.begin(MY_BAUD_RATE) +#endif + #define hwWatchdogReset() wdt_reset() #define hwReboot() wdt_enable(WDTO_15MS); while (1) #define hwMillis() millis() diff --git a/core/MyHwESP8266.h b/core/MyHwESP8266.h index 1495e69..2c5a942 100644 --- a/core/MyHwESP8266.h +++ b/core/MyHwESP8266.h @@ -33,7 +33,13 @@ // Define these as macros to save valuable space #define hwDigitalWrite(__pin, __value) (digitalWrite(__pin, __value)) -#define hwInit() MY_SERIALDEVICE.begin(MY_BAUD_RATE); MY_SERIALDEVICE.setDebugOutput(true) + +#if defined(MY_DISABLED_SERIAL) + #define hwInit() +#else + #define hwInit() MY_SERIALDEVICE.begin(MY_BAUD_RATE); MY_SERIALDEVICE.setDebugOutput(true) +#endif + #define hwWatchdogReset() wdt_reset() #define hwReboot() ESP.restart(); #define hwMillis() millis() diff --git a/core/MyHwLinuxGeneric.cpp b/core/MyHwLinuxGeneric.cpp new file mode 100644 index 0000000..272fe29 --- /dev/null +++ b/core/MyHwLinuxGeneric.cpp @@ -0,0 +1,190 @@ +/** + * The MySensors Arduino library handles the wireless radio link and protocol + * between your home built sensors/actuators and HA controller of choice. + * The sensors forms a self healing radio network with optional repeaters. Each + * repeater and gateway builds a routing tables in EEPROM which keeps track of the + * network topology allowing messages to be routed to nodes. + * + * Created by Marcelo Aquino + * Copyright (C) 2016 Marcelo Aquino + * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors + * + * Documentation: http://www.mysensors.org + * Support Forum: http://forum.mysensors.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + */ + +#include "MyHwLinuxGeneric.h" + +#include +#include +#include +#include +#include + +static const char* CONFIG_FILE = MY_LINUX_CONFIG_FILE; +static const size_t _length = 1024; // ATMega328 has 1024 bytes +static uint8_t _config[_length]; +static unsigned long millis_at_start; + +bool CheckConfigFile() { + struct stat fileInfo; + + if(stat(CONFIG_FILE, &fileInfo) != 0) + { + //File does not exist. Create it. + debug("Config file %s does not exist, creating new config file.\n", CONFIG_FILE); + ofstream myFile(CONFIG_FILE, ios::out | ios::binary); + if(!myFile) + { + debug("Unable to create config file %s.\n", CONFIG_FILE); + return false; + } + myFile.write((const char*)_config, _length); + myFile.close(); + } + else if(fileInfo.st_size < 0 || (size_t)fileInfo.st_size != _length) + { + debug("Config file %s is not the correct size of %i. Please remove the file and a new one will be created.\n", CONFIG_FILE, _length); + return false; + } + else + { + //Read config into local memory. + ifstream myFile(CONFIG_FILE, ios::in | ios::binary); + if(!myFile) + { + debug("Unable to open config to file %s for reading.\n", CONFIG_FILE); + return false; + } + myFile.read((char*)_config, _length); + myFile.close(); + } + + return true; +} + +void hwInit() +{ + timeval curTime; + + for (size_t i = 0; i < _length; i++) + _config[i] = 0xFF; + + if (!CheckConfigFile()) + { + exit(1); + } + + gettimeofday(&curTime, NULL); + millis_at_start = curTime.tv_sec; +} + +void hwReadConfigBlock(void* buf, void* addr, size_t length) +{ + unsigned long int offs = reinterpret_cast(addr); + + if (length && offs + length <= _length) { + memcpy(buf, _config+offs, length); + } +} + +void hwWriteConfigBlock(void* buf, void* addr, size_t length) +{ + unsigned long int offs = reinterpret_cast(addr); + + if (length && offs + length <= _length) { + memcpy(_config+offs, buf, length); + + ofstream myFile(CONFIG_FILE, ios::out | ios::binary); + if(!myFile) + { + debug("Unable to write config to file %s.\n", CONFIG_FILE); + return; + } + myFile.write((const char*)buf+offs, _length); + myFile.close(); + } +} + +uint8_t hwReadConfig(int adr) +{ + uint8_t value = 0xFF; + hwReadConfigBlock(&value, reinterpret_cast(adr), 1); + return value; +} + +void hwWriteConfig(int adr, uint8_t value) +{ + uint8_t curr = hwReadConfig(adr); + if (curr != value) + { + hwWriteConfigBlock(&value, reinterpret_cast(adr), 1); + } +} + +unsigned long hwMillis() +{ + timeval curTime; + + gettimeofday(&curTime, NULL); + return ((curTime.tv_sec - millis_at_start) * 1000) + (curTime.tv_usec / 1000); +} + +// TODO: Not supported! +int8_t hwSleep(unsigned long ms) +{ + (void)ms; + + return -2; +} + +// TODO: Not supported! +int8_t hwSleep(uint8_t interrupt, uint8_t mode, unsigned long ms) +{ + (void)interrupt; + (void)mode; + (void)ms; + + return -2; +} + +// TODO: Not supported! +int8_t hwSleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2, unsigned long ms) +{ + (void)interrupt1; + (void)mode1; + (void)interrupt2; + (void)mode2; + (void)ms; + + return -2; +} + +uint16_t hwCPUVoltage() { + // TODO: Not supported! + return 0; +} + +uint16_t hwCPUFrequency() { + // TODO: Not supported! + return 0; +} + +uint16_t hwFreeMem() { + // TODO: Not supported! + return 0; +} + +#ifdef MY_DEBUG +void hwDebugPrint(const char *fmt, ... ) +{ + va_list arglist; + va_start(arglist, fmt); + vprintf(fmt, arglist); + va_end(arglist); +} +#endif diff --git a/core/MyHwLinuxGeneric.h b/core/MyHwLinuxGeneric.h new file mode 100644 index 0000000..32e8406 --- /dev/null +++ b/core/MyHwLinuxGeneric.h @@ -0,0 +1,75 @@ +/** + * The MySensors Arduino library handles the wireless radio link and protocol + * between your home built sensors/actuators and HA controller of choice. + * The sensors forms a self healing radio network with optional repeaters. Each + * repeater and gateway builds a routing tables in EEPROM which keeps track of the + * network topology allowing messages to be routed to nodes. + * + * Created by Marcelo Aquino + * Copyright (C) 2016 Marcelo Aquino + * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors + * + * Documentation: http://www.mysensors.org + * Support Forum: http://forum.mysensors.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + */ + +#ifndef MyHwLinuxGeneric_h +#define MyHwLinuxGeneric_h + +#include "MyHw.h" +#include +#include +#include + +#define MY_SERIALDEVICE Serial + +// Define these as macros (do nothing) +#define hwDigitalWrite(__pin, __value) +#define hwWatchdogReset() +#define hwReboot() + +using namespace std; + +void hwInit(); +void hwReadConfigBlock(void* buf, void* adr, size_t length); +void hwWriteConfigBlock(void* buf, void* adr, size_t length); +void hwWriteConfig(int adr, uint8_t value); +uint8_t hwReadConfig(int adr); +unsigned long hwMillis(); + +#ifdef MY_RF24_IRQ_PIN +static pthread_mutex_t hw_mutex = PTHREAD_MUTEX_INITIALIZER; + +static __inline__ void __hwUnlock(const uint8_t *__s) { + pthread_mutex_unlock(&hw_mutex); + (void)__s; +} + +static __inline__ void __hwLock() { + pthread_mutex_lock(&hw_mutex); +} +#endif + +#if defined(__DOXYGEN__) + #define ATOMIC_BLOCK_CLEANUP +#elif defined(MY_RF24_IRQ_PIN) + #define ATOMIC_BLOCK_CLEANUP uint8_t __atomic_loop \ + __attribute__((__cleanup__( __hwUnlock ))) = 1 +#endif /* __DOXYGEN__ */ + +#if defined(__DOXYGEN__) + #define ATOMIC_BLOCK +#elif defined(MY_RF24_IRQ_PIN) + #define ATOMIC_BLOCK for ( ATOMIC_BLOCK_CLEANUP, __hwLock(); \ + __atomic_loop ; __atomic_loop = 0 ) +#endif /* __DOXYGEN__ */ + +#ifndef DOXYGEN + #define MY_CRITICAL_SECTION ATOMIC_BLOCK +#endif /* DOXYGEN */ + +#endif diff --git a/core/MyMainLinux.cpp b/core/MyMainLinux.cpp new file mode 100644 index 0000000..5ecab96 --- /dev/null +++ b/core/MyMainLinux.cpp @@ -0,0 +1,39 @@ +// Initialize library and handle sketch functions like we want to + +#include +#include +#include "MySensorsCore.h" + +volatile static int running = 1; + +/* + * handler for SIGINT signal + */ +void handle_sigint(int sig) +{ + running = 0; + + if (sig == SIGINT) { + std::cout << "Received SIGINT\n" << std::endl; + } else if (sig == SIGTERM) { + std::cout << "Received SIGTERM\n" << std::endl; + } + + #ifdef MY_RF24_IRQ_PIN + detachInterrupt(MY_RF24_IRQ_PIN); + #endif +} + +int main(void) { + /* register the signal handler */ + signal(SIGINT, handle_sigint); + signal(SIGTERM, handle_sigint); + + _begin(); // Startup MySensors library + + while (running) { + _process(); // Process incoming data + if (loop) loop(); // Call sketch loop + } + return 0; +} diff --git a/core/MyMessage.cpp b/core/MyMessage.cpp index 027b273..1179a0b 100644 --- a/core/MyMessage.cpp +++ b/core/MyMessage.cpp @@ -21,7 +21,11 @@ #include "MyMessage.h" #include #include +#include +#ifndef ARDUINO + #define min(a,b) ((a)<(b)?(a):(b)) +#endif MyMessage::MyMessage() { diff --git a/core/MyMessage.h b/core/MyMessage.h index aa84b49..b11a7ff 100644 --- a/core/MyMessage.h +++ b/core/MyMessage.h @@ -32,7 +32,6 @@ #ifdef __cplusplus #include -#include #include #endif diff --git a/core/MyProtocolMySensors.cpp b/core/MyProtocolMySensors.cpp index 18fdbd5..8c96b42 100644 --- a/core/MyProtocolMySensors.cpp +++ b/core/MyProtocolMySensors.cpp @@ -20,6 +20,7 @@ #include "MyConfig.h" #include "MyTransport.h" #include "MyProtocol.h" +#include uint8_t protocolH2i(char c); @@ -32,7 +33,6 @@ bool protocolParse(MyMessage &message, char *inputString) { uint8_t blen = 0; int i = 0; uint8_t command = 0; - uint8_t ack = 0; // Extract command data coming on serial line for (str = strtok_r(inputString, ";", &p); // split using semicolon @@ -51,7 +51,7 @@ bool protocolParse(MyMessage &message, char *inputString) { mSetCommand(message, command); break; case 3: // Should we request ack from destination? - ack = atoi(str); + mSetRequestAck(message, atoi(str)?1:0); break; case 4: // Data type message.type = atoi(str); @@ -86,8 +86,7 @@ bool protocolParse(MyMessage &message, char *inputString) { message.sender = GATEWAY_ADDRESS; message.last = GATEWAY_ADDRESS; - mSetRequestAck(message, ack?1:0); - mSetAck(message, false); + mSetAck(message, false); if (command == C_STREAM) message.set(bvalue, blen); else @@ -110,23 +109,23 @@ char * protocolFormatMQTTSubscribe(const char* prefix) { return _fmtBuffer; } -void protocolMQTTParse(MyMessage &message, char* topic, uint8_t* payload, unsigned int length) { - //TODO: Not sure if we should copy the topic and payload into a buffer so we are not modifying original. +#ifdef MY_GATEWAY_MQTT_CLIENT +bool protocolMQTTParse(MyMessage &message, char* topic, uint8_t* payload, unsigned int length) { char *str, *p; uint8_t i = 0; uint8_t bvalue[MAX_PAYLOAD]; uint8_t blen = 0; uint8_t command = 0; + for (str = strtok_r(topic, "/", &p); str && i <= 5; str = strtok_r(NULL, "/", &p)) { switch (i) { case 0: { // Topic prefix - //TODO: Should be checking the topic prefix - //if (strcmp(str, MY_MQTT_SUBSCRIBE_TOPIC_PREFIX) != 0) { + if (strcmp(str, MY_MQTT_SUBSCRIBE_TOPIC_PREFIX) != 0) { // Message not for us or malformed! - // return; - //} + return false; + } break; } case 1: { @@ -153,29 +152,41 @@ void protocolMQTTParse(MyMessage &message, char* topic, uint8_t* payload, unsign case 5: { // Sub type message.type = atoi(str); - // Add payload - if (command == C_STREAM) { - blen = 0; - uint8_t val; - while (*payload) { - val = protocolH2i(*payload++) << 4; - val += protocolH2i(*payload++); - bvalue[blen] = val; - blen++; - } - message.set(bvalue, blen); - } else { - char* ca; - ca = (char *) payload; - ca += length; - *ca = '\0'; - message.set((const char*) payload); - } + break; } } i++; } + + if (i != 6) + return false; + + message.sender = GATEWAY_ADDRESS; + message.last = GATEWAY_ADDRESS; + mSetAck(message, false); + + // Add payload + if (command == C_STREAM) { + blen = 0; + uint8_t val; + while (*payload) { + val = protocolH2i(*payload++) << 4; + val += protocolH2i(*payload++); + bvalue[blen] = val; + blen++; + } + message.set(bvalue, blen); + } else { + char* ca; + ca = (char *) payload; + ca += length; + *ca = '\0'; + message.set((const char*) payload); + } + + return true; } +#endif uint8_t protocolH2i(char c) { uint8_t i = 0; @@ -187,5 +198,3 @@ uint8_t protocolH2i(char c) { i += c - 'A' + 10; return i; } - - diff --git a/core/MySensorsCore.cpp b/core/MySensorsCore.cpp index 0be5781..959e33b 100644 --- a/core/MySensorsCore.cpp +++ b/core/MySensorsCore.cpp @@ -51,6 +51,10 @@ void _process() { transportProcess(); #endif + #if defined(LINUX_ARCH_GENERIC) + // To avoid high cpu usage + usleep(10000); // 10ms + #endif } void _infiniteLoop() { @@ -61,17 +65,17 @@ void _infiniteLoop() { #if defined (MY_LEDS_BLINKING_FEATURE) ledsProcess(); #endif + #if defined(LINUX_ARCH_GENERIC) + exit(1); + #endif } } void _begin() { - if (preHwInit) preHwInit(); - #if !defined(MY_DISABLED_SERIAL) - hwInit(); - #endif + hwInit(); debug(PSTR("MCO:BGN:INIT " MY_NODE_TYPE ",CP=" MY_CAPABILITIES ",VER=" MYSENSORS_LIBRARY_VERSION "\n")); diff --git a/core/MyTransport.cpp b/core/MyTransport.cpp index a54c34d..61925e7 100644 --- a/core/MyTransport.cpp +++ b/core/MyTransport.cpp @@ -467,7 +467,7 @@ void transportProcessMessage() { // Is message addressed to this node? if (destination == _nc.nodeId) { // prevent buffer overflow by limiting max. possible message length (5 bits=31 bytes max) to MAX_PAYLOAD (25 bytes) - mSetLength(_msg, min(mGetLength(_msg),MAX_PAYLOAD)); + mSetLength(_msg, min(mGetLength(_msg),(uint8_t)MAX_PAYLOAD)); // null terminate data _msg.data[mGetLength(_msg)] = 0x00; @@ -717,4 +717,4 @@ bool transportSendWrite(uint8_t to, MyMessage &message) { return (ok || to==BROADCAST_ADDRESS); } -// EOF MyTransport.cpp \ No newline at end of file +// EOF MyTransport.cpp diff --git a/core/MyTransportNRF24.cpp b/core/MyTransportNRF24.cpp index 6d7f407..fa72736 100644 --- a/core/MyTransportNRF24.cpp +++ b/core/MyTransportNRF24.cpp @@ -19,7 +19,13 @@ #include "MyConfig.h" #include "MyTransport.h" -#include "drivers/RF24/RF24.h" + +#ifdef RASPBERRYPI_ARCH + #include "drivers/RF24/RF24_RPi.cpp" +#else + #include "drivers/RF24/RF24.h" +#endif + #include "drivers/CircularBuffer/CircularBuffer.h" #if defined(MY_RF24_ENABLE_ENCRYPTION) diff --git a/core/MyTransportRS485.cpp b/core/MyTransportRS485.cpp index 814f721..285c1d1 100644 --- a/core/MyTransportRS485.cpp +++ b/core/MyTransportRS485.cpp @@ -57,12 +57,7 @@ #include "MyTransport.h" #include - -#if defined(ARDUINO) && ARDUINO >= 100 #include -#else -#include -#endif #include "MyTransport.h" diff --git a/drivers/Linux/Arduino.h b/drivers/Linux/Arduino.h new file mode 100644 index 0000000..7ae8162 --- /dev/null +++ b/drivers/Linux/Arduino.h @@ -0,0 +1,212 @@ +#ifndef Arduino_h +#define Arduino_h + +#include + +#ifndef LSBFIRST + #define LSBFIRST 0 +#endif + +#ifndef MSBFIRST + #define MSBFIRST 1 +#endif + +#define abs(x) ((x)>0?(x):-(x)) +#define constrain(amt,low,high) ((amt)<(low)?(low):((amt)>(high)?(high):(amt))) +#define round(x) ((x)>=0?(long)((x)+0.5):(long)((x)-0.5)) +#define radians(deg) ((deg)*DEG_TO_RAD) +#define degrees(rad) ((rad)*RAD_TO_DEG) +#define sq(x) ((x)*(x)) + +#define lowByte(w) ((uint8_t) ((w) & 0xff)) +#define highByte(w) ((uint8_t) ((w) >> 8)) + +#define bit(b) (1UL << (b)) + +typedef bool boolean; +typedef uint8_t byte; + +#ifdef __cplusplus + extern "C" { +#endif + +/** +* C++ version 0.4 char* style "itoa": +* Written by Lukás Chmela +* Released under GPLv3. +*/ +char *itoa(int value, char* result, int base) { + // check that the base if valid + if (base < 2 || base > 36) { *result = '\0'; return result; } + + char* ptr = result, *ptr1 = result, tmp_char; + int tmp_value; + + do { + tmp_value = value; + value /= base; + *ptr++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz" [35 + (tmp_value - value * base)]; + } while ( value ); + + // Apply negative sign + // Apply negative sign + if (tmp_value < 0) *ptr++ = '-'; + *ptr-- = '\0'; + while(ptr1 < ptr) { + tmp_char = *ptr; + *ptr--= *ptr1; + *ptr1++ = tmp_char; + } + return result; +} + +/** + * C++ version 0.4 char* style "itoa": + * Written by Lukás Chmela + * Released under GPLv3. + */ +char *ltoa(long value, char* result, int base) { + // check that the base if valid + if (base < 2 || base > 36) { *result = '\0'; return result; } + + char* ptr = result, *ptr1 = result, tmp_char; + long tmp_value; + + do { + tmp_value = value; + value /= base; + *ptr++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz" [35 + (tmp_value - value * base)]; + } while ( value ); + + // Apply negative sign + if (tmp_value < 0) *ptr++ = '-'; + *ptr-- = '\0'; + while(ptr1 < ptr) { + tmp_char = *ptr; + *ptr--= *ptr1; + *ptr1++ = tmp_char; + } + return result; +} + +char *ultoa(long num, char *str, int radix) +{ + unsigned long value; + char *sp = str; + char *sp2; + + value = num; + + /* Store sign at start of buffer for negative base-10 values */ + if (10 == radix && 0 > num) { + *sp++ = '-'; + value = -num; + } + + sp2 = sp; + + do { + char rem = value % radix; + value /= radix; + if (10 > rem) { + *sp++ = '0' + rem; + } else { + *sp++ = 'A' + rem - 10; + } + } while (0 < value); + + /* Mark end of string */ + *sp-- = 0; + + /* Reverse string contents (excluding sign) in place */ + while (sp2 < sp) { + char tmp = *sp2; + *sp2++ = *sp; + *sp-- = tmp; + } + + return str; +} + +/** + * Copyright (c) 2012, Peter A. Bigot + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of the software nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +char *utoa(int num, char *str, int radix) +{ + unsigned int value; + char *sp = str; + char *sp2; + + value = num; + + /* Store sign at start of buffer for negative base-10 values */ + if (10 == radix && 0 > num) { + *sp++ = '-'; + value = -num; + } + + sp2 = sp; + + do { + char rem = value % radix; + value /= radix; + if (10 > rem) { + *sp++ = '0' + rem; + } else { + *sp++ = 'A' + rem - 10; + } + } while (0 < value); + + /* Mark end of string */ + *sp-- = 0; + + /* Reverse string contents (excluding sign) in place */ + while (sp2 < sp) { + char tmp = *sp2; + *sp2++ = *sp; + *sp-- = tmp; + } + + return str; +} + +char *dtostrf(float f, int width, int decimals, char *result) +{ + sprintf(result,"%*.*f", width, decimals, f); + return result; +} + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/drivers/Linux/Client.h b/drivers/Linux/Client.h new file mode 100644 index 0000000..0169890 --- /dev/null +++ b/drivers/Linux/Client.h @@ -0,0 +1,46 @@ +/* + Client.h - Base class that provides Client + Copyright (c) 2011 Adrian McEwen. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Modified by Marcelo Aquino for MySensors use +*/ + +#ifndef client_h +#define client_h +#include "Stream.h" +#include "IPAddress.h" + +class Client : public Stream { + +public: + virtual int connect(IPAddress ip, uint16_t port) = 0; + virtual int connect(const char *host, uint16_t port) = 0; + virtual size_t write(uint8_t) = 0; + virtual size_t write(const uint8_t *buf, size_t size) = 0; + virtual int available() = 0; + virtual int read() = 0; + virtual int read(uint8_t *buf, size_t size) = 0; + virtual int peek() = 0; + virtual void flush() = 0; + virtual void stop() = 0; + virtual uint8_t connected() = 0; + virtual operator bool() = 0; +protected: + uint8_t* rawIPAddress(IPAddress& addr) { return addr.raw_address(); }; +}; + +#endif diff --git a/drivers/Linux/EthernetClient.cpp b/drivers/Linux/EthernetClient.cpp new file mode 100644 index 0000000..1777429 --- /dev/null +++ b/drivers/Linux/EthernetClient.cpp @@ -0,0 +1,253 @@ +/* +* The MySensors Arduino library handles the wireless radio link and protocol +* between your home built sensors/actuators and HA controller of choice. +* The sensors forms a self healing radio network with optional repeaters. Each +* repeater and gateway builds a routing tables in EEPROM which keeps track of the +* network topology allowing messages to be routed to nodes. +* +* Created by Marcelo Aquino +* Copyright (C) 2016 Marcelo Aquino +* Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors +* +* Documentation: http://www.mysensors.org +* Support Forum: http://forum.mysensors.org +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* version 2 as published by the Free Software Foundation. +* +* Based on Arduino ethernet library, Copyright (c) 2010 Arduino LLC. All right reserved. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "EthernetClient.h" + +EthernetClient::EthernetClient() : _sock(-1) { +} + +EthernetClient::EthernetClient(int sock) : _sock(sock) { +} + +int EthernetClient::connect(const char* host, uint16_t port) { + int sockfd; + struct addrinfo hints, *servinfo, *p; + int rv; + char s[INET6_ADDRSTRLEN]; + char port_str[6]; + + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + sprintf(port_str, "%hu", port); + if ((rv = getaddrinfo(host, port_str, &hints, &servinfo)) != 0) { + fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv)); + return -1; + } + + // loop through all the results and connect to the first we can + for (p = servinfo; p != NULL; p = p->ai_next) { + if ((sockfd = socket(p->ai_family, p->ai_socktype, + p->ai_protocol)) == -1) { + perror("socket"); + continue; + } + + if (::connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) { + close(sockfd); + perror("connect"); + continue; + } + + break; + } + + if (p == NULL) { + fprintf(stderr, "failed to connect\n"); + return -1; + } + + _sock = sockfd; + + void *addr = &(((struct sockaddr_in*)p->ai_addr)->sin_addr); + inet_ntop(p->ai_family, addr, s, sizeof s); + ETHERNETCLIENT_DEBUG("connected to %s\n", s); + + freeaddrinfo(servinfo); // all done with this structure + + return 1; +} + +int EthernetClient::connect(IPAddress ip, uint16_t port) { + return connect(ip.toString().c_str(), port); +} + +size_t EthernetClient::write(uint8_t b) { + return write(&b, 1); +} + +size_t EthernetClient::write(const uint8_t *buf, size_t size) { + int rc = 0; + int bytes = 0; + + if (_sock == -1) { + return 0; + } + + while (size > 0) { + rc = send(_sock, buf + bytes, size, MSG_NOSIGNAL | MSG_DONTWAIT); + if (rc == -1) { + perror("send"); + close(_sock); + _sock = -1; + break; + } + bytes += rc; + size -= rc; + } + + return bytes; +} + +size_t EthernetClient::write(const char *str) { + if (str == NULL) return 0; + return write((const uint8_t *)str, strlen(str)); +} +size_t EthernetClient::write(const char *buffer, size_t size) { + return write((const uint8_t *)buffer, size); +} + +int EthernetClient::available() { + int count = 0; + + if (_sock != -1) { + ioctl(_sock, FIONREAD, &count); + return count; + } + return 0; +} + +int EthernetClient::read() { + uint8_t b; + if ( recv(_sock, &b, 1, 0) > 0 ) + { + // recv worked + return b; + } else { + // No data available + return -1; + } +} + +int EthernetClient::read(uint8_t *buf, size_t size) { + return recv(_sock, buf, size, 0); +} + +int EthernetClient::peek() { + uint8_t b; + + return recv(_sock, &b, 1, MSG_PEEK | MSG_DONTWAIT); +} + +void EthernetClient::flush() { + // There isn't much we can do here + return; +} + +void EthernetClient::stop() { + if (_sock == -1) + return; + + // attempt to close the connection gracefully (send a FIN to other side) + shutdown(_sock, SHUT_RDWR); + + timeval startTime, curTime; + gettimeofday(&startTime, NULL); + + // wait up to a second for the connection to close + uint8_t s; + do { + s = status(); + if (s == ETHERNETCLIENT_W5100_CLOSED) + break; // exit the loop + usleep(1000); + gettimeofday(&curTime, NULL); + } while (((curTime.tv_sec - startTime.tv_sec) * 1000000) + (curTime.tv_usec - startTime.tv_usec) < 1000000); + + // if it hasn't closed, close it forcefully + if (s != ETHERNETCLIENT_W5100_CLOSED) + close(_sock); + + _sock = -1; +} + +uint8_t EthernetClient::connected() { + if (_sock == -1) return 0; + + if (peek() < 0) { + if (errno == EAGAIN) { + return 1; + } + return 0; + } + return 1; +} + +uint8_t EthernetClient::status() { + if (_sock == -1) return ETHERNETCLIENT_W5100_CLOSED; + + struct tcp_info tcp_info; + int tcp_info_length = sizeof(tcp_info); + + if ( getsockopt( _sock, SOL_TCP, TCP_INFO, (void *)&tcp_info, (socklen_t *)&tcp_info_length ) == 0 ) { + switch (tcp_info.tcpi_state) { + case TCP_ESTABLISHED: + return ETHERNETCLIENT_W5100_ESTABLISHED; + case TCP_SYN_SENT: + return ETHERNETCLIENT_W5100_SYNSENT; + case TCP_SYN_RECV: + return ETHERNETCLIENT_W5100_SYNRECV; + case TCP_FIN_WAIT1: + case TCP_FIN_WAIT2: + return ETHERNETCLIENT_W5100_FIN_WAIT; + case TCP_TIME_WAIT: + return TCP_TIME_WAIT; + case TCP_CLOSE: + return ETHERNETCLIENT_W5100_CLOSED; + case TCP_CLOSE_WAIT: + return ETHERNETCLIENT_W5100_CLOSING; + case TCP_LAST_ACK: + return ETHERNETCLIENT_W5100_LAST_ACK; + case TCP_LISTEN: + return ETHERNETCLIENT_W5100_LISTEN; + case TCP_CLOSING: + return ETHERNETCLIENT_W5100_CLOSING; + } + } else { + perror("getsockopt"); + } + + return ETHERNETCLIENT_W5100_CLOSED; +} + +// the next function allows us to use the client returned by +// EthernetServer::available() as the condition in an if-statement. + +EthernetClient::operator bool() { + return _sock != -1; +} + +bool EthernetClient::operator==(const EthernetClient& rhs) { + return _sock == rhs._sock && _sock != -1 && rhs._sock != -1; +} + +uint8_t EthernetClient::getSocketNumber() { + return _sock; +} diff --git a/drivers/Linux/EthernetClient.h b/drivers/Linux/EthernetClient.h new file mode 100644 index 0000000..b2ad1b1 --- /dev/null +++ b/drivers/Linux/EthernetClient.h @@ -0,0 +1,80 @@ +/* +* The MySensors Arduino library handles the wireless radio link and protocol +* between your home built sensors/actuators and HA controller of choice. +* The sensors forms a self healing radio network with optional repeaters. Each +* repeater and gateway builds a routing tables in EEPROM which keeps track of the +* network topology allowing messages to be routed to nodes. +* +* Created by Marcelo Aquino +* Copyright (C) 2016 Marcelo Aquino +* Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors +* +* Documentation: http://www.mysensors.org +* Support Forum: http://forum.mysensors.org +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* version 2 as published by the Free Software Foundation. +* +* Based on Arduino ethernet library, Copyright (c) 2010 Arduino LLC. All right reserved. +*/ + +#ifndef ethernetclient_h +#define ethernetclient_h + +#include "Client.h" +#include "IPAddress.h" + +// State codes from W5100 library +#define ETHERNETCLIENT_W5100_CLOSED 0x00 +#define ETHERNETCLIENT_W5100_LISTEN 0x14 +#define ETHERNETCLIENT_W5100_SYNSENT 0x15 +#define ETHERNETCLIENT_W5100_SYNRECV 0x16 +#define ETHERNETCLIENT_W5100_ESTABLISHED 0x17 +#define ETHERNETCLIENT_W5100_FIN_WAIT 0x18 +#define ETHERNETCLIENT_W5100_CLOSING 0x1A +#define ETHERNETCLIENT_W5100_TIME_WAIT 0x1B +#define ETHERNETCLIENT_W5100_CLOSE_WAIT 0x1C +#define ETHERNETCLIENT_W5100_LAST_ACK 0x1D + +// debug +#if defined(ETHERNETCLIENT_VERBOSE) + #define ETHERNETCLIENT_DEBUG(x,...) debug(x, ##__VA_ARGS__) +#else + #define ETHERNETCLIENT_DEBUG(x,...) +#endif + +class EthernetClient : public Client { + +public: + EthernetClient(); + EthernetClient(int sock); + + uint8_t status(); + virtual int connect(IPAddress ip, uint16_t port); + virtual int connect(const char *host, uint16_t port); + virtual size_t write(uint8_t); + virtual size_t write(const uint8_t *buf, size_t size); + size_t write(const char *str); + size_t write(const char *buffer, size_t size); + virtual int available(); + virtual int read(); + virtual int read(uint8_t *buf, size_t size); + virtual int peek(); + virtual void flush(); + virtual void stop(); + virtual uint8_t connected(); + virtual operator bool(); + virtual bool operator==(const bool value) { return bool() == value; } + virtual bool operator!=(const bool value) { return bool() != value; } + virtual bool operator==(const EthernetClient&); + virtual bool operator!=(const EthernetClient& rhs) { return !this->operator==(rhs); }; + uint8_t getSocketNumber(); + + friend class EthernetServer; + +private: + int _sock; +}; + +#endif diff --git a/drivers/Linux/EthernetServer.cpp b/drivers/Linux/EthernetServer.cpp new file mode 100644 index 0000000..506eb15 --- /dev/null +++ b/drivers/Linux/EthernetServer.cpp @@ -0,0 +1,241 @@ +/* +* The MySensors Arduino library handles the wireless radio link and protocol +* between your home built sensors/actuators and HA controller of choice. +* The sensors forms a self healing radio network with optional repeaters. Each +* repeater and gateway builds a routing tables in EEPROM which keeps track of the +* network topology allowing messages to be routed to nodes. +* +* Created by Marcelo Aquino +* Copyright (C) 2016 Marcelo Aquino +* Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors +* +* Documentation: http://www.mysensors.org +* Support Forum: http://forum.mysensors.org +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* version 2 as published by the Free Software Foundation. +* +* Based on Arduino ethernet library, Copyright (c) 2010 Arduino LLC. All right reserved. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include "EthernetServer.h" + +struct server_vars { + uint16_t port; + struct IPAddress address; + std::list *new_clients; + std::vector *clients; +}; + +static pthread_mutex_t new_clients_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t clients_mutex = PTHREAD_MUTEX_INITIALIZER; + +void *incoming_connections(void *); + +EthernetServer::EthernetServer(uint16_t port) : port(port) +{ + clients.reserve(MY_GATEWAY_MAX_CLIENTS); +} + +void EthernetServer::begin() +{ + begin(IPAddress(0,0,0,0)); +} + +void EthernetServer::begin(IPAddress address) +{ + pthread_t thread_id; + pthread_attr_t detached_attr; + + struct server_vars *vars = new struct server_vars; + vars->port = port; + vars->address = address; + vars->new_clients = &new_clients; + vars->clients = &clients; + + pthread_attr_init(&detached_attr); + pthread_attr_setdetachstate(&detached_attr, PTHREAD_CREATE_DETACHED); + pthread_create(&thread_id, &detached_attr, &incoming_connections, vars); +} + +void EthernetServer::beginPacket(IPAddress address, uint16_t port) +{ + //TODO: UDP + (void)address; + (void)port; +} + +int EthernetServer::parsePacket() +{ + //TODO: UDP + + return 0; +} + +bool EthernetServer::hasClient() +{ + bool empty; + + pthread_mutex_lock(&new_clients_mutex); + empty = new_clients.empty(); + pthread_mutex_unlock(&new_clients_mutex); + + return !empty; +} + +EthernetClient EthernetServer::available() +{ + int sock; + + pthread_mutex_lock(&new_clients_mutex); + if (new_clients.empty()) { + sock = -1; + } else { + sock = new_clients.front(); + new_clients.pop_front(); + } + pthread_mutex_unlock(&new_clients_mutex); + + return EthernetClient(sock); +} + +size_t EthernetServer::write(uint8_t b) +{ + return write(&b, 1); +} + +size_t EthernetServer::write(const uint8_t *buffer, size_t size) +{ + size_t n = 0; + size_t i = 0; + + pthread_mutex_lock(&clients_mutex); + while (i < clients.size()) { + EthernetClient client(clients[i]); + if (client.connected()) { + n += client.write(buffer, size); + i++; + } else { + clients[i] = clients.back(); + clients.pop_back(); + } + } + pthread_mutex_unlock(&clients_mutex); + + return n; +} + +size_t EthernetServer::write(const char *str) { + if (str == NULL) return 0; + return write((const uint8_t *)str, strlen(str)); +} +size_t EthernetServer::write(const char *buffer, size_t size) { + return write((const uint8_t *)buffer, size); +} + +void *incoming_connections(void* thread_arg) +{ + struct server_vars *vars = (struct server_vars*) thread_arg; + int sockfd, new_fd; + struct addrinfo hints, *servinfo, *p; + struct sockaddr_storage client_addr; + socklen_t sin_size; + int yes=1; + int rv; + char ipstr[INET_ADDRSTRLEN]; + char portstr[6]; + + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; + + sprintf(portstr, "%d", vars->port); + if ((rv = getaddrinfo(vars->address.toString().c_str(), portstr, &hints, &servinfo)) != 0) { + fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv)); + return NULL; + } + + // loop through all the results and bind to the first we can + for (p = servinfo; p != NULL; p = p->ai_next) { + if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) { + perror("socket"); + continue; + } + + if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) { + perror("setsockopt"); + freeaddrinfo(servinfo); + return NULL; + } + + if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) { + close(sockfd); + perror("bind"); + continue; + } + + break; + } + + if (p == NULL) { + fprintf(stderr, "failed to bind\n"); + freeaddrinfo(servinfo); + return NULL; + } + + if (listen(sockfd, ETHERNETSERVER_BACKLOG) == -1) { + perror("listen"); + freeaddrinfo(servinfo); + return NULL; + } + + freeaddrinfo(servinfo); + + struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr; + void *addr = &(ipv4->sin_addr); + inet_ntop(p->ai_family, addr, ipstr, sizeof ipstr); + ETHERNETSERVER_DEBUG("Listening for connections on %s:%s\n", ipstr, portstr); + + while (1) { // accept() loop + sin_size = sizeof client_addr; + new_fd = accept(sockfd, (struct sockaddr *)&client_addr, &sin_size); + if (new_fd == -1) { + perror("accept"); + continue; + } + + void *addr = &(((struct sockaddr_in*)&client_addr)->sin_addr); + inet_ntop(client_addr.ss_family, addr, ipstr, sizeof ipstr); + ETHERNETSERVER_DEBUG("New connection from %s\n", ipstr); + + pthread_mutex_lock(&new_clients_mutex); + pthread_mutex_lock(&clients_mutex); + int connected_clients = vars->new_clients->size() + vars->clients->size(); + + if (connected_clients == MY_GATEWAY_MAX_CLIENTS) { + pthread_mutex_unlock(&clients_mutex); + pthread_mutex_unlock(&new_clients_mutex); + ETHERNETSERVER_DEBUG("Max number of ethernet clients reached."); + close(new_fd); + usleep(5000000); + } else { + vars->new_clients->push_back(new_fd); + vars->clients->push_back(new_fd); + pthread_mutex_unlock(&clients_mutex); + pthread_mutex_unlock(&new_clients_mutex); + } + } + + delete vars; + + return NULL; +} diff --git a/drivers/Linux/EthernetServer.h b/drivers/Linux/EthernetServer.h new file mode 100644 index 0000000..10f1a4d --- /dev/null +++ b/drivers/Linux/EthernetServer.h @@ -0,0 +1,64 @@ +/* +* The MySensors Arduino library handles the wireless radio link and protocol +* between your home built sensors/actuators and HA controller of choice. +* The sensors forms a self healing radio network with optional repeaters. Each +* repeater and gateway builds a routing tables in EEPROM which keeps track of the +* network topology allowing messages to be routed to nodes. +* +* Created by Marcelo Aquino +* Copyright (C) 2016 Marcelo Aquino +* Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors +* +* Documentation: http://www.mysensors.org +* Support Forum: http://forum.mysensors.org +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* version 2 as published by the Free Software Foundation. +* +* Based on Arduino ethernet library, Copyright (c) 2010 Arduino LLC. All right reserved. +*/ + +#ifndef ethernetserver_h +#define ethernetserver_h + +#include +#include +#include "Server.h" +#include "EthernetClient.h" + +#ifndef ETHERNETSERVER_BACKLOG + #define ETHERNETSERVER_BACKLOG 10 +#endif + +// debug +#if defined(ETHERNETSERVER_VERBOSE) + #define ETHERNETSERVER_DEBUG(x,...) debug(x, ##__VA_ARGS__) +#else + #define ETHERNETSERVER_DEBUG(x,...) +#endif + +class EthernetClient; + +class EthernetServer : public Server { + +private: + uint16_t port; + std::list new_clients; + std::vector clients; + +public: + EthernetServer(uint16_t); + virtual void begin(); + void begin(IPAddress); + void beginPacket(IPAddress, uint16_t); + int parsePacket(); + bool hasClient(); + EthernetClient available(); + virtual size_t write(uint8_t); + virtual size_t write(const uint8_t *, size_t); + size_t write(const char *str); + size_t write(const char *buffer, size_t size); +}; + +#endif diff --git a/drivers/Linux/IPAddress.cpp b/drivers/Linux/IPAddress.cpp new file mode 100644 index 0000000..bec1728 --- /dev/null +++ b/drivers/Linux/IPAddress.cpp @@ -0,0 +1,105 @@ +/* + IPAddress.cpp - Base class that provides IPAddress + Copyright (c) 2011 Adrian McEwen. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + + Modified by Marcelo Aquino for MySensors use + */ + +#include +#include "IPAddress.h" + +IPAddress::IPAddress() { + _address.dword = 0; +} + +IPAddress::IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet) { + _address.bytes[0] = first_octet; + _address.bytes[1] = second_octet; + _address.bytes[2] = third_octet; + _address.bytes[3] = fourth_octet; +} + +IPAddress::IPAddress(uint32_t address) { + _address.dword = address; +} + +IPAddress::IPAddress(const uint8_t *address) { + memcpy(_address.bytes, address, sizeof(_address.bytes)); +} + +bool IPAddress::fromString(const char *address) { + // TODO: add support for "a", "a.b", "a.b.c" formats + + uint16_t acc = 0; // Accumulator + uint8_t dots = 0; + + while (*address) + { + char c = *address++; + if (c >= '0' && c <= '9') + { + acc = acc * 10 + (c - '0'); + if (acc > 255) { + // Value out of [0..255] range + return false; + } + } + else if (c == '.') + { + if (dots == 3) { + // Too much dots (there must be 3 dots) + return false; + } + _address.bytes[dots++] = acc; + acc = 0; + } + else + { + // Invalid char + return false; + } + } + + if (dots != 3) { + // Too few dots (there must be 3 dots) + return false; + } + _address.bytes[3] = acc; + return true; +} + +IPAddress& IPAddress::operator=(const uint8_t *address) { + memcpy(_address.bytes, address, sizeof(_address.bytes)); + return *this; +} + +IPAddress& IPAddress::operator=(uint32_t address) { + _address.dword = address; + return *this; +} + +bool IPAddress::operator==(const uint8_t* addr) const { + return memcmp(addr, _address.bytes, sizeof(_address.bytes)) == 0; +} + +std::string IPAddress::toString() +{ + char szRet[16]; + sprintf(szRet,"%u.%u.%u.%u", _address.bytes[0], _address.bytes[1], _address.bytes[2], _address.bytes[3]); + return std::string(szRet); +} diff --git a/drivers/Linux/IPAddress.h b/drivers/Linux/IPAddress.h new file mode 100644 index 0000000..2fb9c6f --- /dev/null +++ b/drivers/Linux/IPAddress.h @@ -0,0 +1,87 @@ +/* + IPAddress.h - Base class that provides IPAddress + Copyright (c) 2011 Adrian McEwen. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + + Modified by Marcelo Aquino for MySensors use + */ + +#ifndef IPAddress_h +#define IPAddress_h + +#include +#include + +// A class to make it easier to handle and pass around IP addresses + +// class IPAddress: public Printable { +class IPAddress { + private: + union { + uint8_t bytes[4]; // IPv4 address + uint32_t dword; + } _address; + + // Access the raw byte array containing the address. Because this returns a pointer + // to the internal structure rather than a copy of the address this function should only + // be used when you know that the usage of the returned uint8_t* will be transient and not + // stored. + uint8_t* raw_address() { + return _address.bytes; + } + + public: + // Constructors + IPAddress(); + IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet); + IPAddress(uint32_t address); + IPAddress(const uint8_t *address); + + bool fromString(const char *address); + bool fromString(const std::string &address) { return fromString(address.c_str()); } + + // Overloaded cast operator to allow IPAddress objects to be used where a pointer + // to a four-byte uint8_t array is expected + operator uint32_t() const { + return _address.dword; + } + bool operator==(const IPAddress& addr) const { + return _address.dword == addr._address.dword; + } + bool operator==(uint32_t addr) const { + return _address.dword == addr; + } + bool operator==(const uint8_t* addr) const; + + // Overloaded index operator to allow getting and setting individual octets of the address + uint8_t operator[](int index) const { + return _address.bytes[index]; + } + uint8_t& operator[](int index) { + return _address.bytes[index]; + } + + // Overloaded copy operators to allow initialisation of IPAddress objects from other types + IPAddress& operator=(const uint8_t *address); + IPAddress& operator=(uint32_t address); + + std::string toString(); + + friend class Client; +}; + +#endif diff --git a/drivers/Linux/Server.h b/drivers/Linux/Server.h new file mode 100644 index 0000000..d1b4046 --- /dev/null +++ b/drivers/Linux/Server.h @@ -0,0 +1,28 @@ +/* + Server.h - Base class that provides Server + Copyright (c) 2011 Adrian McEwen. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef server_h +#define server_h + +class Server { + public: + virtual void begin() =0; +}; + +#endif diff --git a/drivers/Linux/Stream.h b/drivers/Linux/Stream.h new file mode 100644 index 0000000..0241f6a --- /dev/null +++ b/drivers/Linux/Stream.h @@ -0,0 +1,15 @@ +#ifndef Stream_h +#define Stream_h + +class Stream { + public: + virtual size_t write(uint8_t) = 0; + virtual size_t write(const uint8_t *buffer, size_t size) = 0; + + virtual int available() = 0; + virtual int read() = 0; + virtual int peek() = 0; + virtual void flush() = 0; +}; + +#endif diff --git a/drivers/RF24/RF24_RPi.cpp b/drivers/RF24/RF24_RPi.cpp new file mode 100644 index 0000000..3b1a091 --- /dev/null +++ b/drivers/RF24/RF24_RPi.cpp @@ -0,0 +1,210 @@ +/** + * The MySensors Arduino library handles the wireless radio link and protocol + * between your home built sensors/actuators and HA controller of choice. + * The sensors forms a self healing radio network with optional repeaters. Each + * repeater and gateway builds a routing tables in EEPROM which keeps track of the + * network topology allowing messages to be routed to nodes. + * + * Created by Marcelo Aquino + * Copyright (C) 2016 Marcelo Aquino + * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors + * + * Documentation: http://www.mysensors.org + * Support Forum: http://forum.mysensors.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * Based on Olivier Mauti work(RF24.h), copyright (C) 2016 Olivier Mauti + */ + +#include "RF24.h" + +// pipes +#define BROADCAST_PIPE 1 +#define NODE_PIPE 2 + +// debug +#if defined(MY_DEBUG_VERBOSE_RF24) + #define RF24_DEBUG(x,...) debug(x, ##__VA_ARGS__) +#else + #define RF24_DEBUG(x,...) +#endif + +#ifdef MY_RF24_IRQ_PIN + typedef void (*RF24_receiveCallbackType)(void); + + void (* RF24_receiveCallback)(void) = NULL; + + static pthread_mutex_t rf24_mutex = PTHREAD_MUTEX_INITIALIZER; +#endif + +static uint8_t MY_RF24_BASE_ADDR[MY_RF24_ADDR_WIDTH] = { MY_RF24_BASE_RADIO_ID }; +static uint8_t MY_RF24_NODE_ADDRESS = AUTO; + +RF24 _rf24(MY_RF24_CE_PIN, MY_RF24_CS_PIN); + +static void RF24_startListening(void) { + RF24_DEBUG(PSTR("start listening\n")); + + _rf24.startListening(); +} + +static void RF24_powerDown(void) { +#ifdef MY_RF24_IRQ_PIN + pthread_mutex_lock(&rf24_mutex); +#endif + _rf24.powerDown(); +#ifdef MY_RF24_IRQ_PIN + pthread_mutex_unlock(&rf24_mutex); +#endif +} + +static bool RF24_sendMessage( uint8_t recipient, const void* buf, uint8_t len ) { +#ifdef MY_RF24_IRQ_PIN + pthread_mutex_lock(&rf24_mutex); +#endif + // Make sure radio has powered up + _rf24.powerUp(); + _rf24.stopListening(); + + RF24_DEBUG(PSTR("send message to %d, len=%d\n"), recipient,len); + + MY_RF24_BASE_ADDR[0] = recipient; + _rf24.openWritingPipe(MY_RF24_BASE_ADDR); + bool ok = _rf24.write(buf, len, recipient == BROADCAST_ADDRESS); + _rf24.startListening(); +#ifdef MY_RF24_IRQ_PIN + pthread_mutex_unlock(&rf24_mutex); +#endif + return ok; +} + +static bool RF24_isDataAvailable() { +#ifdef MY_RF24_IRQ_PIN + pthread_mutex_lock(&rf24_mutex); +#endif + uint8_t pipe_num = 255; + _rf24.available(&pipe_num); +#ifdef MY_RF24_IRQ_PIN + pthread_mutex_unlock(&rf24_mutex); +#endif + return (pipe_num <= 5); +} + +static uint8_t RF24_readMessage( void* buf) { +#ifdef MY_RF24_IRQ_PIN + pthread_mutex_lock(&rf24_mutex); +#endif + uint8_t len = _rf24.getDynamicPayloadSize(); + _rf24.read(buf, len); +#ifdef MY_RF24_IRQ_PIN + pthread_mutex_unlock(&rf24_mutex); +#endif + return len; +} + +static void RF24_setNodeAddress(uint8_t address) { +#ifdef MY_RF24_IRQ_PIN + pthread_mutex_lock(&rf24_mutex); +#endif + if (address != AUTO){ + MY_RF24_NODE_ADDRESS = address; + // enable node pipe + MY_RF24_BASE_ADDR[0] = MY_RF24_NODE_ADDRESS; + _rf24.openReadingPipe(NODE_PIPE, MY_RF24_BASE_ADDR); + // enable autoACK on node pipe + _rf24.setAutoAck(NODE_PIPE, true); + } +#ifdef MY_RF24_IRQ_PIN + pthread_mutex_unlock(&rf24_mutex); +#endif +} + +static uint8_t RF24_getNodeID(void) { + return MY_RF24_NODE_ADDRESS; +} + +bool RF24_sanityCheck(void) { + bool ok = true; +#ifdef MY_RF24_IRQ_PIN + pthread_mutex_lock(&rf24_mutex); +#endif + // detect HW defect ot interrupted SPI line, CE disconnect cannot be detected + if (_rf24.getPALevel() != MY_RF24_PA_LEVEL || _rf24.getDataRate() != MY_RF24_DATARATE || + _rf24.getChannel() != MY_RF24_CHANNEL) { + ok = false; + } +#ifdef MY_RF24_IRQ_PIN + pthread_mutex_unlock(&rf24_mutex); +#endif + return ok; +} + +#ifdef MY_RF24_IRQ_PIN +void RF24_irqHandler(void) { + if (RF24_receiveCallback) { + while (RF24_isDataAvailable()) { + RF24_receiveCallback(); + } + } else { + // clear RX interrupt + bool tx_ok,tx_fail,rx; + _rf24.whatHappened(tx_ok,tx_fail,rx); + } +} + +void RF24_registerReceiveCallback(RF24_receiveCallbackType cb) { + RF24_receiveCallback = cb; +} +#endif + +static bool RF24_initialize(void) { + // start up the radio library + _rf24.begin(); + // determine whether the hardware is an nRF24L01+ or not + if (!_rf24.isPVariant()) { + RF24_DEBUG(PSTR("radio hardware not compatible")); + return false; + } + // set CRC + _rf24.setCRCLength(RF24_CRC_16); + // set address width + _rf24.setAddressWidth(MY_RF24_ADDR_WIDTH); + // auto retransmit delay 1500us, auto retransmit count 15 + _rf24.setRetries(5, 15); + // channel + _rf24.setChannel(MY_RF24_CHANNEL); + // PA level + _rf24.setPALevel(MY_RF24_PA_LEVEL); + // data rate + _rf24.setDataRate(MY_RF24_DATARATE); + // sanity check + #if defined(MY_RF24_SANITY_CHECK) + if (!RF24_sanityCheck()) { + RF24_DEBUG(PSTR("RF24:Sanity check failed: configuration mismatch! Check wiring, replace module or non-P version\n")); + return false; + } + #endif + // enable Dynamic payload + _rf24.enableDynamicPayloads(); + // enable ACK payload + _rf24.enableAckPayload(); + // disable AA on all pipes, activate when node pipe set + _rf24.setAutoAck(false); + // all nodes listen to broadcast pipe (for FIND_PARENT_RESPONSE messages) + MY_RF24_BASE_ADDR[0] = BROADCAST_ADDRESS; + _rf24.openReadingPipe(BROADCAST_PIPE, MY_RF24_BASE_ADDR); + + //_rf24.printDetails(); + + #ifdef MY_RF24_IRQ_PIN + // Mask all interrupts except the receive interrupt + _rf24.maskIRQ(1,1,0); + + attachInterrupt(MY_RF24_IRQ_PIN, INT_EDGE_FALLING, RF24_irqHandler); + #endif + + return true; +} diff --git a/examples_RPi/MySGateway.cpp b/examples_RPi/MySGateway.cpp new file mode 100644 index 0000000..014d12a --- /dev/null +++ b/examples_RPi/MySGateway.cpp @@ -0,0 +1,93 @@ +/** + * The MySensors Arduino library handles the wireless radio link and protocol + * between your home built sensors/actuators and HA controller of choice. + * The sensors forms a self healing radio network with optional repeaters. Each + * repeater and gateway builds a routing tables in EEPROM which keeps track of the + * network topology allowing messages to be routed to nodes. + * + * Created by Marcelo Aquino + * Copyleft (c) 2016, Marcelo Aquino + * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors + * + * Documentation: http://www.mysensors.org + * Support Forum: http://forum.mysensors.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + */ + +#include +#include +#include + +// Enable debug prints to monitor +#ifndef MY_DEBUG + #define MY_DEBUG +#endif + +// Config file +#ifndef MY_LINUX_CONFIG_FILE + #define MY_LINUX_CONFIG_FILE "/etc/MySensorGateway.cfg" +#endif + +// Enables and select radio type (if attached) +#ifndef MY_RADIO_NRF24 + #define MY_RADIO_NRF24 +#endif + +#define MY_GATEWAY_RASPBERRYPI + +// How many clients should be able to connect to this gateway (default 1) +#define MY_GATEWAY_MAX_CLIENTS 2 + +#ifdef MY_GATEWAY_MQTT_CLIENT + // Set MQTT client id + #ifndef MY_MQTT_CLIENT_ID + #define MY_MQTT_CLIENT_ID "mysensors-1" + #endif + // MQTT broker ip address. + #ifndef MY_CONTROLLER_IP_ADDRESS + #define MY_CONTROLLER_IP_ADDRESS 192, 168, 178, 68 + #endif + // The MQTT broker port to to open + #ifndef MY_PORT + #define MY_PORT 1883 + #endif + + // Enable these if your MQTT broker requires usenrame/password + //#define MY_MQTT_USER "username" + //#define MY_MQTT_PASSWORD "password" + + // Set this node's subscribe and publish topic prefix + #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 +#else + // Enable MY_IP_ADDRESS here if you want to listen for incoming network + // connections on the specified IP address/hostname only + //#define MY_IP_ADDRESS 192,168,178,87 + + // The port to keep open on node server mode + #ifndef MY_PORT + #define MY_PORT 5003 + #endif +#endif + +#include + +using namespace std; + +void setup() { +} + +void presentation() { + // Present locally attached sensors here +} + +void loop() { + // Send locally attached sensors data here +} diff --git a/initscripts/mysgateway.systemd b/initscripts/mysgateway.systemd new file mode 100644 index 0000000..9763c66 --- /dev/null +++ b/initscripts/mysgateway.systemd @@ -0,0 +1,9 @@ +[Unit] +Description=MySensors Gateway daemon +Requires=network.target + +[Service] +ExecStart=%gateway_dir%/MySGateway + +[Install] +WantedBy=multi-user.target diff --git a/initscripts/mysgateway.sysvinit b/initscripts/mysgateway.sysvinit new file mode 100644 index 0000000..df01c6c --- /dev/null +++ b/initscripts/mysgateway.sysvinit @@ -0,0 +1,159 @@ +#! /bin/sh +### BEGIN INIT INFO +# Provides: MySGateway +# Required-Start: $remote_fs $syslog +# Required-Stop: $remote_fs $syslog +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: MySensors Gateway +# Description: This file should be used to construct scripts to be +# placed in /etc/init.d. +### END INIT INFO + +# Author: Foo Bar +# +# Please remove the "Author" lines above and replace them +# with your own name if you copy and modify this script. + +# Do NOT "set -e" + +# PATH should only include /usr/* if it runs after the mountnfs.sh script +PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/sbin:/usr/local/bin +DESC="MySensors Gateway" +NAME=MySGateway +DAEMON=%gateway_dir%/$NAME +DAEMON_ARGS="-d" +PIDFILE=/var/run/$NAME.pid +SCRIPTNAME=/etc/init.d/$NAME + +# Exit if the package is not installed +[ -x "$DAEMON" ] || exit 0 + +# Read configuration variable file if it is present +[ -r /etc/default/$NAME ] && . /etc/default/$NAME + +# Load the VERBOSE setting and other rcS variables +. /lib/init/vars.sh + +# Define LSB log_* functions. +# Depend on lsb-base (>= 3.2-14) to ensure that this file is present +# and status_of_proc is working. +. /lib/lsb/init-functions + +# +# Function that starts the daemon/service +# +do_start() +{ + # Return + # 0 if daemon has been started + # 1 if daemon was already running + # 2 if daemon could not be started + start-stop-daemon --start --quiet --exec $DAEMON --test > /dev/null \ + || return 1 + start-stop-daemon --start --quiet --exec $DAEMON -- \ + $DAEMON_ARGS \ + || return 2 + # Add code here, if necessary, that waits for the process to be ready + # to handle requests from services started subsequently which depend + # on this one. As a last resort, sleep for some time. +} + +# +# Function that stops the daemon/service +# +do_stop() +{ + # Return + # 0 if daemon has been stopped + # 1 if daemon was already stopped + # 2 if daemon could not be stopped + # other if a failure occurred + start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --name $NAME + RETVAL="$?" + [ "$RETVAL" = 2 ] && return 2 + # Wait for children to finish too if this is a daemon that forks + # and if the daemon is only ever run from this initscript. + # If the above conditions are not satisfied then add some other code + # that waits for the process to drop all resources that could be + # needed by services started subsequently. A last resort is to + # sleep for some time. + start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON + [ "$?" = 2 ] && return 2 + # Many daemons don't delete their pidfiles when they exit. + rm -f $PIDFILE + return "$RETVAL" +} + +# +# Function that sends a SIGHUP to the daemon/service +# +do_reload() { + # + # If the daemon can reload its configuration without + # restarting (for example, when it is sent a SIGHUP), + # then implement that here. + # + start-stop-daemon --stop --signal 1 --quiet --name $NAME + return 0 +} + +case "$1" in + start) + [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME" + do_start + case "$?" in + 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; + 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; + esac + ;; + stop) + [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME" + do_stop + case "$?" in + 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; + 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; + esac + ;; + status) + status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $? + ;; + #reload|force-reload) + # + # If do_reload() is not implemented then leave this commented out + # and leave 'force-reload' as an alias for 'restart'. + # + #log_daemon_msg "Reloading $DESC" "$NAME" + #do_reload + #log_end_msg $? + #;; + restart|force-reload) + # + # If the "reload" option is implemented then remove the + # 'force-reload' alias + # + log_daemon_msg "Restarting $DESC" "$NAME" + do_stop + case "$?" in + 0|1) + do_start + case "$?" in + 0) log_end_msg 0 ;; + 1) log_end_msg 1 ;; # Old process is still running + *) log_end_msg 1 ;; # Failed to start + esac + ;; + *) + # Failed to stop + log_end_msg 1 + ;; + esac + ;; + *) + #echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2 + echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2 + exit 3 + ;; +esac + +: