Merge pull request #537 from marceloaqno/dev-raspberrypi
Add support for RaspberryPi
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
#############################################################################
|
||||
#
|
||||
# Makefile for MySensors
|
||||
#
|
||||
# Description:
|
||||
# ------------
|
||||
# use make all and make install to install the gateway
|
||||
#
|
||||
|
||||
CONFIG_FILE=Makefile.inc
|
||||
|
||||
include $(CONFIG_FILE)
|
||||
|
||||
GATEWAY_BIN=mysGateway
|
||||
GATEWAY=examples_linux/$(GATEWAY_BIN)
|
||||
GATEWAY_C_SOURCES=$(wildcard drivers/Linux/*.c)
|
||||
GATEWAY_CPP_SOURCES=$(wildcard drivers/Linux/*.cpp) examples_linux/mysGateway.cpp
|
||||
OBJECTS=$(patsubst %.c,%.o,$(GATEWAY_C_SOURCES)) $(patsubst %.cpp,%.o,$(GATEWAY_CPP_SOURCES))
|
||||
|
||||
DEPS+=$(patsubst %.c,%.d,$(GATEWAY_C_SOURCES)) $(patsubst %.cpp,%.d,$(GATEWAY_CPP_SOURCES))
|
||||
|
||||
CINCLUDE=-I. -I./core -I./drivers/Linux
|
||||
|
||||
ifeq ($(SOC),$(filter $(SOC),BCM2835 BCM2836))
|
||||
RPI_C_SOURCES=$(wildcard drivers/RPi/*.c)
|
||||
RPI_CPP_SOURCES=$(wildcard drivers/RPi/*.cpp)
|
||||
OBJECTS+=$(patsubst %.c,%.o,$(RPI_C_SOURCES)) $(patsubst %.cpp,%.o,$(RPI_CPP_SOURCES))
|
||||
|
||||
DEPS+=$(patsubst %.c,%.d,$(RPI_C_SOURCES)) $(patsubst %.cpp,%.d,$(RPI_CPP_SOURCES))
|
||||
|
||||
CINCLUDE+=-I./drivers/RPi
|
||||
endif
|
||||
|
||||
.PHONY: all gateway cleanconfig clean install uninstall force
|
||||
|
||||
all: $(GATEWAY)
|
||||
|
||||
# Gateway Build
|
||||
$(GATEWAY): $(OBJECTS)
|
||||
$(CXX) $(LDFLAGS) -o $@ $(OBJECTS)
|
||||
|
||||
# Include all .d files
|
||||
-include $(DEPS)
|
||||
|
||||
%.o: %.cpp
|
||||
$(CXX) $(CXXFLAGS) $(CINCLUDE) -MMD -c -o $@ $<
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) $(CINCLUDE) -MMD -c -o $@ $<
|
||||
|
||||
# clear configuration files
|
||||
cleanconfig:
|
||||
@echo "[Cleaning configuration]"
|
||||
rm -rf $(CONFIG_FILE)
|
||||
|
||||
# clear build files
|
||||
clean:
|
||||
@echo "[Cleaning]"
|
||||
rm -rf build $(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:
|
||||
ifeq ($(INIT_SYSTEM), systemd)
|
||||
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
|
||||
@echo "MySensors gateway has been installed, to add to the boot run:"
|
||||
@echo " sudo systemctl enable mysgateway.service"
|
||||
@echo "To start the gateway run:"
|
||||
@echo " sudo systemctl start mysgateway.service"
|
||||
else ifeq ($(INIT_SYSTEM), sysvinit)
|
||||
install -m0755 initscripts/mysgateway.sysvinit /etc/init.d/mysgateway
|
||||
@sed -i -e "s|%gateway_dir%|${GATEWAY_DIR}|g" /etc/init.d/mysgateway
|
||||
@echo "MySensors gateway has been installed, to add to the boot run:"
|
||||
@echo " sudo update-rc.d mysgateway defaults"
|
||||
@echo "To start the gateway run:"
|
||||
@echo " sudo service mysgateway start"
|
||||
endif
|
||||
|
||||
uninstall:
|
||||
ifeq ($(INIT_SYSTEM), systemd)
|
||||
@echo "Stopping daemon mysgateway (ignore errors)"
|
||||
-@systemctl stop mysgateway.service
|
||||
@echo "removing files"
|
||||
rm /etc/systemd/system/mysgateway.service $(GATEWAY_DIR)/$(GATEWAY_BIN)
|
||||
else ifeq ($(INIT_SYSTEM), sysvinit)
|
||||
@echo "Stopping daemon mysgateway (ignore errors)"
|
||||
-@service mysgateway stop
|
||||
@echo "removing files"
|
||||
rm /etc/init.d/mysgateway $(GATEWAY_DIR)/$(GATEWAY_BIN)
|
||||
endif
|
||||
+49
-1
@@ -415,6 +415,8 @@
|
||||
#define MY_RF24_CE_PIN 4
|
||||
#elif defined(ARDUINO_ARCH_SAMD)
|
||||
#define MY_RF24_CE_PIN 27
|
||||
#elif defined(LINUX_ARCH_RASPBERRYPI)
|
||||
#define MY_RF24_CE_PIN 22
|
||||
#else
|
||||
#define MY_RF24_CE_PIN 9
|
||||
#endif
|
||||
@@ -429,6 +431,8 @@
|
||||
#define MY_RF24_CS_PIN 15
|
||||
#elif defined(ARDUINO_ARCH_SAMD)
|
||||
#define MY_RF24_CS_PIN 3
|
||||
#elif defined(LINUX_ARCH_RASPBERRYPI)
|
||||
#define MY_RF24_CS_PIN 24
|
||||
#else
|
||||
#define MY_RF24_CS_PIN 10
|
||||
#endif
|
||||
@@ -610,6 +614,7 @@
|
||||
//#define MY_GATEWAY_W5100
|
||||
//#define MY_GATEWAY_ENC28J60
|
||||
//#define MY_GATEWAY_ESP8266
|
||||
//#define MY_GATEWAY_LINUX
|
||||
|
||||
/**
|
||||
* @def MY_PORT
|
||||
@@ -727,7 +732,49 @@
|
||||
#define MY_ESP8266_SERIAL_MODE SERIAL_FULL
|
||||
#endif
|
||||
|
||||
/** @}*/ // ESP8266 Defaults
|
||||
/**************************************
|
||||
* Linux Settings
|
||||
***************************************/
|
||||
|
||||
/**
|
||||
* @def MY_LINUX_SERIAL_PORT
|
||||
* @brief Serial device port
|
||||
*/
|
||||
#ifndef MY_LINUX_SERIAL_PORT
|
||||
#define MY_LINUX_SERIAL_PORT "/dev/ttyACM0"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @def MY_IS_SERIAL_PTY
|
||||
* @brief Set serial as a pseudo terminal.
|
||||
*
|
||||
* Enable this if you need to connect to a controller running on the same device.
|
||||
*/
|
||||
//#define MY_IS_SERIAL_PTY
|
||||
|
||||
/**
|
||||
* @def MY_LINUX_SERIAL_PTY
|
||||
* @brief Symlink name for the PTY device.
|
||||
*/
|
||||
#ifndef MY_LINUX_SERIAL_PTY
|
||||
#define MY_LINUX_SERIAL_PTY "/dev/ttyMySensorsGateway"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @def MY_LINUX_SERIAL_GROUPNAME
|
||||
* @brief Grant access to the specified system group for the serial device.
|
||||
*/
|
||||
//#define MY_LINUX_SERIAL_GROUPNAME "tty"
|
||||
|
||||
/**
|
||||
* @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/mysensors.dat"
|
||||
#endif
|
||||
|
||||
#endif // MyConfig_h
|
||||
|
||||
@@ -747,4 +794,5 @@
|
||||
#define MY_RX_MESSAGE_BUFFER_FEATURE
|
||||
#define MY_RX_MESSAGE_BUFFER_SIZE
|
||||
#define MY_NODE_LOCK_FEATURE
|
||||
#define MY_LINUX_SERIAL_GROUPNAME
|
||||
#endif
|
||||
|
||||
+42
-5
@@ -40,7 +40,7 @@
|
||||
* @def MY_NODE_TYPE
|
||||
* @brief Contain a string describing the class of sketch/node (gateway/repeater/sensor).
|
||||
*/
|
||||
#if defined(MY_GATEWAY_SERIAL) || defined(MY_GATEWAY_W5100) || defined(MY_GATEWAY_ENC28J60) || defined(MY_GATEWAY_ESP8266) || defined(MY_GATEWAY_MQTT_CLIENT)
|
||||
#if defined(MY_GATEWAY_SERIAL) || defined(MY_GATEWAY_W5100) || defined(MY_GATEWAY_ENC28J60) || defined(MY_GATEWAY_ESP8266) || defined(MY_GATEWAY_LINUX) || defined(MY_GATEWAY_MQTT_CLIENT)
|
||||
#define MY_GATEWAY_FEATURE
|
||||
#define MY_IS_GATEWAY (true)
|
||||
#define MY_NODE_TYPE "GW"
|
||||
@@ -69,6 +69,24 @@
|
||||
#include "core/MyHwATMega328.cpp"
|
||||
#elif defined(ARDUINO_ARCH_SAMD)
|
||||
#include "core/MyHwSAMD.cpp"
|
||||
#elif defined(__linux__)
|
||||
// Remove PSTR macros from debug prints
|
||||
#undef PSTR
|
||||
#define PSTR(x) (x)
|
||||
//#undef F
|
||||
//#define F(x) (x)
|
||||
#define PROGMEM
|
||||
#define vsnprintf_P(...) vsnprintf( __VA_ARGS__ )
|
||||
#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"
|
||||
// Ugly hack
|
||||
#ifdef LINUX_ARCH_RASPBERRYPI
|
||||
#undef hwDigitalWrite
|
||||
#define hwDigitalWrite(__pin, __value) (digitalWrite(__pin, __value))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// LEDS
|
||||
@@ -154,7 +172,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_GATEWAY_CLIENT_MODE)
|
||||
#error You must specify MY_CONTROLLER_IP_ADDRESS or MY_CONTROLLER_URL_ADDRESS
|
||||
#endif
|
||||
@@ -165,18 +183,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.h"
|
||||
#include "drivers/Linux/EthernetServer.h"
|
||||
#include "drivers/Linux/IPAddress.h"
|
||||
#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
|
||||
@@ -190,6 +215,12 @@
|
||||
#if defined(MY_GATEWAY_ESP8266)
|
||||
// GATEWAY - ESP8266
|
||||
#include "core/MyGatewayTransportEthernet.cpp"
|
||||
#elif defined(MY_GATEWAY_LINUX)
|
||||
// GATEWAY - Generic Linux
|
||||
#include "drivers/Linux/EthernetClient.h"
|
||||
#include "drivers/Linux/EthernetServer.h"
|
||||
#include "drivers/Linux/IPAddress.h"
|
||||
#include "core/MyGatewayTransportEthernet.cpp"
|
||||
#elif defined(MY_GATEWAY_W5100)
|
||||
// GATEWAY - W5100
|
||||
#include "core/MyGatewayTransportEthernet.cpp"
|
||||
@@ -216,6 +247,10 @@
|
||||
#include "drivers/AVR/DigitalIO/DigitalIO.h"
|
||||
#endif
|
||||
|
||||
#if defined(MY_RADIO_NRF24) && defined(__linux__) && !defined(LINUX_ARCH_RASPBERRYPI)
|
||||
#error No support for nRF24 radio on this platform
|
||||
#endif
|
||||
|
||||
#include "core/MyTransport.cpp"
|
||||
#if (defined(MY_RADIO_NRF24) && defined(MY_RADIO_RFM69)) || (defined(MY_RADIO_NRF24) && defined(MY_RS485)) || (defined(MY_RADIO_RFM69) && defined(MY_RS485))
|
||||
#error Only one forward link driver can be activated
|
||||
@@ -267,6 +302,8 @@
|
||||
#if !defined(MY_CORE_ONLY)
|
||||
#if defined(ARDUINO_ARCH_ESP8266)
|
||||
#include "core/MyMainESP8266.cpp"
|
||||
#elif defined(__linux__)
|
||||
#include "core/MyMainLinux.cpp"
|
||||
#else
|
||||
#include "core/MyMainDefault.cpp"
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,377 @@
|
||||
#!/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 <<EOF
|
||||
configure script for MySensors gateway.
|
||||
Options:
|
||||
|
||||
Help:
|
||||
-h, --help print this message
|
||||
|
||||
Building options:
|
||||
--soc=[BCM2835|BCM2836|AM33XX|A10|A13|A20|H3]
|
||||
SoC type to be used. [configure autodetected]
|
||||
--cpu-flags=<CXXFLAGS> CPU defining/optimizing flags to be used. [configure autodetected]
|
||||
--extra-cxxflags=<CXXFLAGS> Extra C flags passed to C/C++ compilation. []
|
||||
--extra-ldflags=<LDFLAGS> Extra C flags passed to linking. []
|
||||
--cxx_compiler=<CXX> C++ compiler [arm-linux-gnueabihf-g++][g++]
|
||||
--no-clean Don't clean previous build artifacts.
|
||||
|
||||
Installation options:
|
||||
--prefix=<PREFIX> Installation prefix path. [/usr/local]
|
||||
--gateway-dir=<DIR> Gateway files installation directory. [PREFIX/bin]
|
||||
|
||||
MySensors options:
|
||||
--my-debug=[enable|disable] Enables or disables MySensors core debugging. [enable]
|
||||
--my-config-file=<FILE> Config file path. [/etc/mysensors.dat]
|
||||
--my-gateway=[none|ethernet|serial|mqtt]
|
||||
Gateway type, set to none to disable gateway feature. [ethernet]
|
||||
--my-controller-url-address=<URL>
|
||||
Controller or MQTT broker url.
|
||||
--my-controller-ip-address=<IP>
|
||||
Controller or MQTT broker ip.
|
||||
--my-port=<PORT> The port to keep open on gateway mode.
|
||||
If gateway is set to mqtt, it sets the broker port.
|
||||
--my-serial-port=<PORT> Serial port. [/dev/ttyACM0]
|
||||
--my-serial-baudrate=<BAUD> Serial baud rate. [115200]
|
||||
--my-serial-is-pty Set the serial port to be a pseudo terminal. Use this if you want
|
||||
to connect to a controller running on the same device.
|
||||
--my-serial-pty=<NAME> Symlink name for the PTY device. [/dev/ttyMySensorsGateway]
|
||||
--my-serial-groupname=<GROUP>
|
||||
Grant access to the specified system group for the serial device.
|
||||
--my-mqtt-client-id=<ID> MQTT client id.
|
||||
--my-mqtt-publish-topic-prefix=<PREFIX>
|
||||
MQTT publish topic prefix.
|
||||
--my-mqtt-subscribe-topic-prefix=<PREFIX>
|
||||
MQTT subscribe topic prefix.
|
||||
--my-radio=[none|nrf24] Radio type, set to none to disable radio feature. [rf24]
|
||||
--my-rf24-channel=<0-125> RF channel for the sensor net, 0-125. [76]
|
||||
--my-rf24-pa-level=[RF24_PA_MAX|RF24_PA_LOW]
|
||||
RF24 PA level. [RF24_PA_MAX]
|
||||
--my-rf24-irq-pin=<PIN> Pin number connected to nRF24L01 IRQ pin.
|
||||
--my-rx-message-buffer-size=<SIZE>
|
||||
Buffer size for incoming messages when using rf24 interrupts. [20]
|
||||
--my-leds-err-pin=<PIN> Error LED pin.
|
||||
--my-leds-rx-pin=<PIN> Receive LED pin.
|
||||
--my-leds-tx-pin=<PIN> Transmit LED pin.
|
||||
--my-leds-blinking-inverse Inverse the blinking feature.
|
||||
|
||||
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 -DLINUX_ARCH_RASPBERRYPI"
|
||||
;;
|
||||
BCM2836)
|
||||
flags="-march=armv7-a -mtune=cortex-a7 -mfpu=neon-vfpv4 -mfloat-abi=hard -DLINUX_ARCH_RASPBERRYPI"
|
||||
;;
|
||||
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}
|
||||
}
|
||||
|
||||
# Default values
|
||||
debug=enable
|
||||
gateway_type=ethernet
|
||||
radio_type=nrf24
|
||||
|
||||
params="SOC CXXFLAGS LDFLAGS PREFIX CXX GATEWAY_DIR INIT_SYSTEM"
|
||||
|
||||
for opt do
|
||||
if [ "$opt" = "-h" ] || [ "$opt" = "--help" ]; then
|
||||
help
|
||||
exit 0
|
||||
fi
|
||||
optarg="${opt#*=}"
|
||||
case "$opt" in
|
||||
--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"
|
||||
;;
|
||||
--gateway-dir=*)
|
||||
GATEWAY_DIR="$optarg"
|
||||
;;
|
||||
--my-debug=*)
|
||||
debug=${optarg}
|
||||
;;
|
||||
--my-gateway=*)
|
||||
gateway_type=${optarg}
|
||||
;;
|
||||
--my-config-file=*)
|
||||
CXXFLAGS="-DMY_LINUX_CONFIG_FILE=\\\"${optarg}\\\" $CXXFLAGS"
|
||||
;;
|
||||
--my-radio=*)
|
||||
radio_type=${optarg}
|
||||
;;
|
||||
--my-serial-port=*)
|
||||
CXXFLAGS="-DMY_LINUX_SERIAL_PORT=\\\"${optarg}\\\" $CXXFLAGS"
|
||||
;;
|
||||
--my-serial-baudrate=*)
|
||||
CXXFLAGS="-DMY_BAUD_RATE=${optarg} $CXXFLAGS"
|
||||
;;
|
||||
--my-serial-is-pty*)
|
||||
CXXFLAGS="-DMY_IS_SERIAL_PTY $CXXFLAGS"
|
||||
;;
|
||||
--my-serial-pty=*)
|
||||
CXXFLAGS="-DMY_LINUX_SERIAL_PTY=\\\"${optarg}\\\" $CXXFLAGS"
|
||||
;;
|
||||
--my-serial-groupname=*)
|
||||
CXXFLAGS="-DMY_LINUX_SERIAL_GROUPNAME=\\\"${optarg}\\\" $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=*)
|
||||
controller_ip=`echo ${optarg//./,}`
|
||||
CXXFLAGS="-DMY_CONTROLLER_IP_ADDRESS=${controller_ip} $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_RX_MESSAGE_BUFFER_FEATURE -DMY_RF24_IRQ_PIN=${optarg} $CXXFLAGS"
|
||||
;;
|
||||
--my-rx-message-buffer-size=*)
|
||||
CXXFLAGS="-DMY_RX_MESSAGE_BUFFER_SIZE=${optarg} $CXXFLAGS"
|
||||
;;
|
||||
--my-leds-err-pin=*)
|
||||
CXXFLAGS="-DMY_DEFAULT_ERR_LED_PIN=${optarg} $CXXFLAGS"
|
||||
;;
|
||||
--my-leds-rx-pin=*)
|
||||
CXXFLAGS="-DMY_DEFAULT_RX_LED_PIN=${optarg} $CXXFLAGS"
|
||||
;;
|
||||
--my-leds-tx-pin=*)
|
||||
CXXFLAGS="-DMY_DEFAULT_TX_LED_PIN=${optarg} $CXXFLAGS"
|
||||
;;
|
||||
--my-leds-blinking-inverse*)
|
||||
CXXFLAGS="-DMY_WITH_LEDS_BLINKING_INVERSE $CXXFLAGS"
|
||||
;;
|
||||
*)
|
||||
echo "[WARNING] Unknown option detected:$opt, ignored"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
PREFIX=${PREFIX:-/usr/local}
|
||||
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 $CXXFLAGS"
|
||||
CFLAGS="-Ofast -g -Wall -Wextra $CFLAGS"
|
||||
|
||||
if [[ $TYPE == "RPi2" || $TYPE == "RPi3" || $REV == "0010" ]]; then
|
||||
CXXFLAGS+="-D__RPI_BPLUS"
|
||||
fi
|
||||
|
||||
if [[ ${debug} == "enable" ]]; then
|
||||
CXXFLAGS="-DMY_DEBUG $CXXFLAGS"
|
||||
fi
|
||||
|
||||
if [[ ${gateway_type} == "ethernet" ]]; then
|
||||
CXXFLAGS="-DMY_GATEWAY_LINUX $CXXFLAGS"
|
||||
elif [[ ${gateway_type} == "serial" ]]; then
|
||||
CXXFLAGS="-DMY_GATEWAY_SERIAL $CXXFLAGS"
|
||||
elif [[ ${gateway_type} == "mqtt" ]]; then
|
||||
CXXFLAGS="-DMY_GATEWAY_LINUX -DMY_GATEWAY_MQTT_CLIENT $CXXFLAGS"
|
||||
fi
|
||||
|
||||
if [[ ${radio_type} == "nrf24" ]]; then
|
||||
CXXFLAGS="-DMY_RADIO_NRF24 $CXXFLAGS"
|
||||
fi
|
||||
|
||||
LDFLAGS="-pthread $LDFLAGS"
|
||||
|
||||
if [ -x /usr/bin/systemctl ] || [ -x /bin/systemctl ]; then
|
||||
INIT_SYSTEM=systemd
|
||||
echo "[OK] init system detected: systemd"
|
||||
elif [ -f /etc/init.d/cron ] && [ ! -h /etc/init.d/cron ]; then
|
||||
INIT_SYSTEM=sysvinit
|
||||
echo "[OK] init system detected: sysvinit"
|
||||
else
|
||||
echo "[FAILED] unknown init system"
|
||||
fi
|
||||
|
||||
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."
|
||||
@@ -22,7 +22,6 @@
|
||||
// global variables
|
||||
extern MyMessage _msgTmp;
|
||||
|
||||
|
||||
#if defined(MY_CONTROLLER_IP_ADDRESS)
|
||||
IPAddress _ethernetControllerIP(MY_CONTROLLER_IP_ADDRESS);
|
||||
#endif
|
||||
@@ -30,7 +29,7 @@ extern MyMessage _msgTmp;
|
||||
#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;
|
||||
|
||||
@@ -38,8 +37,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)
|
||||
@@ -52,27 +51,30 @@ typedef struct
|
||||
IPAddress _gatewayIp(MY_IP_GATEWAY_ADDRESS);
|
||||
IPAddress _subnetIp(MY_IP_SUBNET_ADDRESS);
|
||||
#endif
|
||||
#if !defined(MY_GATEWAY_CLIENT_MODE)
|
||||
static bool clientsConnected[MY_GATEWAY_MAX_CLIENTS];
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(MY_USE_UDP)
|
||||
EthernetUDP _ethernetServer;
|
||||
#elif !defined(MY_GATEWAY_CLIENT_MODE)
|
||||
#elif defined(MY_GATEWAY_LINUX)
|
||||
EthernetServer _ethernetServer(_ethernetGatewayPort, MY_GATEWAY_MAX_CLIENTS);
|
||||
#elif defined(MY_GATEWAY_CLIENT_MODE)
|
||||
// Nothing to do here
|
||||
#else
|
||||
EthernetServer _ethernetServer(_ethernetGatewayPort);
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(MY_GATEWAY_ESP8266) && !defined(MY_GATEWAY_CLIENT_MODE)
|
||||
#if defined(MY_GATEWAY_CLIENT_MODE)
|
||||
static EthernetClient client = EthernetClient();
|
||||
static inputBuffer inputString;
|
||||
#elif 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();
|
||||
static inputBuffer inputString;
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef MY_IP_ADDRESS
|
||||
void gatewayTransportRenewIP();
|
||||
#endif
|
||||
@@ -82,25 +84,23 @@ 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)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
bool gatewayTransportInit() {
|
||||
_w5100_spi_en(true);
|
||||
#if defined(MY_GATEWAY_ESP8266)
|
||||
@@ -123,7 +123,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);
|
||||
@@ -145,33 +146,37 @@ bool gatewayTransportInit() {
|
||||
_ethernetServer.begin(_ethernetGatewayPort);
|
||||
#elif defined(MY_GATEWAY_CLIENT_MODE)
|
||||
#if defined(MY_CONTROLLER_URL_ADDRESS)
|
||||
if (!client.connect(MY_CONTROLLER_URL_ADDRESS, MY_PORT)) {
|
||||
if (client.connect(MY_CONTROLLER_URL_ADDRESS, MY_PORT)) {
|
||||
#else
|
||||
if (!client.connect(_ethernetControllerIP, MY_PORT)) {
|
||||
if (client.connect(_ethernetControllerIP, MY_PORT)) {
|
||||
#endif
|
||||
client.stop();
|
||||
debug(PSTR("Eth: Failed to connect\n"));
|
||||
} else {
|
||||
debug(PSTR("Eth: connect\n"));
|
||||
_w5100_spi_en(false);
|
||||
gatewayTransportSend(buildGw(_msgTmp, I_GATEWAY_READY).set("Gateway startup complete."));
|
||||
_w5100_spi_en(true);
|
||||
presentNode();
|
||||
} else {
|
||||
client.stop();
|
||||
debug(PSTR("Eth: Failed to connect\n"));
|
||||
}
|
||||
#else
|
||||
// we have to use pointers due to the constructor of EthernetServer
|
||||
_ethernetServer.begin();
|
||||
#endif
|
||||
#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;
|
||||
}
|
||||
|
||||
bool gatewayTransportSend(MyMessage &message)
|
||||
{
|
||||
bool ret = true;
|
||||
int nbytes = 0;
|
||||
char *_ethernetMsg = protocolFormat(message);
|
||||
|
||||
setIndication(INDICATION_GW_TX);
|
||||
setIndication(INDICATION_GW_TX);
|
||||
|
||||
_w5100_spi_en(true);
|
||||
#if defined(MY_GATEWAY_CLIENT_MODE)
|
||||
@@ -183,28 +188,28 @@ bool gatewayTransportSend(MyMessage &message)
|
||||
#endif
|
||||
_ethernetServer.write(_ethernetMsg, strlen(_ethernetMsg));
|
||||
// returns 1 if the packet was sent successfully
|
||||
ret = _ethernetServer.endPacket();
|
||||
nbytes = _ethernetServer.endPacket();
|
||||
#else
|
||||
if (!client.connected()) {
|
||||
client.stop();
|
||||
#if defined(MY_CONTROLLER_URL_ADDRESS)
|
||||
if (!client.connect(MY_CONTROLLER_URL_ADDRESS, MY_PORT)) {
|
||||
if (client.connect(MY_CONTROLLER_URL_ADDRESS, MY_PORT)) {
|
||||
#else
|
||||
if (!client.connect(_ethernetControllerIP, MY_PORT)) {
|
||||
if (client.connect(_ethernetControllerIP, MY_PORT)) {
|
||||
#endif
|
||||
// connecting to the server failed!
|
||||
debug(PSTR("Eth: Failed to connect\n"));
|
||||
ret = false;
|
||||
} else {
|
||||
debug(PSTR("Eth: connect\n"));
|
||||
_w5100_spi_en(false);
|
||||
gatewayTransportSend(buildGw(_msgTmp, I_GATEWAY_READY).set("Gateway startup complete."));
|
||||
_w5100_spi_en(true);
|
||||
presentNode();
|
||||
} else {
|
||||
// connecting to the server failed!
|
||||
debug(PSTR("Eth: Failed to connect\n"));
|
||||
_w5100_spi_en(false);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
ret = client.write(_ethernetMsg, strlen(_ethernetMsg)) > 0;
|
||||
}
|
||||
nbytes = client.write(_ethernetMsg, strlen(_ethernetMsg));
|
||||
#endif
|
||||
#else
|
||||
// Send message to connected clients
|
||||
@@ -213,20 +218,18 @@ bool gatewayTransportSend(MyMessage &message)
|
||||
{
|
||||
if (clients[i] && clients[i].connected())
|
||||
{
|
||||
clients[i].write((uint8_t*)_ethernetMsg, strlen(_ethernetMsg));
|
||||
nbytes += clients[i].write((uint8_t*)_ethernetMsg, strlen(_ethernetMsg));
|
||||
}
|
||||
}
|
||||
#else
|
||||
ret = _ethernetServer.write(_ethernetMsg) > 0;
|
||||
nbytes = _ethernetServer.write(_ethernetMsg);
|
||||
#endif
|
||||
#endif /* MY_GATEWAY_CLIENT_MODE */
|
||||
_w5100_spi_en(false);
|
||||
return ret;
|
||||
|
||||
return (nbytes > 0);
|
||||
}
|
||||
|
||||
|
||||
#if defined(MY_GATEWAY_ESP8266) && !defined(MY_GATEWAY_CLIENT_MODE)
|
||||
#if (defined(MY_GATEWAY_ESP8266) || defined(MY_GATEWAY_LINUX)) && !defined(MY_GATEWAY_CLIENT_MODE)
|
||||
bool _readFromClient(uint8_t i) {
|
||||
while (clients[i].connected() && clients[i].available()) {
|
||||
char inChar = clients[i].read();
|
||||
@@ -296,7 +299,6 @@ bool gatewayTransportAvailable()
|
||||
#endif
|
||||
|
||||
#ifdef MY_USE_UDP
|
||||
|
||||
int packet_size = _ethernetServer.parsePacket();
|
||||
|
||||
if (packet_size) {
|
||||
@@ -323,19 +325,19 @@ bool gatewayTransportAvailable()
|
||||
if (!client.connected()) {
|
||||
client.stop();
|
||||
#if defined(MY_CONTROLLER_URL_ADDRESS)
|
||||
if (!client.connect(MY_CONTROLLER_URL_ADDRESS, MY_PORT)) {
|
||||
if (client.connect(MY_CONTROLLER_URL_ADDRESS, MY_PORT)) {
|
||||
#else
|
||||
if (!client.connect(_ethernetControllerIP, MY_PORT)) {
|
||||
if (client.connect(_ethernetControllerIP, MY_PORT)) {
|
||||
#endif
|
||||
debug(PSTR("Eth: Failed to connect\n"));
|
||||
_w5100_spi_en(false);
|
||||
return false;
|
||||
} else {
|
||||
debug(PSTR("Eth: connect\n"));
|
||||
_w5100_spi_en(false);
|
||||
gatewayTransportSend(buildGw(_msgTmp, I_GATEWAY_READY).set("Gateway startup complete."));
|
||||
_w5100_spi_en(true);
|
||||
presentNode();
|
||||
} else {
|
||||
debug(PSTR("Eth: Failed to connect\n"));
|
||||
_w5100_spi_en(false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (_readFromClient()) {
|
||||
@@ -344,7 +346,7 @@ bool gatewayTransportAvailable()
|
||||
return true;
|
||||
}
|
||||
#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;
|
||||
@@ -377,7 +379,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;
|
||||
}
|
||||
@@ -403,7 +405,7 @@ bool gatewayTransportAvailable()
|
||||
client.stop();
|
||||
} else {
|
||||
if (_readFromClient()) {
|
||||
setIndication(INDICATION_GW_RX);
|
||||
setIndication(INDICATION_GW_RX);
|
||||
_w5100_spi_en(false);
|
||||
return true;
|
||||
}
|
||||
@@ -421,8 +423,7 @@ MyMessage& gatewayTransportReceive()
|
||||
return _ethernetMsg;
|
||||
}
|
||||
|
||||
|
||||
#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
|
||||
|
||||
@@ -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,99 +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;
|
||||
setIndication(INDICATION_GW_RX);
|
||||
}
|
||||
}
|
||||
i++;
|
||||
}
|
||||
_MQTT_available = protocolMQTTParse(_MQTT_msg, topic, payload, length);
|
||||
}
|
||||
|
||||
|
||||
bool reconnectMQTT() {
|
||||
debug(PSTR("Attempting MQTT connection...\n"));
|
||||
// Attempt to connect
|
||||
@@ -169,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);
|
||||
@@ -216,10 +143,7 @@ bool gatewayTransportInit() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool gatewayTransportAvailable() {
|
||||
|
||||
if (_MQTT_connecting)
|
||||
return false;
|
||||
|
||||
@@ -240,6 +164,3 @@ MyMessage & gatewayTransportReceive() {
|
||||
_MQTT_available = false;
|
||||
return _MQTT_msg;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
|
||||
#include "MyConfig.h"
|
||||
#include "MyProtocol.h"
|
||||
#include "MyGatewayTransport.h"
|
||||
@@ -31,7 +30,6 @@ char _serialInputString[MY_GATEWAY_MAX_RECEIVE_LENGTH]; // A buffer for incom
|
||||
int _serialInputPos;
|
||||
MyMessage _serialMsg;
|
||||
|
||||
|
||||
bool gatewayTransportSend(MyMessage &message) {
|
||||
setIndication(INDICATION_GW_TX);
|
||||
MY_SERIALDEVICE.print(protocolFormat(message));
|
||||
@@ -47,7 +45,6 @@ bool gatewayTransportInit() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool gatewayTransportAvailable() {
|
||||
while (MY_SERIALDEVICE.available()) {
|
||||
// get the new byte:
|
||||
|
||||
@@ -59,7 +59,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()
|
||||
|
||||
+7
-1
@@ -35,7 +35,13 @@
|
||||
// Define these as macros to save valuable space
|
||||
|
||||
#define hwDigitalWrite(__pin, __value) (digitalWrite(__pin, __value))
|
||||
#define hwInit() MY_SERIALDEVICE.begin(MY_BAUD_RATE, SERIAL_8N1, MY_ESP8266_SERIAL_MODE, 1); MY_SERIALDEVICE.setDebugOutput(true)
|
||||
|
||||
#if defined(MY_DISABLED_SERIAL)
|
||||
#define hwInit()
|
||||
#else
|
||||
#define hwInit() MY_SERIALDEVICE.begin(MY_BAUD_RATE, SERIAL_8N1, MY_ESP8266_SERIAL_MODE, 1); MY_SERIALDEVICE.setDebugOutput(true)
|
||||
#endif
|
||||
|
||||
#define hwWatchdogReset() wdt_reset()
|
||||
#define hwReboot() ESP.restart()
|
||||
#define hwMillis() millis()
|
||||
|
||||
@@ -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 <marceloaqno@gmail.org>
|
||||
* 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 <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <sys/stat.h>
|
||||
#include <time.h>
|
||||
#include "log.h"
|
||||
|
||||
static const char* CONFIG_FILE = MY_LINUX_CONFIG_FILE;
|
||||
static const size_t _length = 1024; // ATMega328 has 1024 bytes
|
||||
static uint8_t _config[_length];
|
||||
|
||||
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);
|
||||
std::ofstream myFile(CONFIG_FILE, std::ios::out | std::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.
|
||||
std::ifstream myFile(CONFIG_FILE, std::ios::in | std::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()
|
||||
{
|
||||
for (size_t i = 0; i < _length; i++) {
|
||||
_config[i] = 0xFF;
|
||||
}
|
||||
|
||||
if (!CheckConfigFile()) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
#ifdef MY_GATEWAY_SERIAL
|
||||
MY_SERIALDEVICE.begin(MY_BAUD_RATE);
|
||||
#ifdef MY_LINUX_SERIAL_GROUPNAME
|
||||
if (!MY_SERIALDEVICE.setGroupPerm(MY_LINUX_SERIAL_GROUPNAME)) {
|
||||
debug("Unable to change permission for serial port device.\n");
|
||||
exit(1);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void hwReadConfigBlock(void* buf, void* addr, size_t length)
|
||||
{
|
||||
unsigned long int offs = reinterpret_cast<unsigned long int>(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<unsigned long int>(addr);
|
||||
|
||||
if (length && offs + length <= _length) {
|
||||
memcpy(_config+offs, buf, length);
|
||||
|
||||
std::ofstream myFile(CONFIG_FILE, std::ios::out | std::ios::in | std::ios::binary);
|
||||
if (!myFile) {
|
||||
debug("Unable to write config to file %s.\n", CONFIG_FILE);
|
||||
return;
|
||||
}
|
||||
myFile.seekp(offs);
|
||||
myFile.write((const char*)buf, length);
|
||||
myFile.close();
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t hwReadConfig(int adr)
|
||||
{
|
||||
uint8_t value = 0xFF;
|
||||
hwReadConfigBlock(&value, reinterpret_cast<void*>(adr), 1);
|
||||
return value;
|
||||
}
|
||||
|
||||
void hwWriteConfig(int adr, uint8_t value)
|
||||
{
|
||||
uint8_t curr = hwReadConfig(adr);
|
||||
if (curr != value) {
|
||||
hwWriteConfigBlock(&value, reinterpret_cast<void*>(adr), 1);
|
||||
}
|
||||
}
|
||||
|
||||
void hwRandomNumberInit()
|
||||
{
|
||||
randomSeed(time(NULL));
|
||||
}
|
||||
|
||||
// Not supported!
|
||||
int8_t hwSleep(unsigned long ms)
|
||||
{
|
||||
(void)ms;
|
||||
|
||||
return MY_SLEEP_NOT_POSSIBLE;
|
||||
}
|
||||
|
||||
// Not supported!
|
||||
int8_t hwSleep(uint8_t interrupt, uint8_t mode, unsigned long ms)
|
||||
{
|
||||
(void)interrupt;
|
||||
(void)mode;
|
||||
(void)ms;
|
||||
|
||||
return MY_SLEEP_NOT_POSSIBLE;
|
||||
}
|
||||
|
||||
// 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 MY_SLEEP_NOT_POSSIBLE;
|
||||
}
|
||||
|
||||
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 args;
|
||||
|
||||
va_start(args, fmt);
|
||||
mys_log_v(LOG_DEBUG, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* 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 <marceloaqno@gmail.org>
|
||||
* 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 <cstdlib>
|
||||
#include <pthread.h>
|
||||
#include "MyHw.h"
|
||||
#include "SerialPort.h"
|
||||
|
||||
#ifdef MY_IS_SERIAL_PTY
|
||||
SerialPort Serial = SerialPort(MY_LINUX_SERIAL_PTY, true);
|
||||
#else
|
||||
SerialPort Serial = SerialPort(MY_LINUX_SERIAL_PORT);
|
||||
#endif
|
||||
|
||||
#define MY_SERIALDEVICE Serial
|
||||
|
||||
// Define these as macros (do nothing)
|
||||
#define hwDigitalWrite(__pin, __value)
|
||||
#define hwWatchdogReset()
|
||||
#define hwReboot()
|
||||
#define hwMillis() millis()
|
||||
|
||||
void hwInit();
|
||||
void hwReadConfigBlock(void* buf, void* adr, size_t length);
|
||||
void hwWriteConfigBlock(void* buf, void* adr, size_t length);
|
||||
uint8_t hwReadConfig(int adr);
|
||||
void hwWriteConfig(int adr, uint8_t value);
|
||||
void hwRandomNumberInit();
|
||||
|
||||
#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
|
||||
#else
|
||||
#define ATOMIC_BLOCK_CLEANUP
|
||||
#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 )
|
||||
#else
|
||||
#define ATOMIC_BLOCK
|
||||
#endif /* DOXYGEN */
|
||||
|
||||
#ifndef DOXYGEN
|
||||
#define MY_CRITICAL_SECTION ATOMIC_BLOCK
|
||||
#endif /* DOXYGEN */
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,135 @@
|
||||
// Initialize library and handle sketch functions like we want to
|
||||
|
||||
#include <stdio.h>
|
||||
#include <csignal>
|
||||
#include <cstdlib>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <syslog.h>
|
||||
#include <errno.h>
|
||||
#include "log.h"
|
||||
#include "MySensorsCore.h"
|
||||
|
||||
void handle_sigint(int sig)
|
||||
{
|
||||
if (sig == SIGINT) {
|
||||
mys_log(LOG_NOTICE, "Received SIGINT\n\n");
|
||||
} else if (sig == SIGTERM) {
|
||||
mys_log(LOG_NOTICE, "Received SIGTERM\n\n");
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef MY_RF24_IRQ_PIN
|
||||
detachInterrupt(MY_RF24_IRQ_PIN);
|
||||
#endif
|
||||
|
||||
#if defined(MY_GATEWAY_SERIAL)
|
||||
MY_SERIALDEVICE.end();
|
||||
#endif
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static int daemonize(void)
|
||||
{
|
||||
pid_t pid, sid;
|
||||
|
||||
/* Fork off the parent process */
|
||||
pid = fork();
|
||||
if (pid < 0) {
|
||||
mys_log(LOG_ERR, "fork: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
/* If we got a good PID, then we can exit the parent process. */
|
||||
if (pid > 0) {
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
/* At this point we are executing as the child process */
|
||||
|
||||
/* Change the file mode mask */
|
||||
umask(0);
|
||||
|
||||
/* Create a new SID for the child process */
|
||||
sid = setsid();
|
||||
if (sid < 0) {
|
||||
mys_log(LOG_ERR, "setsid: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Change the current working directory. This prevents the current
|
||||
directory from being locked; hence not being able to remove it. */
|
||||
if ((chdir("/")) < 0) {
|
||||
mys_log(LOG_ERR, "chdir(\"/\"): %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
freopen( "/dev/null", "r", stdin);
|
||||
freopen( "/dev/null", "r", stdout);
|
||||
freopen( "/dev/null", "r", stderr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void print_usage()
|
||||
{
|
||||
printf("Usage: mysGateway [options]\n\n" \
|
||||
"Options:\n" \
|
||||
" -h Display a short summary of all program options.\n" \
|
||||
" -d Enable debug.\n" \
|
||||
" -b Become a daemon.\n");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int opt, log_opts, debug = 0, foreground = 1;
|
||||
|
||||
/* register the signal handler */
|
||||
signal(SIGINT, handle_sigint);
|
||||
signal(SIGTERM, handle_sigint);
|
||||
|
||||
while ((opt = getopt(argc, argv, "hdb")) != -1) {
|
||||
switch (opt) {
|
||||
case 'h':
|
||||
print_usage();
|
||||
exit(0);
|
||||
case 'd':
|
||||
debug = 1;
|
||||
break;
|
||||
case 'b':
|
||||
foreground = 0;
|
||||
break;
|
||||
default:
|
||||
print_usage();
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
log_opts = LOG_CONS;
|
||||
if (foreground && isatty(STDIN_FILENO)) {
|
||||
// Also print syslog to stderror
|
||||
log_opts |= LOG_PERROR;
|
||||
}
|
||||
if (!debug) {
|
||||
setlogmask(LOG_UPTO (LOG_INFO));
|
||||
}
|
||||
openlog(NULL, log_opts, LOG_USER);
|
||||
|
||||
if (!foreground && !debug) {
|
||||
if (daemonize() != 0) {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
mys_log(LOG_INFO, "Starting gateway...\n");
|
||||
mys_log(LOG_INFO, "Protocol version - %s\n", MYSENSORS_LIBRARY_VERSION);
|
||||
|
||||
_begin(); // Startup MySensors library
|
||||
|
||||
for (;;) {
|
||||
_process(); // Process incoming data
|
||||
if (loop) loop(); // Call sketch loop
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
+4
-4
@@ -21,7 +21,7 @@
|
||||
#include "MyMessage.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
MyMessage::MyMessage()
|
||||
{
|
||||
@@ -117,7 +117,7 @@ char* MyMessage::getString(char *buffer) const {
|
||||
} else if (payloadType == P_ULONG32) {
|
||||
ultoa(ulValue, buffer, 10);
|
||||
} else if (payloadType == P_FLOAT32) {
|
||||
dtostrf(fValue,2,min(fPrecision, 8),buffer);
|
||||
dtostrf(fValue,2,min(fPrecision, (uint8_t)8),buffer);
|
||||
} else if (payloadType == P_CUSTOM) {
|
||||
return getCustomString(buffer);
|
||||
}
|
||||
@@ -210,7 +210,7 @@ MyMessage& MyMessage::setDestination(uint8_t _destination) {
|
||||
|
||||
// Set payload
|
||||
MyMessage& MyMessage::set(void* value, uint8_t length) {
|
||||
uint8_t payloadLength = value == NULL ? 0 : min(length, MAX_PAYLOAD);
|
||||
uint8_t payloadLength = value == NULL ? 0 : min(length, (uint8_t)MAX_PAYLOAD);
|
||||
miSetLength(payloadLength);
|
||||
miSetPayloadType(P_CUSTOM);
|
||||
memcpy(data, value, payloadLength);
|
||||
@@ -218,7 +218,7 @@ MyMessage& MyMessage::set(void* value, uint8_t length) {
|
||||
}
|
||||
|
||||
MyMessage& MyMessage::set(const char* value) {
|
||||
uint8_t length = value == NULL ? 0 : min(strlen(value), MAX_PAYLOAD);
|
||||
uint8_t length = value == NULL ? 0 : min(strlen(value), (size_t)MAX_PAYLOAD);
|
||||
miSetLength(length);
|
||||
miSetPayloadType(P_STRING);
|
||||
if (length) {
|
||||
|
||||
@@ -32,7 +32,6 @@
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include <Arduino.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "MyConfig.h"
|
||||
#include "MyTransport.h"
|
||||
#include "MyProtocol.h"
|
||||
#include <string.h>
|
||||
|
||||
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
|
||||
@@ -100,6 +99,95 @@ char * protocolFormat(MyMessage &message) {
|
||||
return _fmtBuffer;
|
||||
}
|
||||
|
||||
char * protocolFormatMQTTTopic(const char* prefix, MyMessage &message) {
|
||||
snprintf_P(_fmtBuffer, MY_GATEWAY_MAX_SEND_LENGTH, PSTR("%s/%d/%d/%d/%d/%d"), prefix, message.sender, message.sensor, mGetCommand(message), mGetAck(message), message.type);
|
||||
return _fmtBuffer;
|
||||
}
|
||||
|
||||
char * protocolFormatMQTTSubscribe(const char* prefix) {
|
||||
snprintf_P(_fmtBuffer, MY_GATEWAY_MAX_SEND_LENGTH, PSTR("%s/+/+/+/+/+"), prefix);
|
||||
return _fmtBuffer;
|
||||
}
|
||||
|
||||
#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
|
||||
if (strcmp(str, MY_MQTT_SUBSCRIBE_TOPIC_PREFIX) != 0) {
|
||||
// Message not for us or malformed!
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
// Node id
|
||||
message.destination = atoi(str);
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
// Sensor id
|
||||
message.sensor = atoi(str);
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
// Command type
|
||||
command = atoi(str);
|
||||
mSetCommand(message, command);
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
// Ack flag
|
||||
mSetRequestAck(message, atoi(str)?1:0);
|
||||
break;
|
||||
}
|
||||
case 5: {
|
||||
// Sub type
|
||||
message.type = atoi(str);
|
||||
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;
|
||||
if (c <= '9')
|
||||
@@ -110,5 +198,3 @@ uint8_t protocolH2i(char c) {
|
||||
i += c - 'A' + 10;
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -51,6 +51,10 @@ void _process() {
|
||||
transportProcess();
|
||||
#endif
|
||||
|
||||
#if defined(__linux__)
|
||||
// To avoid high cpu usage
|
||||
usleep(10000); // 10ms
|
||||
#endif
|
||||
}
|
||||
|
||||
void _infiniteLoop() {
|
||||
@@ -61,17 +65,17 @@ void _infiniteLoop() {
|
||||
#if defined (MY_DEFAULT_TX_LED_PIN) || defined(MY_DEFAULT_RX_LED_PIN) || defined(MY_DEFAULT_ERR_LED_PIN)
|
||||
ledsProcess();
|
||||
#endif
|
||||
#if defined(__linux__)
|
||||
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"));
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -716,4 +716,4 @@ bool transportSendWrite(uint8_t to, MyMessage &message) {
|
||||
return (ok || to==BROADCAST_ADDRESS);
|
||||
}
|
||||
|
||||
// EOF MyTransport.cpp
|
||||
// EOF MyTransport.cpp
|
||||
|
||||
@@ -19,7 +19,9 @@
|
||||
|
||||
#include "MyConfig.h"
|
||||
#include "MyTransport.h"
|
||||
|
||||
#include "drivers/RF24/RF24.h"
|
||||
|
||||
#include "drivers/CircularBuffer/CircularBuffer.h"
|
||||
|
||||
#if defined(MY_RF24_ENABLE_ENCRYPTION)
|
||||
|
||||
@@ -57,12 +57,7 @@
|
||||
#include "MyTransport.h"
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
#if defined(ARDUINO) && ARDUINO >= 100
|
||||
#include <Arduino.h>
|
||||
#else
|
||||
#include <WProgram.h>
|
||||
#endif
|
||||
|
||||
#include "MyTransport.h"
|
||||
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
#ifndef Arduino_h
|
||||
#define Arduino_h
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include "stdlib_noniso.h"
|
||||
|
||||
#ifdef LINUX_ARCH_RASPBERRYPI
|
||||
#include "rpi_util.h"
|
||||
using namespace rpi_util;
|
||||
#endif
|
||||
|
||||
#define PI 3.1415926535897932384626433832795
|
||||
#define HALF_PI 1.5707963267948966192313216916398
|
||||
#define TWO_PI 6.283185307179586476925286766559
|
||||
#define DEG_TO_RAD 0.017453292519943295769236907684886
|
||||
#define RAD_TO_DEG 57.295779513082320876798154814105
|
||||
#define EULER 2.718281828459045235360287471352
|
||||
|
||||
#define constrain(amt,low,high) ((amt)<(low)?(low):((amt)>(high)?(high):(amt)))
|
||||
#define radians(deg) ((deg)*DEG_TO_RAD)
|
||||
#define degrees(rad) ((rad)*RAD_TO_DEG)
|
||||
#define sq(x) ((x)*(x))
|
||||
#define round(x) ((x)>=0?(long)((x)+0.5):(long)((x)-0.5))
|
||||
|
||||
#define lowByte(w) ((uint8_t) ((w) & 0xff))
|
||||
#define highByte(w) ((uint8_t) ((w) >> 8))
|
||||
|
||||
#define bitRead(value, bit) (((value) >> (bit)) & 0x01)
|
||||
#define bitSet(value, bit) ((value) |= (1UL << (bit)))
|
||||
#define bitClear(value, bit) ((value) &= ~(1UL << (bit)))
|
||||
#define bitWrite(value, bit, bitvalue) (bitvalue ? bitSet(value, bit) : bitClear(value, bit))
|
||||
|
||||
#define bit(b) (1UL << (b))
|
||||
|
||||
#define GET_MACRO(_0, _1, _2, NAME, ...) NAME
|
||||
#define random(...) GET_MACRO(_0, ##__VA_ARGS__, randMinMax, randMax, rand)(__VA_ARGS__)
|
||||
|
||||
#ifndef delay
|
||||
#define delay _delay_ms
|
||||
#endif
|
||||
|
||||
using std::string;
|
||||
using std::min;
|
||||
using std::max;
|
||||
using std::abs;
|
||||
|
||||
typedef bool boolean;
|
||||
typedef uint8_t byte;
|
||||
typedef string String;
|
||||
|
||||
void yield(void);
|
||||
unsigned long millis(void);
|
||||
void _delay_ms(unsigned int millis);
|
||||
void randomSeed(unsigned long seed);
|
||||
long randMax(long howbig);
|
||||
long randMinMax(long howsmall, long howbig);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
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 <marceloaqno@gmail.org> for MySensors use
|
||||
*/
|
||||
|
||||
#ifndef client_h
|
||||
#define client_h
|
||||
|
||||
#include "Stream.h"
|
||||
#include "IPAddress.h"
|
||||
|
||||
#if !DOXYGEN
|
||||
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
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,254 @@
|
||||
/*
|
||||
* 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 <marceloaqno@gmail.org>
|
||||
* 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 <cstdio>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <netdb.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <cstring>
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <errno.h>
|
||||
#include "log.h"
|
||||
#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) {
|
||||
mys_log(LOG_ERR, "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) {
|
||||
mys_log(LOG_ERR, "socket: %s\n", strerror(errno));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (::connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
|
||||
close(sockfd);
|
||||
mys_log(LOG_ERR, "connect: %s\n", strerror(errno));
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (p == NULL) {
|
||||
mys_log(LOG_ERR, "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);
|
||||
mys_log(LOG_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) {
|
||||
mys_log(LOG_ERR, "send: %s\n", strerror(errno));
|
||||
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::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;
|
||||
}
|
||||
}
|
||||
|
||||
return ETHERNETCLIENT_W5100_CLOSED;
|
||||
}
|
||||
|
||||
uint8_t EthernetClient::connected() {
|
||||
if (_sock == -1) return 0;
|
||||
|
||||
if (peek() < 0) {
|
||||
if (errno == EAGAIN) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int EthernetClient::getSocketNumber() {
|
||||
return _sock;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
* 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 <marceloaqno@gmail.org>
|
||||
* 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
|
||||
|
||||
/**
|
||||
* EthernetClient class
|
||||
*/
|
||||
class EthernetClient : public Client {
|
||||
|
||||
private:
|
||||
int _sock; //!< @brief Network socket.
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief EthernetClient constructor.
|
||||
*/
|
||||
EthernetClient();
|
||||
/**
|
||||
* @brief EthernetClient constructor.
|
||||
*
|
||||
* @param sock Network socket.
|
||||
*/
|
||||
EthernetClient(int sock);
|
||||
/**
|
||||
* @brief Initiate a connection with host:port.
|
||||
*
|
||||
* @param host host name to resolve or a stringified dotted IP address.
|
||||
* @param port to connect to.
|
||||
* @return 1 if SUCCESS or -1 if FAILURE.
|
||||
*/
|
||||
virtual int connect(const char *host, uint16_t port);
|
||||
/**
|
||||
* @brief Initiate a connection with ip:port.
|
||||
*
|
||||
* @param ip to connect to.
|
||||
* @param port to connect to.
|
||||
* @return 1 if SUCCESS or -1 if FAILURE.
|
||||
*/
|
||||
virtual int connect(IPAddress ip, uint16_t port);
|
||||
/**
|
||||
* @brief Write a byte.
|
||||
*
|
||||
* @param b byte to write.
|
||||
* @return 0 if FAILURE or 1 if SUCCESS.
|
||||
*/
|
||||
virtual size_t write(uint8_t b);
|
||||
/**
|
||||
* @brief Write at most 'size' bytes.
|
||||
*
|
||||
* @param buf Buffer to read from.
|
||||
* @param size of the buffer.
|
||||
* @return 0 if FAILURE or the number of bytes sent.
|
||||
*/
|
||||
virtual size_t write(const uint8_t *buf, size_t size);
|
||||
/**
|
||||
* @brief Write a null-terminated string.
|
||||
*
|
||||
* @param str String to write.
|
||||
* @return 0 if FAILURE or number of characters sent.
|
||||
*/
|
||||
size_t write(const char *str);
|
||||
/**
|
||||
* @brief Write at most 'size' characters.
|
||||
*
|
||||
* @param buffer to read from.
|
||||
* @param size of the buffer.
|
||||
* @return 0 if FAILURE or the number of characters sent.
|
||||
*/
|
||||
size_t write(const char *buffer, size_t size);
|
||||
/**
|
||||
* @brief Checks if new data is available.
|
||||
*
|
||||
* @return number of bytes available.
|
||||
*/
|
||||
virtual int available();
|
||||
/**
|
||||
* @brief Read a byte.
|
||||
*
|
||||
* @return 0 if FAILURE or 1 if SUCCESS.
|
||||
* @note This function will block (until data becomes available or timeout is reached).
|
||||
*/
|
||||
virtual int read();
|
||||
/**
|
||||
* @brief Read a number of bytes and store in a buffer.
|
||||
*
|
||||
* @return -1 if FAILURE or number of read bytes.
|
||||
* @note This function will block (until data becomes available or timeout is reached).
|
||||
*/
|
||||
virtual int read(uint8_t *buf, size_t size);
|
||||
/**
|
||||
* @brief Check if new data are available.
|
||||
*
|
||||
* @return 0 if no data, else number of bytes available.
|
||||
*/
|
||||
virtual int peek();
|
||||
/**
|
||||
* @brief Flushes the network buffers.
|
||||
*
|
||||
* Empty function due to the way TCP works.
|
||||
*/
|
||||
virtual void flush();
|
||||
/**
|
||||
* @brief Close the connection gracefully.
|
||||
*
|
||||
* Send a FIN and wait 1s for a response. If no response close it forcefully.
|
||||
*/
|
||||
virtual void stop();
|
||||
/**
|
||||
* @brief Connection status.
|
||||
*
|
||||
* @return state according to W5100 library codes.
|
||||
*/
|
||||
uint8_t status();
|
||||
/**
|
||||
* @brief Checks whether the socket is alive.
|
||||
*
|
||||
* @return 0 if disconnected or 1 if connected.
|
||||
*/
|
||||
virtual uint8_t connected();
|
||||
/**
|
||||
* @brief Get the internal socket file descriptor.
|
||||
*
|
||||
* @return an integer, that is the socket number.
|
||||
*/
|
||||
int getSocketNumber();
|
||||
/**
|
||||
* @brief Overloaded cast operators.
|
||||
*
|
||||
* Allow EthernetClient objects to be used where a bool is expected.
|
||||
*/
|
||||
virtual operator bool();
|
||||
/**
|
||||
* @brief Overloaded cast operators.
|
||||
*
|
||||
*/
|
||||
virtual bool operator==(const bool value) { return bool() == value; }
|
||||
/**
|
||||
* @brief Overloaded cast operators.
|
||||
*
|
||||
*/
|
||||
virtual bool operator!=(const bool value) { return bool() != value; }
|
||||
/**
|
||||
* @brief Overloaded cast operators.
|
||||
*
|
||||
*/
|
||||
virtual bool operator==(const EthernetClient& rhs);
|
||||
/**
|
||||
* @brief Overloaded cast operators.
|
||||
*
|
||||
*/
|
||||
virtual bool operator!=(const EthernetClient& rhs) { return !this->operator==(rhs); };
|
||||
|
||||
friend class EthernetServer;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
* 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 <marceloaqno@gmail.org>
|
||||
* 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 <cstdio>
|
||||
#include <sys/socket.h>
|
||||
#include <cstring>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include "log.h"
|
||||
#include "EthernetClient.h"
|
||||
#include "EthernetServer.h"
|
||||
|
||||
EthernetServer::EthernetServer(uint16_t port, uint16_t max_clients) : port(port), max_clients(max_clients)
|
||||
{
|
||||
clients.reserve(max_clients);
|
||||
}
|
||||
|
||||
void EthernetServer::begin()
|
||||
{
|
||||
begin(IPAddress(0,0,0,0));
|
||||
}
|
||||
|
||||
void EthernetServer::begin(IPAddress address)
|
||||
{
|
||||
struct addrinfo hints, *servinfo, *p;
|
||||
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", port);
|
||||
if ((rv = getaddrinfo(address.toString().c_str(), portstr, &hints, &servinfo)) != 0) {
|
||||
mys_log(LOG_ERR, "getaddrinfo: %s\n", gai_strerror(rv));
|
||||
return;
|
||||
}
|
||||
|
||||
// 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) {
|
||||
mys_log(LOG_ERR, "socket: %s\n", strerror(errno));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) {
|
||||
mys_log(LOG_ERR, "setsockopt: %s\n", strerror(errno));
|
||||
freeaddrinfo(servinfo);
|
||||
return;
|
||||
}
|
||||
|
||||
if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
|
||||
close(sockfd);
|
||||
mys_log(LOG_ERR, "bind: %s\n", strerror(errno));
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (p == NULL) {
|
||||
mys_log(LOG_ERR, "failed to bind\n");
|
||||
freeaddrinfo(servinfo);
|
||||
return;
|
||||
}
|
||||
|
||||
if (listen(sockfd, ETHERNETSERVER_BACKLOG) == -1) {
|
||||
mys_log(LOG_ERR, "listen: %s\n", strerror(errno));
|
||||
freeaddrinfo(servinfo);
|
||||
return;
|
||||
}
|
||||
|
||||
freeaddrinfo(servinfo);
|
||||
|
||||
fcntl(sockfd, F_SETFL, O_NONBLOCK);
|
||||
|
||||
struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;
|
||||
void *addr = &(ipv4->sin_addr);
|
||||
inet_ntop(p->ai_family, addr, ipstr, sizeof ipstr);
|
||||
mys_log(LOG_DEBUG, "Listening for connections on %s:%s\n", ipstr, portstr);
|
||||
}
|
||||
|
||||
bool EthernetServer::hasClient()
|
||||
{
|
||||
_accept();
|
||||
|
||||
return !new_clients.empty();
|
||||
}
|
||||
|
||||
EthernetClient EthernetServer::available()
|
||||
{
|
||||
if (new_clients.empty()) {
|
||||
return EthernetClient();
|
||||
} else {
|
||||
int sock = new_clients.front();
|
||||
new_clients.pop_front();
|
||||
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;
|
||||
|
||||
while (i < clients.size()) {
|
||||
EthernetClient client(clients[i]);
|
||||
if (client.connected()) {
|
||||
n += client.write(buffer, size);
|
||||
i++;
|
||||
} else if (!client.available()) {
|
||||
client.stop();
|
||||
clients[i] = clients.back();
|
||||
clients.pop_back();
|
||||
mys_log(LOG_DEBUG, "Client disconnected.");
|
||||
}
|
||||
}
|
||||
|
||||
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 EthernetServer::_accept()
|
||||
{
|
||||
int new_fd;
|
||||
socklen_t sin_size;
|
||||
struct sockaddr_storage client_addr;
|
||||
char ipstr[INET_ADDRSTRLEN];
|
||||
|
||||
if (new_clients.size() + clients.size() == max_clients) {
|
||||
// no free slots, search for a dead client
|
||||
bool no_free_slots = true;
|
||||
for (size_t i = 0; i < clients.size();) {
|
||||
EthernetClient client(clients[i]);
|
||||
if (client.connected() || client.available()) {
|
||||
i++;
|
||||
} else {
|
||||
clients[i] = clients.back();
|
||||
clients.pop_back();
|
||||
no_free_slots = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (no_free_slots) {
|
||||
for (std::list<int>::iterator it = new_clients.begin(); it != new_clients.end(); ++it) {
|
||||
EthernetClient client(*it);
|
||||
if (!client.connected() && !client.available()) {
|
||||
new_clients.erase(it);
|
||||
no_free_slots = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (no_free_slots) {
|
||||
mys_log(LOG_DEBUG, "Max number of ethernet clients reached.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
sin_size = sizeof client_addr;
|
||||
new_fd = accept(sockfd, (struct sockaddr *)&client_addr, &sin_size);
|
||||
if (new_fd == -1) {
|
||||
if (errno != EAGAIN && errno != EWOULDBLOCK) {
|
||||
mys_log(LOG_ERR, "accept: %s\n", strerror(errno));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
new_clients.push_back(new_fd);
|
||||
clients.push_back(new_fd);
|
||||
|
||||
void *addr = &(((struct sockaddr_in*)&client_addr)->sin_addr);
|
||||
inet_ntop(client_addr.ss_family, addr, ipstr, sizeof ipstr);
|
||||
mys_log(LOG_DEBUG, "New connection from %s\n", ipstr);
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* 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 <marceloaqno@gmail.org>
|
||||
* 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 <list>
|
||||
#include <vector>
|
||||
#include "Server.h"
|
||||
#include "IPAddress.h"
|
||||
|
||||
#ifdef ETHERNETSERVER_MAX_CLIENTS
|
||||
#define ETHERNETSERVER_BACKLOG ETHERNETSERVER_MAX_CLIENTS //!< Maximum length to which the queue of pending connections may grow.
|
||||
#else
|
||||
#define ETHERNETSERVER_MAX_CLIENTS 10 //!< Default value for max_clients.
|
||||
#define ETHERNETSERVER_BACKLOG 10 //!< Maximum length to which the queue of pending connections may grow.
|
||||
#endif
|
||||
|
||||
class EthernetClient;
|
||||
|
||||
/**
|
||||
* @brief EthernetServer class
|
||||
*/
|
||||
class EthernetServer : public Server {
|
||||
|
||||
private:
|
||||
uint16_t port; //!< @brief Port number for the network socket.
|
||||
std::list<int> new_clients; //!< Socket list of new clients.
|
||||
std::vector<int> clients; //!< @brief Socket list of clients.
|
||||
uint16_t max_clients; //!< @brief The maximum number of allowed clients.
|
||||
int sockfd; //!< @brief Network socket used to accept connections.
|
||||
|
||||
/**
|
||||
* @brief Accept new clients if the total of connected clients is below max_clients.
|
||||
*
|
||||
*/
|
||||
void _accept();
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief EthernetServer constructor.
|
||||
*
|
||||
* @param port number for the socket addresses.
|
||||
* @param max_clients The maximum number allowed for connected clients.
|
||||
*/
|
||||
EthernetServer(uint16_t port, uint16_t max_clients = ETHERNETSERVER_MAX_CLIENTS);
|
||||
/**
|
||||
* @brief Listen for inbound connection request.
|
||||
*
|
||||
*/
|
||||
virtual void begin();
|
||||
/**
|
||||
* @brief Listen on the specified ip for inbound connection request.
|
||||
*
|
||||
* @param addr IP address to bind to.
|
||||
*/
|
||||
void begin(IPAddress addr);
|
||||
/**
|
||||
* @brief Verifies if a new client has connected.
|
||||
*
|
||||
* @return @c true if a new client has connected, else @c false.
|
||||
*/
|
||||
bool hasClient();
|
||||
/**
|
||||
* @brief Get the new connected client.
|
||||
*
|
||||
* @return client class object if a new client has connected else -1.
|
||||
*/
|
||||
EthernetClient available();
|
||||
/**
|
||||
* @brief Write a byte to all clients.
|
||||
*
|
||||
* @param b byte to send.
|
||||
* @return 0 if FAILURE or 1 if SUCCESS.
|
||||
*/
|
||||
virtual size_t write(uint8_t b);
|
||||
/**
|
||||
* @brief Write at most 'size' bytes to all clients.
|
||||
*
|
||||
* @param buffer to read from.
|
||||
* @param size of the buffer.
|
||||
* @return 0 if FAILURE else number of bytes sent.
|
||||
*/
|
||||
virtual size_t write(const uint8_t *buffer, size_t size);
|
||||
/**
|
||||
* @brief Write a null-terminated string to all clients.
|
||||
*
|
||||
* @param str String to write.
|
||||
* @return 0 if FAILURE else number of characters sent.
|
||||
*/
|
||||
size_t write(const char *str);
|
||||
/**
|
||||
* @brief Write at most 'size' characters to all clients.
|
||||
*
|
||||
* @param buffer to read from.
|
||||
* @param size of the buffer.
|
||||
* @return 0 if FAILURE else the number of characters sent.
|
||||
*/
|
||||
size_t write(const char *buffer, size_t size);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* 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 <marceloaqno@gmail.org> for MySensors use
|
||||
*/
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#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);
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* 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 <marceloaqno@gmail.org> for MySensors use
|
||||
*/
|
||||
|
||||
#ifndef IPAddress_h
|
||||
#define IPAddress_h
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
|
||||
/**
|
||||
* @brief A class to make it easier to handle and pass around IP addresses
|
||||
*/
|
||||
class IPAddress {
|
||||
private:
|
||||
union {
|
||||
uint8_t bytes[4]; //!< IPv4 address as an array
|
||||
uint32_t dword; //!< IPv4 address in 32 bits format
|
||||
} _address;
|
||||
|
||||
/**
|
||||
* @brief 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.
|
||||
*
|
||||
* @return pointer to the internal structure.
|
||||
*/
|
||||
uint8_t* raw_address() {
|
||||
return _address.bytes;
|
||||
}
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief IPAddress constructor.
|
||||
*/
|
||||
IPAddress();
|
||||
/**
|
||||
* @brief IPAddress constructor.
|
||||
*
|
||||
* @param first_octet first octet of the IPv4 address.
|
||||
* @param second_octet second octet of the IPv4 address.
|
||||
* @param third_octet third octet of the IPv4 address.
|
||||
* @param fourth_octet fourth octet of the IPv4 address.
|
||||
*/
|
||||
IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet);
|
||||
/**
|
||||
* @brief IPAddress constructor.
|
||||
*
|
||||
* @param address to be set from a 32 bits integer.
|
||||
*/
|
||||
IPAddress(uint32_t address);
|
||||
/**
|
||||
* @brief IPAddress constructor.
|
||||
*
|
||||
* @param address to be set from a byte array.
|
||||
*/
|
||||
IPAddress(const uint8_t *address);
|
||||
/**
|
||||
* @brief Set the IP from a array of characters.
|
||||
*
|
||||
* @param address to be set.
|
||||
*/
|
||||
bool fromString(const char *address);
|
||||
/**
|
||||
* @brief Set the IP from a string class type.
|
||||
*
|
||||
* @param address to be set.
|
||||
*/
|
||||
bool fromString(const std::string &address) { return fromString(address.c_str()); }
|
||||
/**
|
||||
* @brief Overloaded cast operator
|
||||
*
|
||||
* 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;
|
||||
}
|
||||
/**
|
||||
* @brief Overloaded cast operator
|
||||
*
|
||||
*/
|
||||
bool operator==(const IPAddress& addr) const {
|
||||
return _address.dword == addr._address.dword;
|
||||
}
|
||||
/**
|
||||
* @brief Overloaded cast operator
|
||||
*
|
||||
*/
|
||||
bool operator==(uint32_t addr) const {
|
||||
return _address.dword == addr;
|
||||
}
|
||||
/**
|
||||
* @brief Overloaded cast operator
|
||||
*
|
||||
*/
|
||||
bool operator==(const uint8_t* addr) const;
|
||||
/**
|
||||
* @brief Overloaded index operator.
|
||||
*
|
||||
* Allow getting and setting individual octets of the address.
|
||||
*
|
||||
*/
|
||||
uint8_t operator[](int index) const {
|
||||
return _address.bytes[index];
|
||||
}
|
||||
/**
|
||||
* @brief Overloaded index operator
|
||||
*
|
||||
*/
|
||||
uint8_t& operator[](int index) {
|
||||
return _address.bytes[index];
|
||||
}
|
||||
/**
|
||||
* @brief Overloaded copy operators.
|
||||
*
|
||||
* Allow initialisation of IPAddress objects from byte array.
|
||||
*/
|
||||
IPAddress& operator=(const uint8_t *address);
|
||||
/**
|
||||
* @brief Overloaded copy operator.
|
||||
*
|
||||
* Allow initialisation of IPAddress objects from a 32 bits integer.
|
||||
*/
|
||||
IPAddress& operator=(uint32_t address);
|
||||
/**
|
||||
* @brief Convert the IP address to a string.
|
||||
*
|
||||
* @return A stringified IP address
|
||||
*/
|
||||
std::string toString();
|
||||
|
||||
friend class Client;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,242 @@
|
||||
/*
|
||||
Print.cpp - Base class that provides print() and println()
|
||||
Copyright (c) 2008 David A. Mellis. 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 23 November 2006 by David A. Mellis
|
||||
Modified December 2014 by Ivan Grokhotkov
|
||||
Modified May 2015 by Michael C. Miller - esp8266 progmem support
|
||||
Modified August 2016 by Marcelo Aquino <marceloaqno@gmail.org> for MySensors use
|
||||
*/
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
#include <cstdarg>
|
||||
#include <cmath>
|
||||
#include "Print.h"
|
||||
|
||||
// Public Methods //////////////////////////////////////////////////////////////
|
||||
|
||||
size_t Print::write(const uint8_t *buffer, size_t size) {
|
||||
size_t n = 0;
|
||||
while(size--) {
|
||||
n += write(*buffer++);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t
|
||||
#ifdef __GNUC__
|
||||
__attribute__((format(printf, 2, 3)))
|
||||
#endif
|
||||
Print::printf(const char *format, ...) {
|
||||
va_list arg;
|
||||
va_start(arg, format);
|
||||
char temp[64];
|
||||
char* buffer = temp;
|
||||
size_t len = vsnprintf(temp, sizeof(temp), format, arg);
|
||||
va_end(arg);
|
||||
if (len > sizeof(temp) - 1) {
|
||||
buffer = new char[len + 1];
|
||||
if (!buffer) {
|
||||
return 0;
|
||||
}
|
||||
va_start(arg, format);
|
||||
vsnprintf(buffer, len + 1, format, arg);
|
||||
va_end(arg);
|
||||
}
|
||||
len = write((const uint8_t*) buffer, len);
|
||||
if (buffer != temp) {
|
||||
delete[] buffer;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
size_t Print::print(const std::string &s) {
|
||||
return write(s.c_str(), s.length());
|
||||
}
|
||||
|
||||
size_t Print::print(const char str[]) {
|
||||
return write(str);
|
||||
}
|
||||
|
||||
size_t Print::print(char c) {
|
||||
return write(c);
|
||||
}
|
||||
|
||||
size_t Print::print(unsigned char b, int base) {
|
||||
return print((unsigned long) b, base);
|
||||
}
|
||||
|
||||
size_t Print::print(int n, int base) {
|
||||
return print((long) n, base);
|
||||
}
|
||||
|
||||
size_t Print::print(unsigned int n, int base) {
|
||||
return print((unsigned long) n, base);
|
||||
}
|
||||
|
||||
size_t Print::print(long n, int base) {
|
||||
if(base == 0) {
|
||||
return write(n);
|
||||
} else if(base == 10) {
|
||||
if(n < 0) {
|
||||
int t = print('-');
|
||||
n = -n;
|
||||
return printNumber(n, 10) + t;
|
||||
}
|
||||
return printNumber(n, 10);
|
||||
} else {
|
||||
return printNumber(n, base);
|
||||
}
|
||||
}
|
||||
|
||||
size_t Print::print(unsigned long n, int base) {
|
||||
if(base == 0)
|
||||
return write(n);
|
||||
else
|
||||
return printNumber(n, base);
|
||||
}
|
||||
|
||||
size_t Print::print(double n, int digits) {
|
||||
return printFloat(n, digits);
|
||||
}
|
||||
|
||||
size_t Print::println(void) {
|
||||
return print("\r\n");
|
||||
}
|
||||
|
||||
size_t Print::println(const std::string &s) {
|
||||
size_t n = print(s);
|
||||
n += println();
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t Print::println(const char c[]) {
|
||||
size_t n = print(c);
|
||||
n += println();
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t Print::println(char c) {
|
||||
size_t n = print(c);
|
||||
n += println();
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t Print::println(unsigned char b, int base) {
|
||||
size_t n = print(b, base);
|
||||
n += println();
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t Print::println(int num, int base) {
|
||||
size_t n = print(num, base);
|
||||
n += println();
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t Print::println(unsigned int num, int base) {
|
||||
size_t n = print(num, base);
|
||||
n += println();
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t Print::println(long num, int base) {
|
||||
size_t n = print(num, base);
|
||||
n += println();
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t Print::println(unsigned long num, int base) {
|
||||
size_t n = print(num, base);
|
||||
n += println();
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t Print::println(double num, int digits) {
|
||||
size_t n = print(num, digits);
|
||||
n += println();
|
||||
return n;
|
||||
}
|
||||
|
||||
// Private Methods /////////////////////////////////////////////////////////////
|
||||
|
||||
size_t Print::printNumber(unsigned long n, uint8_t base) {
|
||||
char buf[8 * sizeof(long) + 1]; // Assumes 8-bit chars plus zero byte.
|
||||
char *str = &buf[sizeof(buf) - 1];
|
||||
|
||||
*str = '\0';
|
||||
|
||||
// prevent crash if called with base == 1
|
||||
if(base < 2)
|
||||
base = 10;
|
||||
|
||||
do {
|
||||
unsigned long m = n;
|
||||
n /= base;
|
||||
char c = m - base * n;
|
||||
*--str = c < 10 ? c + '0' : c + 'A' - 10;
|
||||
} while(n);
|
||||
|
||||
return write(str);
|
||||
}
|
||||
|
||||
size_t Print::printFloat(double number, uint8_t digits) {
|
||||
size_t n = 0;
|
||||
|
||||
if(std::isnan(number))
|
||||
return print("nan");
|
||||
if(std::isinf(number))
|
||||
return print("inf");
|
||||
if(number > 4294967040.0)
|
||||
return print("ovf"); // constant determined empirically
|
||||
if(number < -4294967040.0)
|
||||
return print("ovf"); // constant determined empirically
|
||||
|
||||
// Handle negative numbers
|
||||
if(number < 0.0) {
|
||||
n += print('-');
|
||||
number = -number;
|
||||
}
|
||||
|
||||
// Round correctly so that print(1.999, 2) prints as "2.00"
|
||||
double rounding = 0.5;
|
||||
for(uint8_t i = 0; i < digits; ++i)
|
||||
rounding /= 10.0;
|
||||
|
||||
number += rounding;
|
||||
|
||||
// Extract the integer part of the number and print it
|
||||
unsigned long int_part = (unsigned long) number;
|
||||
double remainder = number - (double) int_part;
|
||||
n += print(int_part);
|
||||
|
||||
// Print the decimal point, but only if there are digits beyond
|
||||
if(digits > 0) {
|
||||
n += print(".");
|
||||
}
|
||||
|
||||
// Extract digits from the remainder one at a time
|
||||
while(digits-- > 0) {
|
||||
remainder *= 10.0;
|
||||
int toPrint = int(remainder);
|
||||
n += print(toPrint);
|
||||
remainder -= toPrint;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
Print.h - Base class that provides print() and println()
|
||||
Copyright (c) 2008 David A. Mellis. 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 August 2016 by Marcelo Aquino <marceloaqno@gmail.org> for MySensors use
|
||||
*/
|
||||
|
||||
#ifndef Print_h
|
||||
#define Print_h
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
#include <string.h>
|
||||
|
||||
#if !DOXYGEN
|
||||
#define DEC 10
|
||||
#define HEX 16
|
||||
#define OCT 8
|
||||
#define BIN 2
|
||||
|
||||
class Print {
|
||||
private:
|
||||
size_t printNumber(unsigned long n, uint8_t base);
|
||||
size_t printFloat(double number, uint8_t digits);
|
||||
|
||||
public:
|
||||
virtual size_t write(uint8_t) = 0;
|
||||
size_t write(const char *str) {
|
||||
if (str == NULL)
|
||||
return 0;
|
||||
return write((const uint8_t *) str, strlen(str));
|
||||
}
|
||||
virtual size_t write(const uint8_t *buffer, size_t size);
|
||||
size_t write(const char *buffer, size_t size) {
|
||||
return write((const uint8_t *) buffer, size);
|
||||
}
|
||||
|
||||
size_t printf(const char * format, ...) __attribute__ ((format (printf, 2, 3)));
|
||||
size_t print(const std::string &);
|
||||
size_t print(const char[]);
|
||||
size_t print(char);
|
||||
size_t print(unsigned char, int = DEC);
|
||||
size_t print(int, int = DEC);
|
||||
size_t print(unsigned int, int = DEC);
|
||||
size_t print(long, int = DEC);
|
||||
size_t print(unsigned long, int = DEC);
|
||||
size_t print(double, int = 2);
|
||||
|
||||
size_t println(const std::string &s);
|
||||
size_t println(const char[]);
|
||||
size_t println(char);
|
||||
size_t println(unsigned char, int = DEC);
|
||||
size_t println(int, int = DEC);
|
||||
size_t println(unsigned int, int = DEC);
|
||||
size_t println(long, int = DEC);
|
||||
size_t println(unsigned long, int = DEC);
|
||||
size_t println(double, int = 2);
|
||||
size_t println(void);
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,232 @@
|
||||
/*
|
||||
* 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 <marceloaqno@gmail.org>
|
||||
* 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 <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <limits.h>
|
||||
#include <unistd.h>
|
||||
#include <termios.h>
|
||||
#include <grp.h>
|
||||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
#include "log.h"
|
||||
#include "SerialPort.h"
|
||||
|
||||
SerialPort::SerialPort(const char *port, bool isPty) : serialPort(std::string(port)), isPty(isPty)
|
||||
{
|
||||
sd = -1;
|
||||
}
|
||||
|
||||
void SerialPort::begin(int bauds)
|
||||
{
|
||||
if (!open(bauds)) {
|
||||
mys_log(LOG_ERR, "Failed to open serial port.\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
bool SerialPort::open(int bauds)
|
||||
{
|
||||
speed_t speed;
|
||||
struct termios options;
|
||||
|
||||
if (isPty) {
|
||||
sd = posix_openpt(O_RDWR | O_NOCTTY | O_NDELAY);
|
||||
if (sd < 0) {
|
||||
mys_log(LOG_ERR, "Couldn't open a PTY: %s\n", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (grantpt(sd) != 0) {
|
||||
mys_log(LOG_ERR, "Couldn't grant permission to the PTY: %s\n", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (unlockpt(sd) != 0) {
|
||||
mys_log(LOG_ERR, "Couldn't unlock the PTY: %s\n", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
/* create a symlink with predictable name to the PTY device */
|
||||
unlink(serialPort.c_str()); // remove the symlink if it already exists
|
||||
if (symlink(ptsname(sd), serialPort.c_str()) != 0) {
|
||||
mys_log(LOG_ERR, "Couldn't create a symlink '%s' to PTY! (%d) %s\n", serialPort.c_str(), errno, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if ((sd = ::open(serialPort.c_str(), O_RDWR | O_NOCTTY | O_NDELAY)) == -1) {
|
||||
mys_log(LOG_ERR, "Unable to open the serial port %s\n", serialPort.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// nonblocking mode
|
||||
fcntl(sd, F_SETFL, FNDELAY);
|
||||
}
|
||||
|
||||
switch (bauds) {
|
||||
case 50: speed = B50 ; break ;
|
||||
case 75: speed = B75 ; break ;
|
||||
case 110: speed = B110 ; break ;
|
||||
case 134: speed = B134 ; break ;
|
||||
case 150: speed = B150 ; break ;
|
||||
case 200: speed = B200 ; break ;
|
||||
case 300: speed = B300 ; break ;
|
||||
case 600: speed = B600 ; break ;
|
||||
case 1200: speed = B1200 ; break ;
|
||||
case 1800: speed = B1800 ; break ;
|
||||
case 2400: speed = B2400 ; break ;
|
||||
case 9600: speed = B9600 ; break ;
|
||||
case 19200: speed = B19200 ; break ;
|
||||
case 38400: speed = B38400 ; break ;
|
||||
case 57600: speed = B57600 ; break ;
|
||||
case 115200: speed = B115200 ; break ;
|
||||
default: speed = B115200 ; break ;
|
||||
}
|
||||
|
||||
// Get the current options of the port
|
||||
if (tcgetattr(sd, &options) < 0) {
|
||||
mys_log(LOG_ERR, "Couldn't get term attributes: %s\n", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Clear all the options
|
||||
bzero(&options, sizeof(options));
|
||||
|
||||
// Set the baud rate
|
||||
cfsetispeed(&options, speed);
|
||||
cfsetospeed(&options, speed);
|
||||
|
||||
// Configure the device : 8 bits, no parity, no control
|
||||
options.c_cflag |= ( CLOCAL | CREAD | CS8);
|
||||
// Ignore framing errors, parity errors and BREAK condition on input.
|
||||
options.c_iflag |= ( IGNPAR | IGNBRK );
|
||||
|
||||
// Timer unused
|
||||
options.c_cc[VTIME]=0;
|
||||
// At least on character before satisfy reading
|
||||
options.c_cc[VMIN]=0;
|
||||
|
||||
// Set parameters
|
||||
if (tcsetattr(sd, TCSANOW, &options) < 0) {
|
||||
mys_log(LOG_ERR, "Couldn't set term attributes: %s\n", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
// flush
|
||||
if (tcflush(sd, TCIOFLUSH) < 0) {
|
||||
mys_log(LOG_ERR, "Couldn't flush serial: %s\n", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
usleep(10000);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SerialPort::setGroupPerm(const char *groupName)
|
||||
{
|
||||
struct group* devGrp;
|
||||
const mode_t ttyPermissions = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;
|
||||
const char *dev;
|
||||
int ret;
|
||||
|
||||
if (sd != -1 && groupName != NULL) {
|
||||
devGrp = getgrnam(groupName);
|
||||
if (devGrp == NULL) {
|
||||
mys_log(LOG_ERR, "getgrnam: %s failed. (%d) %s\n", groupName, errno, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isPty) {
|
||||
dev = ptsname(sd);
|
||||
} else {
|
||||
dev = serialPort.c_str();
|
||||
}
|
||||
|
||||
ret = chown(dev, -1, devGrp->gr_gid);
|
||||
if (ret == -1) {
|
||||
mys_log(LOG_ERR, "Could not change PTY owner! (%d) %s\n", errno, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = chmod(dev, ttyPermissions);
|
||||
if (ret != 0) {
|
||||
mys_log(LOG_ERR, "Could not change PTY permissions! (%d) %s\n", errno, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int SerialPort::available()
|
||||
{
|
||||
int nbytes = 0;
|
||||
|
||||
if (ioctl(sd, FIONREAD, &nbytes) < 0) {
|
||||
mys_log(LOG_ERR, "Failed to get byte count on serial.\n");
|
||||
exit(-1);
|
||||
}
|
||||
return nbytes;
|
||||
}
|
||||
|
||||
int SerialPort::read()
|
||||
{
|
||||
unsigned char c;
|
||||
|
||||
::read(sd, &c, 1);
|
||||
return c;
|
||||
}
|
||||
|
||||
size_t SerialPort::write(uint8_t b)
|
||||
{
|
||||
return ::write(sd, &b, 1);
|
||||
}
|
||||
|
||||
size_t SerialPort::write(const uint8_t *buffer, size_t size)
|
||||
{
|
||||
return ::write(sd, buffer, size);
|
||||
}
|
||||
|
||||
int SerialPort::peek()
|
||||
{
|
||||
FILE * f = fdopen(sd, "r+");
|
||||
int c = getc(f);
|
||||
if (c == EOF) return -1;
|
||||
ungetc(c, f);
|
||||
return c;
|
||||
}
|
||||
|
||||
void SerialPort::flush()
|
||||
{
|
||||
tcflush(sd, TCIFLUSH);
|
||||
}
|
||||
|
||||
void SerialPort::end()
|
||||
{
|
||||
close(sd);
|
||||
|
||||
if (isPty) {
|
||||
unlink(serialPort.c_str()); // remove the symlink
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* 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 <marceloaqno@gmail.org>
|
||||
* 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 SerialPort_h
|
||||
#define SerialPort_h
|
||||
|
||||
#include <string>
|
||||
#include <stdbool.h>
|
||||
#include "Stream.h"
|
||||
|
||||
/**
|
||||
* SerialPort Class
|
||||
* Class that provides the functionality of arduino Serial library
|
||||
*/
|
||||
class SerialPort : public Stream {
|
||||
|
||||
private:
|
||||
int sd; //!< @brief file descriptor number.
|
||||
std::string serialPort; //!< @brief tty name.
|
||||
bool isPty; //!< @brief true if serial is pseudo terminal.
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief SerialPort constructor.
|
||||
*/
|
||||
SerialPort(const char *port, bool isPty = false);
|
||||
/**
|
||||
* @brief Open the serial port and set the data rate in bits per second (baud).
|
||||
*
|
||||
* This function will terminate the program on an error.
|
||||
*
|
||||
* @param bauds bits per second.
|
||||
*/
|
||||
void begin(int bauds);
|
||||
/**
|
||||
* @brief Open the serial port and set the data rate in bits per second (baud).
|
||||
*
|
||||
* @param bauds bits per second.
|
||||
* @return @c true if no errors, else @c false.
|
||||
*/
|
||||
bool open(int bauds = 115200);
|
||||
/**
|
||||
* @brief Grant access to the specified system group for the serial device.
|
||||
*
|
||||
* @param groupName system group name.
|
||||
*/
|
||||
bool setGroupPerm(const char *groupName);
|
||||
/**
|
||||
* @brief Get the number of bytes available.
|
||||
*
|
||||
* Get the numberof bytes (characters) available for reading from
|
||||
* the serial port.
|
||||
*
|
||||
* @return number of bytes avalable to read.
|
||||
*/
|
||||
int available();
|
||||
/**
|
||||
* @brief Reads 1 byte of incoming serial data.
|
||||
*
|
||||
* @return first byte of incoming serial data available.
|
||||
*/
|
||||
int read();
|
||||
/**
|
||||
* @brief Writes a single byte to the serial port.
|
||||
*
|
||||
* @param b byte to write.
|
||||
* @return number of bytes written.
|
||||
*/
|
||||
size_t write(uint8_t b);
|
||||
/**
|
||||
* @brief Writes binary data to the serial port.
|
||||
*
|
||||
* @param buffer to write.
|
||||
* @param size of the buffer.
|
||||
* @return number of bytes written.
|
||||
*/
|
||||
size_t write(const uint8_t *buffer, size_t size);
|
||||
/**
|
||||
* @brief
|
||||
*
|
||||
* Returns the next byte (character) of incoming serial data without removing it from
|
||||
* the internal serial buffer.
|
||||
*
|
||||
* @return -1 if no data else character in the buffer.
|
||||
*/
|
||||
int peek();
|
||||
/**
|
||||
* @brief Remove any data remaining on the serial buffer.
|
||||
*/
|
||||
void flush();
|
||||
/**
|
||||
* @brief Disables serial communication.
|
||||
*/
|
||||
void end();
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
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
|
||||
|
||||
#include "Print.h"
|
||||
|
||||
#if !DOXYGEN
|
||||
class Server : public Print {
|
||||
public:
|
||||
virtual void begin() =0;
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,258 @@
|
||||
/*
|
||||
Stream.cpp - adds parsing methods to Stream class
|
||||
Copyright (c) 2008 David A. Mellis. 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
|
||||
|
||||
Created July 2011
|
||||
parsing functions based on TextFinder library by Michael Margolis
|
||||
Modified August 2016 by Marcelo Aquino <marceloaqno@gmail.org> for MySensors use
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <Stream.h>
|
||||
|
||||
#define PARSE_TIMEOUT 1000 // default number of milli-seconds to wait
|
||||
#define NO_SKIP_CHAR 1 // a magic char not found in a valid ASCII numeric field
|
||||
|
||||
// private method to read stream with timeout
|
||||
int Stream::timedRead() {
|
||||
int c;
|
||||
_startMillis = millis();
|
||||
do {
|
||||
c = read();
|
||||
if(c >= 0)
|
||||
return c;
|
||||
yield();
|
||||
} while(millis() - _startMillis < _timeout);
|
||||
return -1; // -1 indicates timeout
|
||||
}
|
||||
|
||||
// private method to peek stream with timeout
|
||||
int Stream::timedPeek() {
|
||||
int c;
|
||||
_startMillis = millis();
|
||||
do {
|
||||
c = peek();
|
||||
if(c >= 0)
|
||||
return c;
|
||||
yield();
|
||||
} while(millis() - _startMillis < _timeout);
|
||||
return -1; // -1 indicates timeout
|
||||
}
|
||||
|
||||
// returns peek of the next digit in the stream or -1 if timeout
|
||||
// discards non-numeric characters
|
||||
int Stream::peekNextDigit() {
|
||||
int c;
|
||||
while(1) {
|
||||
c = timedPeek();
|
||||
if(c < 0)
|
||||
return c; // timeout
|
||||
if(c == '-')
|
||||
return c;
|
||||
if(c >= '0' && c <= '9')
|
||||
return c;
|
||||
read(); // discard non-numeric
|
||||
}
|
||||
}
|
||||
|
||||
// Public Methods
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
void Stream::setTimeout(unsigned long timeout) // sets the maximum number of milliseconds to wait
|
||||
{
|
||||
_timeout = timeout;
|
||||
}
|
||||
|
||||
// find returns true if the target string is found
|
||||
bool Stream::find(const char *target) {
|
||||
return findUntil(target, (char*) "");
|
||||
}
|
||||
|
||||
// reads data from the stream until the target string of given length is found
|
||||
// returns true if target string is found, false if timed out
|
||||
bool Stream::find(const char *target, size_t length) {
|
||||
return findUntil(target, length, NULL, 0);
|
||||
}
|
||||
|
||||
// as find but search ends if the terminator string is found
|
||||
bool Stream::findUntil(const char *target, const char *terminator) {
|
||||
return findUntil(target, strlen(target), terminator, strlen(terminator));
|
||||
}
|
||||
|
||||
// reads data from the stream until the target string of the given length is found
|
||||
// search terminated if the terminator string is found
|
||||
// returns true if target string is found, false if terminated or timed out
|
||||
bool Stream::findUntil(const char *target, size_t targetLen, const char *terminator, size_t termLen) {
|
||||
size_t index = 0; // maximum target string length is 64k bytes!
|
||||
size_t termIndex = 0;
|
||||
int c;
|
||||
|
||||
if(*target == 0)
|
||||
return true; // return true if target is a null string
|
||||
while((c = timedRead()) > 0) {
|
||||
|
||||
if(c != target[index])
|
||||
index = 0; // reset index if any char does not match
|
||||
|
||||
if(c == target[index]) {
|
||||
//////Serial.print("found "); Serial.write(c); Serial.print("index now"); Serial.println(index+1);
|
||||
if(++index >= targetLen) { // return true if all chars in the target match
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if(termLen > 0 && c == terminator[termIndex]) {
|
||||
if(++termIndex >= termLen)
|
||||
return false; // return false if terminate string found before target string
|
||||
} else
|
||||
termIndex = 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// returns the first valid (long) integer value from the current position.
|
||||
// initial characters that are not digits (or the minus sign) are skipped
|
||||
// function is terminated by the first character that is not a digit.
|
||||
long Stream::parseInt() {
|
||||
return parseInt(NO_SKIP_CHAR); // terminate on first non-digit character (or timeout)
|
||||
}
|
||||
|
||||
// as above but a given skipChar is ignored
|
||||
// this allows format characters (typically commas) in values to be ignored
|
||||
long Stream::parseInt(char skipChar) {
|
||||
boolean isNegative = false;
|
||||
long value = 0;
|
||||
int c;
|
||||
|
||||
c = peekNextDigit();
|
||||
// ignore non numeric leading characters
|
||||
if(c < 0)
|
||||
return 0; // zero returned if timeout
|
||||
|
||||
do {
|
||||
if(c == skipChar)
|
||||
; // ignore this charactor
|
||||
else if(c == '-')
|
||||
isNegative = true;
|
||||
else if(c >= '0' && c <= '9') // is c a digit?
|
||||
value = value * 10 + c - '0';
|
||||
read(); // consume the character we got with peek
|
||||
c = timedPeek();
|
||||
} while((c >= '0' && c <= '9') || c == skipChar);
|
||||
|
||||
if(isNegative)
|
||||
value = -value;
|
||||
return value;
|
||||
}
|
||||
|
||||
// as parseInt but returns a floating point value
|
||||
float Stream::parseFloat() {
|
||||
return parseFloat(NO_SKIP_CHAR);
|
||||
}
|
||||
|
||||
// as above but the given skipChar is ignored
|
||||
// this allows format characters (typically commas) in values to be ignored
|
||||
float Stream::parseFloat(char skipChar) {
|
||||
boolean isNegative = false;
|
||||
boolean isFraction = false;
|
||||
long value = 0;
|
||||
int c;
|
||||
float fraction = 1.0;
|
||||
|
||||
c = peekNextDigit();
|
||||
// ignore non numeric leading characters
|
||||
if(c < 0)
|
||||
return 0; // zero returned if timeout
|
||||
|
||||
do {
|
||||
if(c == skipChar)
|
||||
; // ignore
|
||||
else if(c == '-')
|
||||
isNegative = true;
|
||||
else if(c == '.')
|
||||
isFraction = true;
|
||||
else if(c >= '0' && c <= '9') { // is c a digit?
|
||||
value = value * 10 + c - '0';
|
||||
if(isFraction)
|
||||
fraction *= 0.1;
|
||||
}
|
||||
read(); // consume the character we got with peek
|
||||
c = timedPeek();
|
||||
} while((c >= '0' && c <= '9') || c == '.' || c == skipChar);
|
||||
|
||||
if(isNegative)
|
||||
value = -value;
|
||||
if(isFraction)
|
||||
return value * fraction;
|
||||
else
|
||||
return value;
|
||||
}
|
||||
|
||||
// read characters from stream into buffer
|
||||
// terminates if length characters have been read, or timeout (see setTimeout)
|
||||
// returns the number of characters placed in the buffer
|
||||
// the buffer is NOT null terminated.
|
||||
//
|
||||
size_t Stream::readBytes(char *buffer, size_t length) {
|
||||
size_t count = 0;
|
||||
while(count < length) {
|
||||
int c = timedRead();
|
||||
if(c < 0)
|
||||
break;
|
||||
*buffer++ = (char) c;
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
// as readBytes with terminator character
|
||||
// terminates if length characters have been read, timeout, or if the terminator character detected
|
||||
// returns the number of characters placed in the buffer (0 means no valid data found)
|
||||
|
||||
size_t Stream::readBytesUntil(char terminator, char *buffer, size_t length) {
|
||||
if(length < 1)
|
||||
return 0;
|
||||
size_t index = 0;
|
||||
while(index < length) {
|
||||
int c = timedRead();
|
||||
if(c < 0 || c == terminator)
|
||||
break;
|
||||
*buffer++ = (char) c;
|
||||
index++;
|
||||
}
|
||||
return index; // return number of characters, not including null terminator
|
||||
}
|
||||
|
||||
std::string Stream::readString() {
|
||||
std::string ret;
|
||||
int c = timedRead();
|
||||
while(c >= 0) {
|
||||
ret += (char) c;
|
||||
c = timedRead();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string Stream::readStringUntil(char terminator) {
|
||||
std::string ret;
|
||||
int c = timedRead();
|
||||
while(c >= 0 && c != terminator) {
|
||||
ret += (char) c;
|
||||
c = timedRead();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
Stream.h - base class for character-based streams.
|
||||
Copyright (c) 2010 David A. Mellis. 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
|
||||
|
||||
parsing functions based on TextFinder library by Michael Margolis
|
||||
Modified August 2016 by Marcelo Aquino <marceloaqno@gmail.org> for MySensors use
|
||||
*/
|
||||
|
||||
#ifndef Stream_h
|
||||
#define Stream_h
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <string>
|
||||
#include "Print.h"
|
||||
|
||||
// compatability macros for testing
|
||||
/*
|
||||
#define getInt() parseInt()
|
||||
#define getInt(skipChar) parseInt(skipchar)
|
||||
#define getFloat() parseFloat()
|
||||
#define getFloat(skipChar) parseFloat(skipChar)
|
||||
#define getString( pre_string, post_string, buffer, length)
|
||||
readBytesBetween( pre_string, terminator, buffer, length)
|
||||
*/
|
||||
|
||||
#if !DOXYGEN
|
||||
class Stream: public Print {
|
||||
protected:
|
||||
unsigned long _timeout; // number of milliseconds to wait for the next char before aborting timed read
|
||||
unsigned long _startMillis; // used for timeout measurement
|
||||
int timedRead(); // private method to read stream with timeout
|
||||
int timedPeek(); // private method to peek stream with timeout
|
||||
int peekNextDigit(); // returns the next numeric digit in the stream or -1 if timeout
|
||||
|
||||
public:
|
||||
virtual int available() = 0;
|
||||
virtual int read() = 0;
|
||||
virtual int peek() = 0;
|
||||
virtual void flush() = 0;
|
||||
|
||||
Stream() {
|
||||
_timeout = 1000;
|
||||
}
|
||||
|
||||
// parsing methods
|
||||
|
||||
void setTimeout(unsigned long timeout); // sets maximum milliseconds to wait for stream data, default is 1 second
|
||||
|
||||
bool find(const char *target); // reads data from the stream until the target string is found
|
||||
bool find(uint8_t *target) {
|
||||
return find((char *) target);
|
||||
}
|
||||
// returns true if target string is found, false if timed out (see setTimeout)
|
||||
|
||||
bool find(const char *target, size_t length); // reads data from the stream until the target string of given length is found
|
||||
bool find(const uint8_t *target, size_t length) {
|
||||
return find((char *) target, length);
|
||||
}
|
||||
// returns true if target string is found, false if timed out
|
||||
|
||||
bool find(char target) { return find (&target, 1); }
|
||||
|
||||
bool findUntil(const char *target, const char *terminator); // as find but search ends if the terminator string is found
|
||||
bool findUntil(const uint8_t *target, const char *terminator) {
|
||||
return findUntil((char *) target, terminator);
|
||||
}
|
||||
|
||||
bool findUntil(const char *target, size_t targetLen, const char *terminate, size_t termLen); // as above but search ends if the terminate string is found
|
||||
bool findUntil(const uint8_t *target, size_t targetLen, const char *terminate, size_t termLen) {
|
||||
return findUntil((char *) target, targetLen, terminate, termLen);
|
||||
}
|
||||
|
||||
long parseInt(); // returns the first valid (long) integer value from the current position.
|
||||
// initial characters that are not digits (or the minus sign) are skipped
|
||||
// integer is terminated by the first character that is not a digit.
|
||||
|
||||
float parseFloat(); // float version of parseInt
|
||||
|
||||
virtual size_t readBytes(char *buffer, size_t length); // read chars from stream into buffer
|
||||
virtual size_t readBytes(uint8_t *buffer, size_t length) {
|
||||
return readBytes((char *) buffer, length);
|
||||
}
|
||||
// terminates if length characters have been read or timeout (see setTimeout)
|
||||
// returns the number of characters placed in the buffer (0 means no valid data found)
|
||||
|
||||
size_t readBytesUntil(char terminator, char *buffer, size_t length); // as readBytes with terminator character
|
||||
size_t readBytesUntil(char terminator, uint8_t *buffer, size_t length) {
|
||||
return readBytesUntil(terminator, (char *) buffer, length);
|
||||
}
|
||||
// terminates if length characters have been read, timeout, or if the terminator character detected
|
||||
// returns the number of characters placed in the buffer (0 means no valid data found)
|
||||
|
||||
// Arduino String functions to be added here
|
||||
std::string readString();
|
||||
std::string readStringUntil(char terminator);
|
||||
|
||||
protected:
|
||||
long parseInt(char skipChar); // as above but the given skipChar is ignored
|
||||
// as above but the given skipChar is ignored
|
||||
// this allows format characters (typically commas) in values to be ignored
|
||||
|
||||
float parseFloat(char skipChar); // as above but the given skipChar is ignored
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,55 @@
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include <stdlib.h>
|
||||
#include "Arduino.h"
|
||||
|
||||
// For millis()
|
||||
static unsigned long millis_at_start = 0;
|
||||
|
||||
void yield(void) {}
|
||||
|
||||
unsigned long millis(void)
|
||||
{
|
||||
timeval curTime;
|
||||
|
||||
if (millis_at_start == 0) {
|
||||
gettimeofday(&curTime, NULL);
|
||||
millis_at_start = curTime.tv_sec;
|
||||
}
|
||||
|
||||
gettimeofday(&curTime, NULL);
|
||||
return ((curTime.tv_sec - millis_at_start) * 1000) + (curTime.tv_usec / 1000);
|
||||
}
|
||||
|
||||
void _delay_ms(unsigned int millis)
|
||||
{
|
||||
struct timespec sleeper;
|
||||
|
||||
sleeper.tv_sec = (time_t)(millis / 1000);
|
||||
sleeper.tv_nsec = (long)(millis % 1000) * 1000000;
|
||||
nanosleep(&sleeper, NULL);
|
||||
}
|
||||
|
||||
void randomSeed(unsigned long seed)
|
||||
{
|
||||
if (seed != 0) {
|
||||
srand(seed);
|
||||
}
|
||||
}
|
||||
|
||||
long randMax(long howbig)
|
||||
{
|
||||
if (howbig == 0) {
|
||||
return 0;
|
||||
}
|
||||
return rand() % howbig;
|
||||
}
|
||||
|
||||
long randMinMax(long howsmall, long howbig)
|
||||
{
|
||||
if (howsmall >= howbig) {
|
||||
return howsmall;
|
||||
}
|
||||
long diff = howbig - howsmall;
|
||||
return randMax(diff) + howsmall;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#include <stdio.h>
|
||||
#include <syslog.h>
|
||||
#include <stdarg.h>
|
||||
#include "log.h"
|
||||
|
||||
void mys_log_v(int level, const char *fmt, va_list args)
|
||||
{
|
||||
vsyslog(level, fmt, args);
|
||||
}
|
||||
|
||||
void
|
||||
#ifdef __GNUC__
|
||||
__attribute__((format(printf, 2, 3)))
|
||||
#endif
|
||||
mys_log(int level, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
mys_log_v(level, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#ifndef LOG_H
|
||||
#define LOG_H
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <syslog.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern void mys_log_v(int level, const char *fmt, va_list args);
|
||||
extern void mys_log(int level, const char *fmt, ...) __attribute__((format(printf,2,3)));
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
noniso.cpp - replacements for non-ISO functions used by Arduino core
|
||||
Copyright © 2016 Ivan Grokhotkov
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
Modified August 2016 by Marcelo Aquino <marceloaqno@gmail.org> for MySensors use
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <math.h>
|
||||
#include "stdlib_noniso.h"
|
||||
|
||||
char* utoa(unsigned value, char* result, int base) {
|
||||
if(base < 2 || base > 16) {
|
||||
*result = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
char* out = result;
|
||||
unsigned quotient = value;
|
||||
|
||||
do {
|
||||
const unsigned tmp = quotient / base;
|
||||
*out = "0123456789abcdef"[quotient - (tmp * base)];
|
||||
++out;
|
||||
quotient = tmp;
|
||||
} while(quotient);
|
||||
|
||||
reverse(result, out);
|
||||
*out = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
char* itoa(int value, char* result, int base) {
|
||||
if(base < 2 || base > 16) {
|
||||
*result = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
char* out = result;
|
||||
int quotient = abs(value);
|
||||
|
||||
do {
|
||||
const int tmp = quotient / base;
|
||||
*out = "0123456789abcdef"[quotient - (tmp * base)];
|
||||
++out;
|
||||
quotient = tmp;
|
||||
} while(quotient);
|
||||
|
||||
// Apply negative sign
|
||||
if(value < 0)
|
||||
*out++ = '-';
|
||||
|
||||
reverse(result, out);
|
||||
*out = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
int atoi(const char* s) {
|
||||
return (int) atol(s);
|
||||
}
|
||||
|
||||
long atol(const char* s) {
|
||||
char * tmp;
|
||||
return strtol(s, &tmp, 10);
|
||||
}
|
||||
|
||||
double atof(const char* s) {
|
||||
char * tmp;
|
||||
return strtod(s, &tmp);
|
||||
}
|
||||
|
||||
void reverse(char* begin, char* end) {
|
||||
char *is = begin;
|
||||
char *ie = end - 1;
|
||||
while(is < ie) {
|
||||
char tmp = *ie;
|
||||
*ie = *is;
|
||||
*is = tmp;
|
||||
++is;
|
||||
--ie;
|
||||
}
|
||||
}
|
||||
|
||||
char* ltoa(long value, char* result, int base) {
|
||||
if(base < 2 || base > 16) {
|
||||
*result = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
char* out = result;
|
||||
long quotient = abs(value);
|
||||
|
||||
do {
|
||||
const long tmp = quotient / base;
|
||||
*out = "0123456789abcdef"[quotient - (tmp * base)];
|
||||
++out;
|
||||
quotient = tmp;
|
||||
} while(quotient);
|
||||
|
||||
// Apply negative sign
|
||||
if(value < 0)
|
||||
*out++ = '-';
|
||||
|
||||
reverse(result, out);
|
||||
*out = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
char* ultoa(unsigned long value, char* result, int base) {
|
||||
if(base < 2 || base > 16) {
|
||||
*result = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
char* out = result;
|
||||
unsigned long quotient = value;
|
||||
|
||||
do {
|
||||
const unsigned long tmp = quotient / base;
|
||||
*out = "0123456789abcdef"[quotient - (tmp * base)];
|
||||
++out;
|
||||
quotient = tmp;
|
||||
} while(quotient);
|
||||
|
||||
reverse(result, out);
|
||||
*out = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
char* dtostrf (double val, signed char width, unsigned char prec, char *s) {
|
||||
sprintf(s,"%*.*f", width, prec, val);
|
||||
return s;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
stdlib_noniso.h - nonstandard (but usefull) conversion functions
|
||||
|
||||
Copyright (c) 2014 Ivan Grokhotkov. All rights reserved.
|
||||
This file is part of the esp8266 core for Arduino environment.
|
||||
|
||||
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 STDLIB_NONISO_H
|
||||
#define STDLIB_NONISO_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"{
|
||||
#endif
|
||||
|
||||
int atoi(const char *s);
|
||||
|
||||
long atol(const char* s);
|
||||
|
||||
double atof(const char* s);
|
||||
|
||||
char* itoa (int val, char *s, int radix);
|
||||
|
||||
char* ltoa (long val, char *s, int radix);
|
||||
|
||||
char* utoa (unsigned int val, char *s, int radix);
|
||||
|
||||
char* ultoa (unsigned long val, char *s, int radix);
|
||||
|
||||
char* dtostrf (double val, signed char width, unsigned char prec, char *s);
|
||||
|
||||
void reverse(char* begin, char* end);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
+43
-10
@@ -29,8 +29,13 @@ LOCAL uint8_t MY_RF24_NODE_ADDRESS = AUTO;
|
||||
LOCAL RF24_receiveCallbackType RF24_receiveCallback = NULL;
|
||||
#endif
|
||||
|
||||
#ifdef LINUX_ARCH_RASPBERRYPI
|
||||
uint8_t spi_rxbuff[32+1] ; //SPI receive buffer (payload max 32 bytes)
|
||||
uint8_t spi_txbuff[32+1] ; //SPI transmit buffer (payload max 32 bytes + 1 byte for the command)
|
||||
#endif
|
||||
|
||||
LOCAL void RF24_csn(bool level) {
|
||||
digitalWrite(MY_RF24_CS_PIN, level);
|
||||
digitalWrite(MY_RF24_CS_PIN, level);
|
||||
}
|
||||
|
||||
LOCAL void RF24_ce(bool level) {
|
||||
@@ -38,6 +43,7 @@ LOCAL void RF24_ce(bool level) {
|
||||
}
|
||||
|
||||
LOCAL uint8_t RF24_spiMultiByteTransfer(uint8_t cmd, uint8_t* buf, uint8_t len, bool aReadMode) {
|
||||
uint8_t status;
|
||||
uint8_t* current = buf;
|
||||
#if !defined(MY_SOFTSPI)
|
||||
_SPI.beginTransaction(SPISettings(MY_RF24_SPI_MAX_SPEED, MY_RF24_SPI_DATA_ORDER, MY_RF24_SPI_DATA_MODE));
|
||||
@@ -45,13 +51,40 @@ LOCAL uint8_t RF24_spiMultiByteTransfer(uint8_t cmd, uint8_t* buf, uint8_t len,
|
||||
RF24_csn(LOW);
|
||||
// timing
|
||||
delayMicroseconds(10);
|
||||
uint8_t status = _SPI.transfer( cmd );
|
||||
while ( len-- ) {
|
||||
if (aReadMode) {
|
||||
status = _SPI.transfer( NOP );
|
||||
if(buf != NULL) *current++ = status;
|
||||
} else status = _SPI.transfer(*current++);
|
||||
}
|
||||
#ifdef LINUX_ARCH_RASPBERRYPI
|
||||
uint8_t * prx = spi_rxbuff;
|
||||
uint8_t * ptx = spi_txbuff;
|
||||
uint8_t size = len + 1; // Add register value to transmit buffer
|
||||
|
||||
*ptx++ = cmd;
|
||||
while ( len-- ) {
|
||||
if (aReadMode) {
|
||||
*ptx++ = NOP ;
|
||||
} else {
|
||||
*ptx++ = *current++;
|
||||
}
|
||||
}
|
||||
_SPI.transfernb( (char *) spi_txbuff, (char *) spi_rxbuff, size);
|
||||
if (aReadMode) {
|
||||
if (size == 2) {
|
||||
status = *++prx; // result is 2nd byte of receive buffer
|
||||
} else {
|
||||
status = *prx++; // status is 1st byte of receive buffer
|
||||
// decrement before to skip status byte
|
||||
while (--size) { *buf++ = *prx++; }
|
||||
}
|
||||
} else {
|
||||
status = *prx; // status is 1st byte of receive buffer
|
||||
}
|
||||
#else
|
||||
status = _SPI.transfer( cmd );
|
||||
while ( len-- ) {
|
||||
if (aReadMode) {
|
||||
status = _SPI.transfer( NOP );
|
||||
if (buf != NULL) *current++ = status;
|
||||
} else status = _SPI.transfer(*current++);
|
||||
}
|
||||
#endif
|
||||
RF24_csn(HIGH);
|
||||
#if !defined(MY_SOFTSPI)
|
||||
_SPI.endTransaction();
|
||||
@@ -268,7 +301,7 @@ LOCAL void RF24_irqHandler( void )
|
||||
// rx coming in during our stay will not be handled and will cause characters to be lost.
|
||||
// As a workaround we re-enable interrupts to allow nested processing of other interrupts.
|
||||
// Our own handler is disconnected to prevent recursive calling of this handler.
|
||||
#ifdef MY_GATEWAY_SERIAL
|
||||
#if defined(MY_GATEWAY_SERIAL) && !defined(__linux__)
|
||||
detachInterrupt(digitalPinToInterrupt(MY_RF24_IRQ_PIN));
|
||||
interrupts();
|
||||
#endif
|
||||
@@ -283,7 +316,7 @@ LOCAL void RF24_irqHandler( void )
|
||||
RF24_receiveCallback(); // Must call RF24_readMessage(), which will clear RX_DR IRQ !
|
||||
}
|
||||
// Restore our interrupt handler.
|
||||
#ifdef MY_GATEWAY_SERIAL
|
||||
#if defined(MY_GATEWAY_SERIAL) && !defined(__linux__)
|
||||
noInterrupts();
|
||||
attachInterrupt(digitalPinToInterrupt(MY_RF24_IRQ_PIN), RF24_irqHandler, FALLING);
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* 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 <marceloaqno@gmail.org>
|
||||
* 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 TMRh20 RF24 library, Copyright (c) 2015 Charles-Henri Hallard <tmrh20@gmail.com>
|
||||
*/
|
||||
|
||||
#include "SPI.h"
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
#include "log.h"
|
||||
|
||||
static pthread_mutex_t spiMutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
// Declare a single default instance
|
||||
SPIClass SPI = SPIClass();
|
||||
|
||||
uint8_t SPIClass::initialized = 0;
|
||||
|
||||
SPIClass::SPIClass() {
|
||||
if (!bcm2835_init()) {
|
||||
mys_log(LOG_ERR, "Failed to initialized bcm2835.\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
SPIClass::~SPIClass() {}
|
||||
|
||||
uint8_t SPIClass::isInitialized() {
|
||||
return initialized;
|
||||
}
|
||||
|
||||
void SPIClass::begin() {
|
||||
if (!initialized) {
|
||||
if (!bcm2835_spi_begin()) {
|
||||
mys_log(LOG_ERR, "You need to be root to use SPI.\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
initialized++; // reference count
|
||||
}
|
||||
|
||||
void SPIClass::end() {
|
||||
if (initialized) {
|
||||
initialized--;
|
||||
}
|
||||
|
||||
if (!initialized) {
|
||||
// End the SPI
|
||||
bcm2835_spi_end();
|
||||
}
|
||||
}
|
||||
|
||||
void SPIClass::setBitOrder(uint8_t bit_order) {
|
||||
bcm2835_spi_setBitOrder(bit_order);
|
||||
}
|
||||
|
||||
void SPIClass::setDataMode(uint8_t data_mode) {
|
||||
bcm2835_spi_setDataMode(data_mode);
|
||||
}
|
||||
|
||||
void SPIClass::setClockDivider(uint16_t divider) {
|
||||
bcm2835_spi_setClockDivider(divider);
|
||||
}
|
||||
|
||||
void SPIClass::chipSelect(int csn_pin) {
|
||||
if (csn_pin == RPI_GPIO_P1_26) csn_pin = BCM2835_SPI_CS1;
|
||||
else if (csn_pin == RPI_GPIO_P1_24) csn_pin = BCM2835_SPI_CS0;
|
||||
else csn_pin = BCM2835_SPI_CS0;
|
||||
bcm2835_spi_chipSelect(csn_pin);
|
||||
delayMicroseconds(5);
|
||||
}
|
||||
|
||||
void SPIClass::beginTransaction(SPISettings settings) {
|
||||
pthread_mutex_lock(&spiMutex);
|
||||
setBitOrder(settings.border);
|
||||
setDataMode(settings.dmode);
|
||||
setClockDivider(settings.cdiv);
|
||||
}
|
||||
|
||||
void SPIClass::endTransaction() {
|
||||
pthread_mutex_unlock(&spiMutex);
|
||||
}
|
||||
|
||||
void SPIClass::usingInterrupt(uint8_t interruptNumber) {
|
||||
(void)interruptNumber;
|
||||
}
|
||||
|
||||
void SPIClass::notUsingInterrupt(uint8_t interruptNumber) {
|
||||
(void)interruptNumber;
|
||||
}
|
||||
@@ -0,0 +1,231 @@
|
||||
/*
|
||||
* 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 <marceloaqno@gmail.org>
|
||||
* 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 TMRh20 RF24 library, Copyright (c) 2015 Charles-Henri Hallard <tmrh20@gmail.com>
|
||||
*/
|
||||
#ifndef _SPI_H_
|
||||
#define _SPI_H_
|
||||
|
||||
#include <stdio.h>
|
||||
#include "bcm2835.h"
|
||||
|
||||
#define SPI_HAS_TRANSACTION
|
||||
|
||||
// SPI Clock divider
|
||||
#define SPI_CLOCK_DIV2 2
|
||||
#define SPI_CLOCK_DIV4 4
|
||||
#define SPI_CLOCK_DIV8 8
|
||||
#define SPI_CLOCK_DIV16 16
|
||||
#define SPI_CLOCK_DIV32 32
|
||||
#define SPI_CLOCK_DIV64 64
|
||||
#define SPI_CLOCK_DIV128 128
|
||||
|
||||
// SPI Data mode
|
||||
#define SPI_MODE0 BCM2835_SPI_MODE0
|
||||
#define SPI_MODE1 BCM2835_SPI_MODE1
|
||||
#define SPI_MODE2 BCM2835_SPI_MODE2
|
||||
#define SPI_MODE3 BCM2835_SPI_MODE3
|
||||
|
||||
/**
|
||||
* SPISettings class
|
||||
*/
|
||||
class SPISettings {
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief SPISettings constructor.
|
||||
*
|
||||
* Default clock speed is 8Mhz.
|
||||
*/
|
||||
SPISettings() {
|
||||
init(BCM2835_SPI_CLOCK_DIVIDER_32, BCM2835_SPI_BIT_ORDER_MSBFIRST, BCM2835_SPI_MODE0);
|
||||
}
|
||||
/**
|
||||
* @brief SPISettings constructor.
|
||||
*
|
||||
* @param clock SPI clock speed in Hz.
|
||||
* @param bitOrder SPI bit order.
|
||||
* @param dataMode SPI data mode.
|
||||
*/
|
||||
SPISettings(uint32_t clock, uint8_t bitOrder, uint8_t dataMode) {
|
||||
uint16_t divider;
|
||||
|
||||
switch (clock) {
|
||||
case 500000:
|
||||
divider = BCM2835_SPI_CLOCK_DIVIDER_512;
|
||||
break;
|
||||
case 1000000:
|
||||
divider = BCM2835_SPI_CLOCK_DIVIDER_256;
|
||||
break;
|
||||
case 2000000:
|
||||
divider = BCM2835_SPI_CLOCK_DIVIDER_128;
|
||||
break;
|
||||
case 4000000:
|
||||
divider = BCM2835_SPI_CLOCK_DIVIDER_64;
|
||||
break;
|
||||
case 8000000:
|
||||
divider = BCM2835_SPI_CLOCK_DIVIDER_32;
|
||||
break;
|
||||
case 16000000:
|
||||
divider = BCM2835_SPI_CLOCK_DIVIDER_16;
|
||||
break;
|
||||
default:
|
||||
// 8Mhz
|
||||
divider = BCM2835_SPI_CLOCK_DIVIDER_32;
|
||||
}
|
||||
|
||||
init(divider, bitOrder, dataMode);
|
||||
}
|
||||
|
||||
uint16_t cdiv; //!< @brief SPI clock divider.
|
||||
uint8_t border; //!< @brief SPI bit order.
|
||||
uint8_t dmode; //!< @brief SPI data mode.
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Initialized class members.
|
||||
*
|
||||
* @param divider SPI clock divider.
|
||||
* @param bitOrder SPI bit order.
|
||||
* @param dataMode SPI data mode.
|
||||
*/
|
||||
void init(uint16_t divider, uint8_t bitOrder, uint8_t dataMode) {
|
||||
cdiv = divider;
|
||||
border = bitOrder;
|
||||
dmode = dataMode;
|
||||
}
|
||||
|
||||
friend class SPIClass;
|
||||
};
|
||||
|
||||
/**
|
||||
* SPIClass class
|
||||
*/
|
||||
class SPIClass {
|
||||
|
||||
private:
|
||||
static uint8_t initialized; //!< @brief SPI initialized flag.
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief SPIClass constructor.
|
||||
*/
|
||||
SPIClass();
|
||||
/**
|
||||
* @brief SPIClass destructor.
|
||||
*/
|
||||
virtual ~SPIClass();
|
||||
/**
|
||||
* @brief Checks if SPI was initialized.
|
||||
*
|
||||
* @return 0 if wasn't initialized, else 1 or more.
|
||||
*/
|
||||
static uint8_t isInitialized();
|
||||
/**
|
||||
* @brief Send and receive a byte.
|
||||
*
|
||||
* @param data to send.
|
||||
* @return byte received.
|
||||
*/
|
||||
inline static uint8_t transfer(uint8_t data);
|
||||
/**
|
||||
* @brief Send and receive a number of bytes.
|
||||
*
|
||||
* @param tbuf Sending buffer.
|
||||
* @param rbuf Receive buffer.
|
||||
* @param len Buffer length.
|
||||
*/
|
||||
inline static void transfernb(char* tbuf, char* rbuf, uint32_t len);
|
||||
/**
|
||||
* @brief Send and receive a number of bytes.
|
||||
*
|
||||
* @param buf Buffer to read from and write to.
|
||||
* @param len Buffer length.
|
||||
*/
|
||||
inline static void transfern(char* buf, uint32_t len);
|
||||
/**
|
||||
* @brief Start SPI operations.
|
||||
*/
|
||||
static void begin();
|
||||
/**
|
||||
* @brief End SPI operations.
|
||||
*/
|
||||
static void end();
|
||||
/**
|
||||
* @brief Sets the SPI bit order.
|
||||
*
|
||||
* @param bit_order The desired bit order.
|
||||
*/
|
||||
static void setBitOrder(uint8_t bit_order);
|
||||
/**
|
||||
* @brief Sets the SPI data mode.
|
||||
*
|
||||
* @param data_mode The desired data mode.
|
||||
*/
|
||||
static void setDataMode(uint8_t data_mode);
|
||||
/**
|
||||
* @brief Sets the SPI clock divider and therefore the SPI clock speed.
|
||||
*
|
||||
* @param divider The desired SPI clock divider.
|
||||
*/
|
||||
static void setClockDivider(uint16_t divider);
|
||||
/**
|
||||
* @brief Sets the chip select pin.
|
||||
*
|
||||
* @param csn_pin Specifies the CS pin.
|
||||
*/
|
||||
static void chipSelect(int csn_pin);
|
||||
/**
|
||||
* @brief Start SPI transaction.
|
||||
*
|
||||
* @param settings for SPI.
|
||||
*/
|
||||
static void beginTransaction(SPISettings settings);
|
||||
/**
|
||||
* @brief End SPI transaction.
|
||||
*/
|
||||
static void endTransaction();
|
||||
/**
|
||||
* @brief Not implemented.
|
||||
*
|
||||
* @param interruptNumber ignored parameter.
|
||||
*/
|
||||
static void usingInterrupt(uint8_t interruptNumber);
|
||||
/**
|
||||
* @brief Not implemented.
|
||||
*
|
||||
* @param interruptNumber ignored parameter.
|
||||
*/
|
||||
static void notUsingInterrupt(uint8_t interruptNumber);
|
||||
};
|
||||
|
||||
uint8_t SPIClass::transfer(uint8_t data) {
|
||||
return bcm2835_spi_transfer(data);
|
||||
}
|
||||
|
||||
void SPIClass::transfernb(char* tbuf, char* rbuf, uint32_t len) {
|
||||
bcm2835_spi_transfernb( tbuf, rbuf, len);
|
||||
}
|
||||
|
||||
void SPIClass::transfern(char* buf, uint32_t len) {
|
||||
transfernb(buf, buf, len);
|
||||
}
|
||||
|
||||
extern SPIClass SPI;
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* piHiPri:
|
||||
* Simple way to get your program running at high priority
|
||||
* with realtime schedulling.
|
||||
*
|
||||
* Copyright (c) 2012 Gordon Henderson
|
||||
***********************************************************************
|
||||
* This file is part of wiringPi:
|
||||
* https://projects.drogon.net/raspberry-pi/wiringpi/
|
||||
*
|
||||
* wiringPi 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 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* wiringPi 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 wiringPi.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
***********************************************************************
|
||||
*/
|
||||
|
||||
#include <sched.h>
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
* piHiPri:
|
||||
* Attempt to set a high priority schedulling for the running program
|
||||
*********************************************************************************
|
||||
*/
|
||||
|
||||
int piHiPri (const int pri)
|
||||
{
|
||||
struct sched_param sched ;
|
||||
|
||||
memset (&sched, 0, sizeof(sched)) ;
|
||||
|
||||
if (pri > sched_get_priority_max (SCHED_RR))
|
||||
sched.sched_priority = sched_get_priority_max (SCHED_RR) ;
|
||||
else
|
||||
sched.sched_priority = pri ;
|
||||
|
||||
return sched_setscheduler (0, SCHED_RR, &sched) ;
|
||||
}
|
||||
@@ -0,0 +1,330 @@
|
||||
/*
|
||||
* 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 <marceloaqno@gmail.org>
|
||||
* 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 wiringPi Copyright (c) 2012 Gordon Henderson.
|
||||
*/
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <poll.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <stropts.h>
|
||||
#include <errno.h>
|
||||
#include "rpi_util.h"
|
||||
#include "SPI.h"
|
||||
#include "log.h"
|
||||
|
||||
extern "C" {
|
||||
int piHiPri(const int pri);
|
||||
}
|
||||
|
||||
struct ThreadArgs {
|
||||
void (*func)();
|
||||
int gpioPin;
|
||||
};
|
||||
|
||||
static pthread_mutex_t intMutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
static pthread_t *threadIds[64] = {NULL};
|
||||
|
||||
// sysFds:
|
||||
// Map a file descriptor from the /sys/class/gpio/gpioX/value
|
||||
static int sysFds[64] =
|
||||
{
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
};
|
||||
|
||||
#ifdef __RPI_BPLUS
|
||||
static uint8_t physToGpio[64] =
|
||||
{
|
||||
255, // 0
|
||||
255, 255, // 1, 2
|
||||
2, 255,
|
||||
3, 255,
|
||||
4, 14,
|
||||
255, 15,
|
||||
17, 18,
|
||||
27, 255,
|
||||
22, 23,
|
||||
255, 24,
|
||||
10, 255,
|
||||
9, 25,
|
||||
11, 8,
|
||||
255, 7, // 25, 26
|
||||
// B+
|
||||
0, 1,
|
||||
5, 255,
|
||||
6, 12,
|
||||
13, 255,
|
||||
19, 16,
|
||||
26, 20,
|
||||
255, 21,
|
||||
// the P5 connector on the Rev 2 boards:
|
||||
255, 255,
|
||||
255, 255,
|
||||
255, 255,
|
||||
255, 255,
|
||||
255, 255,
|
||||
28, 29,
|
||||
30, 31,
|
||||
255, 255,
|
||||
255, 255,
|
||||
255, 255,
|
||||
255, 255,
|
||||
};
|
||||
#else
|
||||
static uint8_t physToGpio[64] =
|
||||
{
|
||||
255, // 0
|
||||
255, 255, // 1, 2
|
||||
0, 255,
|
||||
1, 255,
|
||||
4, 14,
|
||||
255, 15,
|
||||
17, 18,
|
||||
21, 255,
|
||||
22, 23,
|
||||
255, 24,
|
||||
10, 255,
|
||||
9, 25,
|
||||
11, 8,
|
||||
255, 7, // 25, 26
|
||||
255, 255, 255, 255, 255, // ... 31
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // ... 47
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // ... 63
|
||||
};
|
||||
#endif
|
||||
|
||||
void *interruptHandler(void *args) {
|
||||
int fd, ret;
|
||||
struct pollfd polls;
|
||||
char c;
|
||||
struct ThreadArgs *arguments = (struct ThreadArgs *)args;
|
||||
int gpioPin = arguments->gpioPin;
|
||||
void (*func)() = arguments->func;
|
||||
delete arguments;
|
||||
|
||||
(void)piHiPri(55); // Only effective if we run as root
|
||||
|
||||
if ((fd = sysFds[gpioPin]) == -1) {
|
||||
mys_log(LOG_ERR, "Failed to attach interrupt for pin %d\n", gpioPin);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Setup poll structure
|
||||
polls.fd = fd;
|
||||
polls.events = POLLPRI;
|
||||
|
||||
while (1) {
|
||||
// Wait for it ...
|
||||
ret = poll(&polls, 1, -1);
|
||||
if (ret < 0) {
|
||||
mys_log(LOG_ERR, "Error waiting for interrupt: %s\n", strerror(errno));
|
||||
break;
|
||||
}
|
||||
// Do a dummy read to clear the interrupt
|
||||
// A one character read appars to be enough.
|
||||
// Followed by a seek to reset it.
|
||||
(void)read (fd, &c, 1) ;
|
||||
lseek (fd, 0, SEEK_SET) ;
|
||||
// Call user function.
|
||||
func();
|
||||
}
|
||||
|
||||
close(fd);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void rpi_util::pinMode(uint8_t physPin, uint8_t mode) {
|
||||
uint8_t gpioPin = (physPin > 63)? 255 : physToGpio[physPin];
|
||||
if (gpioPin == 255) {
|
||||
mys_log(LOG_ERR, "pinMode: invalid pin: %d\n", physPin);
|
||||
return;
|
||||
}
|
||||
// Check if SPI is in use and target pin is related to SPI
|
||||
if (SPIClass::isInitialized() && gpioPin >= RPI_GPIO_P1_26 && gpioPin <= RPI_GPIO_P1_23) {
|
||||
return;
|
||||
} else {
|
||||
bcm2835_gpio_fsel(gpioPin, mode);
|
||||
}
|
||||
}
|
||||
|
||||
void rpi_util::digitalWrite(uint8_t physPin, uint8_t value) {
|
||||
uint8_t gpioPin = (physPin > 63)? 255 : physToGpio[physPin];
|
||||
if (gpioPin == 255) {
|
||||
mys_log(LOG_ERR, "digitalWrite: invalid pin: %d\n", physPin);
|
||||
return;
|
||||
}
|
||||
// Check if SPI is in use and target pin is related to SPI
|
||||
if (SPIClass::isInitialized() && gpioPin >= RPI_GPIO_P1_26 && gpioPin <= RPI_GPIO_P1_23) {
|
||||
if (value == LOW && (gpioPin == RPI_GPIO_P1_24 || gpioPin == RPI_GPIO_P1_26)) {
|
||||
SPI.chipSelect(gpioPin);
|
||||
}
|
||||
} else {
|
||||
bcm2835_gpio_write(gpioPin, value);
|
||||
// Delay to allow any change in state to be reflected in the LEVn, register bit.
|
||||
delayMicroseconds(1);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t rpi_util::digitalRead(uint8_t physPin) {
|
||||
uint8_t gpioPin = (physPin > 63)? 255 : physToGpio[physPin];
|
||||
if (gpioPin == 255) {
|
||||
mys_log(LOG_ERR, "digitalRead: invalid pin: %d\n", physPin);
|
||||
return 0;
|
||||
}
|
||||
// Check if SPI is in use and target pin is related to SPI
|
||||
if (SPIClass::isInitialized() && gpioPin >= RPI_GPIO_P1_26 && gpioPin <= RPI_GPIO_P1_23) {
|
||||
return 0;
|
||||
} else {
|
||||
return bcm2835_gpio_lev(gpioPin);
|
||||
}
|
||||
}
|
||||
|
||||
void rpi_util::attachInterrupt(uint8_t physPin, void (*func)(), uint8_t mode) {
|
||||
FILE *fd;
|
||||
char fName[40];
|
||||
char c;
|
||||
int count, i;
|
||||
|
||||
uint8_t gpioPin = (physPin > 63)? 255 : physToGpio[physPin];
|
||||
if (gpioPin == 255) {
|
||||
mys_log(LOG_ERR, "attachInterrupt: invalid pin: %d\n", physPin);
|
||||
return;
|
||||
}
|
||||
|
||||
if (threadIds[gpioPin] == NULL) {
|
||||
threadIds[gpioPin] = new pthread_t;
|
||||
} else {
|
||||
// Cancel the existing thread for that pin
|
||||
pthread_cancel(*threadIds[gpioPin]);
|
||||
// Wait a bit
|
||||
delay(1L);
|
||||
}
|
||||
|
||||
// Export pin for interrupt
|
||||
if ((fd = fopen("/sys/class/gpio/export", "w")) == NULL) {
|
||||
mys_log(LOG_ERR, "attachInterrupt: Unable to export pin %d for interrupt: %s\n", physPin, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
fprintf(fd, "%d\n", gpioPin);
|
||||
fclose(fd);
|
||||
|
||||
// Wait a bit the system to create /sys/class/gpio/gpio<GPIO number>
|
||||
delay(1L);
|
||||
|
||||
snprintf(fName, sizeof(fName), "/sys/class/gpio/gpio%d/direction", gpioPin) ;
|
||||
if ((fd = fopen (fName, "w")) == NULL) {
|
||||
fprintf (stderr, "attachInterrupt: Unable to open GPIO direction interface for pin %d: %s\n", physPin, strerror(errno));
|
||||
exit(1) ;
|
||||
}
|
||||
fprintf(fd, "in\n") ;
|
||||
fclose(fd) ;
|
||||
|
||||
snprintf(fName, sizeof(fName), "/sys/class/gpio/gpio%d/edge", gpioPin) ;
|
||||
if ((fd = fopen(fName, "w")) == NULL) {
|
||||
fprintf (stderr, "attachInterrupt: Unable to open GPIO edge interface for pin %d: %s\n", physPin, strerror(errno));
|
||||
exit(1) ;
|
||||
}
|
||||
switch (mode) {
|
||||
case CHANGE: fprintf(fd, "both\n"); break;
|
||||
case FALLING: fprintf(fd, "falling\n"); break;
|
||||
case RISING: fprintf(fd, "rising\n"); break;
|
||||
case NONE: fprintf(fd, "none\n"); break;
|
||||
default:
|
||||
mys_log(LOG_ERR, "attachInterrupt: Invalid mode\n");
|
||||
fclose(fd);
|
||||
return;
|
||||
}
|
||||
fclose(fd);
|
||||
|
||||
if (sysFds[gpioPin] == -1) {
|
||||
snprintf(fName, sizeof(fName), "/sys/class/gpio/gpio%d/value", gpioPin);
|
||||
if ((sysFds[gpioPin] = open(fName, O_RDWR)) < 0) {
|
||||
fprintf (stderr, "Error reading pin %d: %s\n", physPin, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Clear any initial pending interrupt
|
||||
ioctl(sysFds[gpioPin], FIONREAD, &count);
|
||||
for (i = 0; i < count; ++i) {
|
||||
if (read(sysFds[gpioPin], &c, 1) == -1) {
|
||||
mys_log(LOG_ERR, "attachInterrupt: failed to read pin status: %s\n", strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
struct ThreadArgs *threadArgs = new struct ThreadArgs;
|
||||
threadArgs->func = func;
|
||||
threadArgs->gpioPin = gpioPin;
|
||||
|
||||
// Create a thread passing the pin and function
|
||||
pthread_create(threadIds[gpioPin], NULL, interruptHandler, (void *)threadArgs);
|
||||
}
|
||||
|
||||
void rpi_util::detachInterrupt(uint8_t physPin) {
|
||||
uint8_t gpioPin = (physPin > 63)? 255 : physToGpio[physPin];
|
||||
if (gpioPin == 255) {
|
||||
mys_log(LOG_ERR, "detachInterrupt: invalid pin: %d\n", physPin);
|
||||
return;
|
||||
}
|
||||
|
||||
// Cancel the thread
|
||||
if (threadIds[gpioPin] != NULL) {
|
||||
pthread_cancel(*threadIds[gpioPin]);
|
||||
delete threadIds[gpioPin];
|
||||
threadIds[gpioPin] = NULL;
|
||||
}
|
||||
|
||||
// Close filehandle
|
||||
if (sysFds[gpioPin] != -1) {
|
||||
close(sysFds[gpioPin]);
|
||||
sysFds[gpioPin] = -1;
|
||||
}
|
||||
|
||||
FILE *fp = fopen("/sys/class/gpio/unexport", "w");
|
||||
if (fp == NULL) {
|
||||
mys_log(LOG_ERR, "Unable to unexport pin %d for interrupt\n", gpioPin);
|
||||
exit(1);
|
||||
}
|
||||
fprintf(fp, "%d", gpioPin);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
uint8_t rpi_util::digitalPinToInterrupt(uint8_t physPin) {
|
||||
// No need to convert the pin to gpio, we do it in attachInterrupt().
|
||||
return physPin;
|
||||
}
|
||||
|
||||
void rpi_util::interrupts() {
|
||||
pthread_mutex_unlock(&intMutex);
|
||||
}
|
||||
|
||||
void rpi_util::noInterrupts() {
|
||||
pthread_mutex_lock(&intMutex);
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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 <marceloaqno@gmail.org>
|
||||
* 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 wiringPi Copyright (c) 2012 Gordon Henderson.
|
||||
*/
|
||||
|
||||
#ifndef pins_io_h
|
||||
#define pins_io_h
|
||||
|
||||
#include <stdint.h>
|
||||
#include "bcm2835.h"
|
||||
|
||||
namespace rpi_util {
|
||||
|
||||
typedef enum {
|
||||
LSBFIRST = BCM2835_SPI_BIT_ORDER_LSBFIRST,
|
||||
MSBFIRST = BCM2835_SPI_BIT_ORDER_MSBFIRST
|
||||
} rpi_bitorder;
|
||||
|
||||
typedef enum {
|
||||
INPUT = BCM2835_GPIO_FSEL_INPT,
|
||||
OUTPUT = BCM2835_GPIO_FSEL_OUTP
|
||||
} rpi_pinmode;
|
||||
|
||||
typedef enum {
|
||||
CHANGE = 1,
|
||||
FALLING = 2,
|
||||
RISING = 3,
|
||||
NONE = 4
|
||||
} rpi_pinedge;
|
||||
|
||||
typedef enum {
|
||||
PIN_SPI_SS = RPI_GPIO_P1_24,
|
||||
PIN_SPI_MOSI = RPI_GPIO_P1_19,
|
||||
PIN_SPI_MISO = RPI_GPIO_P1_21,
|
||||
PIN_SPI_SCK = RPI_GPIO_P1_23
|
||||
} rpi_spipins;
|
||||
|
||||
const uint8_t SS = PIN_SPI_SS;
|
||||
const uint8_t MOSI = PIN_SPI_MOSI;
|
||||
const uint8_t MISO = PIN_SPI_MISO;
|
||||
const uint8_t SCK = PIN_SPI_SCK;
|
||||
|
||||
/* Some useful arduino functions */
|
||||
void pinMode(uint8_t physPin, uint8_t mode);
|
||||
void digitalWrite(uint8_t physPin, uint8_t value);
|
||||
uint8_t digitalRead(uint8_t physPin);
|
||||
void attachInterrupt(uint8_t physPin,void (*func)(), uint8_t mode);
|
||||
void detachInterrupt(uint8_t physPin);
|
||||
uint8_t digitalPinToInterrupt(uint8_t physPin);
|
||||
void interrupts();
|
||||
void noInterrupts();
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* 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 <marceloaqno@gmail.org>
|
||||
* 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 <iostream>
|
||||
#include <cstdio>
|
||||
#include <unistd.h>
|
||||
|
||||
// For more options run ./configure --help
|
||||
|
||||
// Config file
|
||||
//#define MY_LINUX_CONFIG_FILE "/etc/mysensors.dat"
|
||||
|
||||
// How many clients should be able to connect to this gateway (default 1)
|
||||
#define MY_GATEWAY_MAX_CLIENTS 10
|
||||
|
||||
// Serial config
|
||||
// Enable this if you are using an Arduino connected to the USB
|
||||
//#define MY_LINUX_SERIAL_PORT "/dev/ttyACM0"
|
||||
// Enable this if you need to connect to a controller running on the same device
|
||||
//#define MY_IS_SERIAL_PTY
|
||||
// Choose a symlink name for the PTY device
|
||||
//#define MY_LINUX_SERIAL_PTY "/dev/ttyMySensorsGateway"
|
||||
// Grant access to the specified system group for the serial device
|
||||
//#define MY_LINUX_SERIAL_GROUPNAME "tty"
|
||||
|
||||
// MQTT options
|
||||
//#define MY_CONTROLLER_IP_ADDRESS 192, 168, 178, 68
|
||||
//#define MY_PORT 1883
|
||||
//#define MY_MQTT_CLIENT_ID "mysensors-1"
|
||||
//#define MY_MQTT_PUBLISH_TOPIC_PREFIX "mygateway1-out"
|
||||
//#define MY_MQTT_SUBSCRIBE_TOPIC_PREFIX "mygateway1-in"
|
||||
|
||||
// Enable these if your MQTT broker requires usenrame/password
|
||||
//#define MY_MQTT_USER "username"
|
||||
//#define MY_MQTT_PASSWORD "password"
|
||||
|
||||
// Flash leds on rx/tx/err
|
||||
//#define MY_DEFAULT_ERR_LED_PIN 12 // Error LED pin
|
||||
//#define MY_DEFAULT_RX_LED_PIN 16 // Receive LED pin
|
||||
//#define MY_DEFAULT_TX_LED_PIN 18 // Transmit LED pin
|
||||
// Inverse the blinking feature
|
||||
//#define MY_WITH_LEDS_BLINKING_INVERSE
|
||||
|
||||
#include <MySensors.h>
|
||||
|
||||
void setup() {
|
||||
}
|
||||
|
||||
void presentation() {
|
||||
// Present locally attached sensors here
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Send locally attached sensors data here
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
[Unit]
|
||||
Description=MySensors Gateway daemon
|
||||
Requires=network.target
|
||||
|
||||
[Service]
|
||||
ExecStart=%gateway_dir%/mysGateway
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -0,0 +1,154 @@
|
||||
#! /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
|
||||
|
||||
# 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="-b"
|
||||
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
|
||||
|
||||
:
|
||||
Reference in New Issue
Block a user