Merge pull request #537 from marceloaqno/dev-raspberrypi

Add support for RaspberryPi
This commit is contained in:
Henrik Ekblad
2016-10-01 13:32:39 +02:00
committed by GitHub
50 changed files with 7915 additions and 197 deletions
+100
View File
@@ -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
View File
@@ -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
View File
@@ -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
Vendored Executable
+377
View File
@@ -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."
+64 -63
View File
@@ -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
+12 -91
View File
@@ -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;
}
-3
View File
@@ -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:
+7 -1
View File
@@ -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
View File
@@ -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()
+190
View File
@@ -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
+84
View File
@@ -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
+135
View File
@@ -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
View File
@@ -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) {
-1
View File
@@ -32,7 +32,6 @@
#ifdef __cplusplus
#include <Arduino.h>
#include <string.h>
#include <stdint.h>
#endif
+92 -6
View File
@@ -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;
}
+8 -4
View File
@@ -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"));
+2 -2
View File
@@ -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
+2
View File
@@ -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)
-5
View File
@@ -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"
+67
View File
@@ -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
+50
View File
@@ -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
+254
View File
@@ -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;
}
+191
View File
@@ -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
+215
View File
@@ -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);
}
+120
View File
@@ -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
+106
View File
@@ -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);
}
+155
View File
@@ -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
+242
View File
@@ -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;
}
+76
View File
@@ -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
+232
View File
@@ -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
}
}
+113
View File
@@ -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
+32
View File
@@ -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
+258
View File
@@ -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;
}
+120
View File
@@ -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
+55
View File
@@ -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;
}
+22
View File
@@ -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);
}
+18
View File
@@ -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
+147
View File
@@ -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;
}
+52
View File
@@ -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
View File
@@ -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
+106
View File
@@ -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;
}
+231
View File
@@ -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
+48
View File
@@ -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) ;
}
+330
View File
@@ -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);
}
+71
View File
@@ -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
+71
View File
@@ -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
}
+9
View File
@@ -0,0 +1,9 @@
[Unit]
Description=MySensors Gateway daemon
Requires=network.target
[Service]
ExecStart=%gateway_dir%/mysGateway
[Install]
WantedBy=multi-user.target
+154
View File
@@ -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
: