From 35cab8c26dc98a0415455e8891cd0af453255ff0 Mon Sep 17 00:00:00 2001 From: Marcelo Aquino Date: Sun, 21 Feb 2016 10:07:49 -0300 Subject: [PATCH 01/33] Add support for RaspberryPi --- libraries/MySensors/Makefile | 67 ++++ libraries/MySensors/MyConfig.h | 38 +++ libraries/MySensors/MySensor.h | 29 +- .../core/MyGatewayTransportEthernetLinux.cpp | 304 ++++++++++++++++++ libraries/MySensors/core/MyHwLinuxGeneric.cpp | 107 ++++++ libraries/MySensors/core/MyHwLinuxGeneric.h | 42 +++ libraries/MySensors/core/MyMainLinux.cpp | 13 + libraries/MySensors/core/MySensorCore.cpp | 5 + libraries/MySensors/core/MyTransportNRF24.cpp | 6 +- .../MySensors/examples_RPi/PiGateway.cpp | 55 ++++ 10 files changed, 661 insertions(+), 5 deletions(-) create mode 100644 libraries/MySensors/Makefile create mode 100644 libraries/MySensors/core/MyGatewayTransportEthernetLinux.cpp create mode 100644 libraries/MySensors/core/MyHwLinuxGeneric.cpp create mode 100644 libraries/MySensors/core/MyHwLinuxGeneric.h create mode 100644 libraries/MySensors/core/MyMainLinux.cpp create mode 100644 libraries/MySensors/examples_RPi/PiGateway.cpp diff --git a/libraries/MySensors/Makefile b/libraries/MySensors/Makefile new file mode 100644 index 0000000..20a46ff --- /dev/null +++ b/libraries/MySensors/Makefile @@ -0,0 +1,67 @@ +########################################################################## +# Configurable options # +########################################################################## +# Install Base location +PREFIX=/usr/local +# Bin Dir +BINDIR=$(PREFIX)/sbin + +########################################################################## +# Please do not change anything below this line # +########################################################################## +CXX ?= g++ + +ifeq "$(shell uname -m)" "armv6l" + ARCH=armv6zk +endif +ifeq "$(shell uname -m)" "armv7l" + ARCH=armv7-a +endif +ifdef ARCH + # The recommended compiler flags for the Raspberry Pi + CXXFLAGS+=-Ofast -mfpu=vfp -mfloat-abi=hard -march=$(ARCH) -mtune=arm1176jzf-s -DRASPBERRYPI_ARCH $(OPTFLAGS) + LDFLAGS+=-lrf24-bcm +endif + +CXXFLAGS+=-g -Wall -Wextra +CXXFLAGS+=-DLINUX_ARCH_GENERIC $(OPTFLAGS) + +# get PI Revision from cpuinfo +PIREV := $(shell cat /proc/cpuinfo | grep Revision | cut -f 2 -d ":" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$$//') +ifeq ($(PIREV),$(filter $(PIREV),a01041 a21041 0010)) + # a01041 and a21041 are PI 2 Model B with BPLUS Layout and 0010 is Pi Model B+ with BPLUS Layout + CXXFLAGS+=-D__PI_BPLUS +endif + +GATEWAY=examples_RPi/PiGateway +GATEWAY_SOURCES=examples_RPi/PiGateway.cpp $(wildcard ./utility/*.cpp) +GATEWAY_OBJECTS=$(patsubst %.cpp,%.o,$(GATEWAY_SOURCES)) +DEPS+=$(patsubst %.cpp,%.d,$(GATEWAY_SOURCES)) + +RF24H = /usr/local/include/RF24 +CINCLUDE=-I. -I./core -I$(RF24H) + +.PHONY: all clean install uninstall + +all: $(GATEWAY) + +# Basic Gateway Build +$(GATEWAY): LDFLAGS += -pthread +$(GATEWAY): $(GATEWAY_OBJECTS) + $(CXX) $(LDFLAGS) -o $@ $(GATEWAY_OBJECTS) + +# Include all .d files +-include $(DEPS) + +%.o: %.cpp + $(CXX) $(CXXFLAGS) $(CINCLUDE) -MMD -c -o $@ $< + +# The Cleaner +clean: + rm -rf build $(GATEWAY_OBJECTS) $(GATEWAY) $(DEPS) + +install: all install-gatewaybasic install-gatewayserial install-gatewayethernet install-initscripts + +install-gatewaybasic: + @echo "Installing $(GATEWAY) to $(BINDIR)" + @install -m 0755 $(GATEWAY) $(BINDIR) diff --git a/libraries/MySensors/MyConfig.h b/libraries/MySensors/MyConfig.h index ffe3978..d25a65b 100644 --- a/libraries/MySensors/MyConfig.h +++ b/libraries/MySensors/MyConfig.h @@ -342,6 +342,17 @@ #define MY_RF24_CE_PIN 4 #elif defined(ARDUINO_ARCH_SAMD) #define MY_RF24_CE_PIN 27 + #elif defined(RASPBERRYPI_ARCH) + #include + #include + #ifdef __PI_BPLUS + #define MY_RF24_CE_PIN RPI_BPLUS_GPIO_J8_22 + #define MY_RF24_CS_PIN RPI_BPLUS_GPIO_J8_24 + #else + #define MY_RF24_CE_PIN RPI_V2_GPIO_P1_22 + #define MY_RF24_CS_PIN BCM2835_SPI_CS0 + #endif + #define MY_RF24_SPI_SPEED BCM2835_SPI_SPEED_8MHZ #else #define MY_RF24_CE_PIN 9 #endif @@ -509,6 +520,8 @@ //#define MY_GATEWAY_W5100 //#define MY_GATEWAY_ENC28J60 //#define MY_GATEWAY_ESP8266 +//#define MY_GATEWAY_RASPBERRYPI +//#define MY_GATEWAY_LINUX /** * @def MY_PORT @@ -546,6 +559,10 @@ // If MY_CONTROLLER_IP_ADDRESS is left un-defined, gateway acts as server allowing incoming connections. //#define MY_CONTROLLER_IP_ADDRESS 192, 168, 178, 254 +/************************************** +* Node Locking +***************************************/ + /** * @defgroup MyLockgrp MyNodeLock * @ingroup internals @@ -604,6 +621,27 @@ #endif +/************************************** +* RaspberryPi Settings +***************************************/ + +/** + * @def MY_RASPBERRYPI_TTY_NAME + * @brief Set the name of predictable tty + */ +#ifndef MY_RASPBERRYPI_TTY_NAME +#define MY_RASPBERRYPI_TTY_NAME /dev/ttyMySensorsGateway +#endif + +/** + * @def MY_RASPBERRYPI_TTY_GROUPNAME + * @brief Set the group name for the raw tty + */ +#ifndef MY_RASPBERRYPI_TTY_GROUPNAME +#define MY_RASPBERRYPI_TTY_GROUPNAME tty +#endif + + // Doxygen specific constructs, not included when built normally // This is used to enable disabled macros/definitions to be included in the documentation as well. #if DOXYGEN diff --git a/libraries/MySensors/MySensor.h b/libraries/MySensors/MySensor.h index c562a2a..57bfd27 100644 --- a/libraries/MySensors/MySensor.h +++ b/libraries/MySensors/MySensor.h @@ -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_RASPBERRYPI) || defined(MY_GATEWAY_LINUX) || defined(MY_GATEWAY_MQTT_CLIENT) #define MY_GATEWAY_FEATURE #define MY_IS_GATEWAY (true) #define MY_NODE_TYPE "gateway" @@ -68,7 +68,19 @@ #elif defined(ARDUINO_ARCH_AVR) #include "core/MyHwATMega328.cpp" #elif defined(ARDUINO_ARCH_SAMD) - #include "core/MyHwSAMD.cpp" + #include "core/MyHwSAMD.cpp" +#elif defined(LINUX_ARCH_GENERIC) + // Remove PSTR macros from debug prints + #undef PSTR + #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) + #include "core/MyHwLinuxGeneric.cpp" #endif // LEDS @@ -208,6 +220,9 @@ // We assume that a gateway having a radio also should act as repeater #define MY_REPEATER_FEATURE #endif + #if defined(MY_GATEWAY_RASPBERRYPI) + #define MY_GATEWAY_LINUX + #endif #if defined(MY_CONTROLLER_IP_ADDRESS) #define MY_GATEWAY_CLIENT_MODE #endif @@ -217,6 +232,9 @@ #if defined(MY_GATEWAY_ESP8266) // GATEWAY - ESP8266 #include "core/MyGatewayTransportEthernet.cpp" + #elif defined(MY_GATEWAY_LINUX) + // GATEWAY - Generic Linux (RaspberryPi, BBB) + #include "core/MyGatewayTransportEthernetLinux.cpp" #elif defined(MY_GATEWAY_W5100) // GATEWAY - W5100 #include "core/MyGatewayTransportEthernet.cpp" @@ -229,6 +247,7 @@ #elif defined(MY_GATEWAY_SERIAL) // GATEWAY - SERIAL #include "core/MyGatewayTransportSerial.cpp" + #endif #endif @@ -256,7 +275,9 @@ #if defined(MY_RF24_ENABLE_ENCRYPTION) #include "drivers/AES/AES.cpp" #endif - #include "drivers/RF24/RF24.cpp" + #if !defined(RASPBERRYPI_ARCH) + #include "drivers/RF24/RF24.cpp" + #endif #include "core/MyTransportNRF24.cpp" #elif defined(MY_RS485) #include "drivers/AltSoftSerial/AltSoftSerial.cpp" @@ -295,6 +316,8 @@ #if !defined(MY_CORE_ONLY) #if defined(ARDUINO_ARCH_ESP8266) #include "core/MyMainESP8266.cpp" + #elif defined(LINUX_ARCH_GENERIC) + #include "core/MyMainLinux.cpp" #else #include "core/MyMainDefault.cpp" #endif diff --git a/libraries/MySensors/core/MyGatewayTransportEthernetLinux.cpp b/libraries/MySensors/core/MyGatewayTransportEthernetLinux.cpp new file mode 100644 index 0000000..e522828 --- /dev/null +++ b/libraries/MySensors/core/MyGatewayTransportEthernetLinux.cpp @@ -0,0 +1,304 @@ +/** + * The MySensors Arduino library handles the wireless radio link and protocol + * between your home built sensors/actuators and HA controller of choice. + * The sensors forms a self healing radio network with optional repeaters. Each + * repeater and gateway builds a routing tables in EEPROM which keeps track of the + * network topology allowing messages to be routed to nodes. + * + * Created by Marcelo Aquino + * Copyleft (c) 2016, Marcelo Aquino + * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors + * + * Documentation: http://www.mysensors.org + * Support Forum: http://forum.mysensors.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + */ + +#include "MyGatewayTransport.h" +#include +#include +#include +#include +#include +#include +#include +#include + +//TODO +#ifdef MY_USE_UDP + #error UDP not supported for this type of gateway +#endif + +union _addrIPv4{ + uint8_t bytes[4]; + uint32_t dword; +}; + +static int controllers[MY_GATEWAY_MAX_CLIENTS]; +static MyMessage _ethernetMsg; +static std::list ethernetMsg_q; +static pthread_mutex_t ethernetMsg_mutex = PTHREAD_MUTEX_INITIALIZER; + +// Prototypes +int open_listen(char *ip, uint16_t port); +uint32_t getAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet); +void *get_in_addr(struct sockaddr *sa); +void *waiting_controllers(void* thread_arg); +void *connected_controller(void* thread_arg); + +bool gatewayTransportInit() { + long int sockfd; + pthread_t thread_id; + pthread_attr_t attr; + char *ip = NULL; + + // ignore SIGPIPE generated by send() to a disconnected client + signal(SIGPIPE, SIG_IGN); + + for (uint8_t i = 0; i < MY_GATEWAY_MAX_CLIENTS; i++) { + controllers[i] = -1; + } + +#ifdef MY_CONTROLLER_IP_ADDRESS + //TODO + return false; +#else + #ifdef MY_IP_ADDRESS + char ipstr[INET_ADDRSTRLEN]; + union _addrIPv4 addrIPv4 = getAddress(MY_IP_ADDRESS); + sprintf(ipstr, "%d.%d.%d.%d", addrIPv4.bytes[0], addrIPv4.bytes[1], + addrIPv4.bytes[2], addrIPv4.bytes[3]); + ip = ipstr; + #endif + + sockfd = open_listen(ip, MY_PORT); + if (sockfd < 0) { + return false; + } +#endif + + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_create(&thread_id, &attr, &waiting_controllers, (void *)sockfd); + pthread_attr_destroy(&attr); + + return true; +} + +bool gatewayTransportSend(MyMessage &message) +{ + char *ethernetMsg = protocolFormat(message); + + for (uint8_t i = 0; i < MY_GATEWAY_MAX_CLIENTS; i++) { + if (controllers[i] == -1) + continue; + if (send(controllers[i], ethernetMsg, strlen(ethernetMsg), 0) == -1) + perror("send"); + } + return true; +} + +bool gatewayTransportAvailable() +{ + bool empty; + + pthread_mutex_lock(ðernetMsg_mutex); + empty = ethernetMsg_q.empty(); + pthread_mutex_unlock(ðernetMsg_mutex); + + return !empty; +} + +MyMessage& gatewayTransportReceive() +{ + // Return the last parsed message + pthread_mutex_lock(ðernetMsg_mutex); + _ethernetMsg = ethernetMsg_q.front(); + ethernetMsg_q.pop_front(); + pthread_mutex_unlock(ðernetMsg_mutex); + + return _ethernetMsg; +} + +int open_listen(char *address, uint16_t port) +{ + int sockfd; + 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_INET; // IPv4 + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; + + sprintf(portstr, "%d", port); + if ((rv = getaddrinfo(address, portstr, &hints, &servinfo)) != 0) { + fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv)); + return -1; + } + + // loop through all the results and bind to the first we can + for (p = servinfo; p != NULL; p = p->ai_next) { + if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) { + perror("socket"); + continue; + } + + if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) { + perror("setsockopt"); + freeaddrinfo(servinfo); + return -1; + } + + if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) { + close(sockfd); + perror("bind"); + continue; + } + + break; + } + + if (p == NULL) { + fprintf(stderr, "failed to bind\n"); + freeaddrinfo(servinfo); + return -1; + } + + if (listen(sockfd, MY_GATEWAY_MAX_CLIENTS) == -1) { + perror("listen"); + freeaddrinfo(servinfo); + return -1; + } + + struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr; + void *addr = &(ipv4->sin_addr); + inet_ntop(p->ai_family, addr, ipstr, sizeof ipstr); + debug("Eth: Listening for connections on %s:%s\n", ipstr, portstr); + + freeaddrinfo(servinfo); + return sockfd; +} + +uint32_t getAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet) +{ + union _addrIPv4 addrIPv4; + + addrIPv4.bytes[0] = first_octet; + addrIPv4.bytes[1] = second_octet; + addrIPv4.bytes[2] = third_octet; + addrIPv4.bytes[3] = fourth_octet; + + return addrIPv4.dword; +} + +void *get_in_addr(struct sockaddr *sa) +{ + if (sa->sa_family == AF_INET) { + return &(((struct sockaddr_in*)sa)->sin_addr); + } + + return &(((struct sockaddr_in6*)sa)->sin6_addr); +} + +void *waiting_controllers(void* thread_arg) +{ + long int sockfd = (long int) thread_arg; + long int new_fd; // new connection on new_fd + struct sockaddr_storage client_addr; // connector's address information + socklen_t sin_size; + char s[INET6_ADDRSTRLEN]; + pthread_t thread_id; + pthread_attr_t attr; + uint8_t i; + + while (1) { // accept() loop + sin_size = sizeof client_addr; + new_fd = accept(sockfd, (struct sockaddr *)&client_addr, &sin_size); + if (new_fd == -1) { + perror("accept"); + continue; + } + + inet_ntop(client_addr.ss_family, + get_in_addr((struct sockaddr *)&client_addr), s, sizeof s); + debug("Eth: New connection from %s\n", s); + + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_create(&thread_id, &attr, &connected_controller, (void *)new_fd); + pthread_attr_destroy(&attr); + + for (i = 0; i < MY_GATEWAY_MAX_CLIENTS; i++) { + if (controllers[i] == -1) { + controllers[i] = new_fd; + break; + } + } + if (i >= MY_GATEWAY_MAX_CLIENTS) { + // no free/disconnected spot so reject + close(new_fd); + } + } + + return NULL; +} + +void *connected_controller(void* thread_arg) +{ + long int sockfd = (long int) thread_arg; + char inputString[MY_GATEWAY_MAX_RECEIVE_LENGTH]; + char *ptr = inputString; + int nbytes = 0; + MyMessage ethernetMsg; + + while (1) { + if (recv(sockfd, ptr, 1, 0) > 0) { + if (*ptr == '\n' || *ptr == '\r') { + // String terminator + *ptr = 0; + debug("Eth: %s\n", inputString); + if (protocolParse(ethernetMsg, inputString)) { + pthread_mutex_lock(ðernetMsg_mutex); + ethernetMsg_q.push_back(ethernetMsg); + pthread_mutex_unlock(ðernetMsg_mutex); + } + + // Prepare for the next message + ptr = inputString; + nbytes = 0; + continue; + } + nbytes++; + if (nbytes == MY_GATEWAY_MAX_RECEIVE_LENGTH) { + // Incoming message too long. Throw away + debug("Eth: Message too long\n"); + ptr = inputString; + nbytes = 0; + } else { + ptr++; + } + } else { + break; + } + } + + if (nbytes == -1) + perror("recv"); + + for (uint8_t i = 0; i < MY_GATEWAY_MAX_CLIENTS; i++) { + if (controllers[i] == sockfd) { + controllers[i] = -1; + break; + } + } + + close(sockfd); + return NULL; +} diff --git a/libraries/MySensors/core/MyHwLinuxGeneric.cpp b/libraries/MySensors/core/MyHwLinuxGeneric.cpp new file mode 100644 index 0000000..35afc37 --- /dev/null +++ b/libraries/MySensors/core/MyHwLinuxGeneric.cpp @@ -0,0 +1,107 @@ +/** + * The MySensors Arduino library handles the wireless radio link and protocol + * between your home built sensors/actuators and HA controller of choice. + * The sensors forms a self healing radio network with optional repeaters. Each + * repeater and gateway builds a routing tables in EEPROM which keeps track of the + * network topology allowing messages to be routed to nodes. + * + * Created by Marcelo Aquino + * Copyleft (c) 2016, Marcelo Aquino + * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors + * + * Documentation: http://www.mysensors.org + * Support Forum: http://forum.mysensors.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + */ + +#include "MyHwLinuxGeneric.h" + +#include +#include +#include + +static const size_t _length = 1024; // ATMega328 has 1024 bytes +//TODO store _config to a file +static uint8_t _config[_length]; +static unsigned long millis_at_start; + +void hwInit() +{ + timeval curTime; + + for (size_t i = 0; i < _length; i++) + _config[i] = 0xFF; + + gettimeofday(&curTime, NULL); + millis_at_start = curTime.tv_sec; +} + +void hwReadConfigBlock(void* buf, void* addr, size_t length) +{ + unsigned long int offs = reinterpret_cast(addr); + + if (length && offs + length <= _length) { + memcpy(buf, _config+offs, length); + } +} + +void hwWriteConfigBlock(void* buf, void* addr, size_t length) +{ + unsigned long int offs = reinterpret_cast(addr); + + if (length && offs + length <= _length) { + memcpy(_config+offs, buf, length); + } +} + +uint8_t hwReadConfig(int addr) +{ + if (addr >= 0 && (unsigned)addr < _length) + return _config[addr]; + return 0xFF; +} + +void hwWriteConfig(int addr, uint8_t value) +{ + if (addr >= 0 && (unsigned)addr < _length) + _config[addr] = value; +} + +unsigned long hwMillis() +{ + timeval curTime; + + gettimeofday(&curTime, NULL); + return ((curTime.tv_sec - millis_at_start) * 1000) + (curTime.tv_usec / 1000); +} + +int8_t hwSleep(unsigned long ms) +{ + // TODO: Not supported! + return -2; +} + +int8_t hwSleep(uint8_t interrupt, uint8_t mode, unsigned long ms) +{ + // TODO: Not supported! + return -2; +} + +int8_t hwSleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2, unsigned long ms) +{ + // TODO: Not supported! + return -2; +} + +#ifdef MY_DEBUG +void hwDebugPrint(const char *fmt, ... ) +{ + va_list arglist; + va_start(arglist, fmt); + vprintf(fmt, arglist); + va_end(arglist); +} +#endif diff --git a/libraries/MySensors/core/MyHwLinuxGeneric.h b/libraries/MySensors/core/MyHwLinuxGeneric.h new file mode 100644 index 0000000..3546e09 --- /dev/null +++ b/libraries/MySensors/core/MyHwLinuxGeneric.h @@ -0,0 +1,42 @@ +/** + * The MySensors Arduino library handles the wireless radio link and protocol + * between your home built sensors/actuators and HA controller of choice. + * The sensors forms a self healing radio network with optional repeaters. Each + * repeater and gateway builds a routing tables in EEPROM which keeps track of the + * network topology allowing messages to be routed to nodes. + * + * Created by Marcelo Aquino + * Copyleft (c) 2016, Marcelo Aquino + * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors + * + * Documentation: http://www.mysensors.org + * Support Forum: http://forum.mysensors.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + */ + +#ifndef MyHwLinuxGeneric_h +#define MyHwLinuxGeneric_h + +#include "MyHw.h" +#include + +#define MY_SERIALDEVICE Serial +#define min(a,b) ((a)<(b)?(a):(b)) +#define max(a,b) ((a)>(b)?(a):(b)) + +// Define these as macros (do nothing) +#define hwDigitalWrite(__pin, __value) +#define hwWatchdogReset() +#define hwReboot() + +void hwInit(); +void hwReadConfigBlock(void* buf, void* adr, size_t length); +void hwWriteConfigBlock(void* buf, void* adr, size_t length); +void hwWriteConfig(int adr, uint8_t value); +uint8_t hwReadConfig(int adr); +unsigned long hwMillis(); + +#endif diff --git a/libraries/MySensors/core/MyMainLinux.cpp b/libraries/MySensors/core/MyMainLinux.cpp new file mode 100644 index 0000000..98982e5 --- /dev/null +++ b/libraries/MySensors/core/MyMainLinux.cpp @@ -0,0 +1,13 @@ +// Initialize library and handle sketch functions like we want to + +#include "MySensorCore.h" + +int main(void) { + _begin(); // Startup MySensors library + + for(;;) { + _process(); // Process incoming data + if (loop) loop(); // Call sketch loop + } + return 0; +} diff --git a/libraries/MySensors/core/MySensorCore.cpp b/libraries/MySensors/core/MySensorCore.cpp index f74cd13..23ad2f0 100644 --- a/libraries/MySensors/core/MySensorCore.cpp +++ b/libraries/MySensors/core/MySensorCore.cpp @@ -51,6 +51,11 @@ void _process() { #if defined(MY_RADIO_FEATURE) transportProcess(); #endif + + #if defined(LINUX_ARCH_GENERIC) + // To avoid high cpu usage + usleep(10000); // 10ms + #endif } #if defined(MY_RADIO_FEATURE) diff --git a/libraries/MySensors/core/MyTransportNRF24.cpp b/libraries/MySensors/core/MyTransportNRF24.cpp index 3d2ac1d..67a7ca9 100644 --- a/libraries/MySensors/core/MyTransportNRF24.cpp +++ b/libraries/MySensors/core/MyTransportNRF24.cpp @@ -20,8 +20,10 @@ #include "MyConfig.h" #include "MyTransport.h" #include -#include "drivers/RF24/RF24.h" -#include "drivers/RF24/RF24_config.h" +#ifndef RASPBERRYPI_ARCH + #include "drivers/RF24/RF24.h" + #include "drivers/RF24/RF24_config.h" +#endif #if defined(MY_RF24_ENABLE_ENCRYPTION) #include "drivers/AES/AES.h" #endif diff --git a/libraries/MySensors/examples_RPi/PiGateway.cpp b/libraries/MySensors/examples_RPi/PiGateway.cpp new file mode 100644 index 0000000..16703f8 --- /dev/null +++ b/libraries/MySensors/examples_RPi/PiGateway.cpp @@ -0,0 +1,55 @@ +/** + * The MySensors Arduino library handles the wireless radio link and protocol + * between your home built sensors/actuators and HA controller of choice. + * The sensors forms a self healing radio network with optional repeaters. Each + * repeater and gateway builds a routing tables in EEPROM which keeps track of the + * network topology allowing messages to be routed to nodes. + * + * Created by Marcelo Aquino + * Copyleft (c) 2016, Marcelo Aquino + * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors + * + * Documentation: http://www.mysensors.org + * Support Forum: http://forum.mysensors.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + */ + +#include +#include +#include + +// Enable debug prints to serial monitor +#define MY_DEBUG + +// Enables and select radio type (if attached) +#define MY_RADIO_NRF24 + +#define MY_GATEWAY_RASPBERRYPI + +// Enable MY_IP_ADDRESS here if you want to listen for incoming network +// connections on the specified IP address/hostname only +//#define MY_IP_ADDRESS 192,168,178,87 + +// The port to keep open on node server mode +#define MY_PORT 5003 + +// How many clients should be able to connect to this gateway (default 1) +#define MY_GATEWAY_MAX_CLIENTS 2 + +#include + +using namespace std; + +void setup() { +} + +void presentation() { + // Present locally attached sensors here +} + +void loop() { + // Send locally attached sensors data here +} From 4b0664be5cf8dad6be91b79008173674440c707a Mon Sep 17 00:00:00 2001 From: aaron832 Date: Mon, 22 Feb 2016 22:01:16 -0800 Subject: [PATCH 02/33] Add MQTT topic generation Add MQTT topic generation to the protocol definition file. --- .../MySensors/core/MyGatewayTransportMQTTClient.cpp | 6 +++--- libraries/MySensors/core/MyProtocolMySensors.cpp | 10 ++++++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/libraries/MySensors/core/MyGatewayTransportMQTTClient.cpp b/libraries/MySensors/core/MyGatewayTransportMQTTClient.cpp index a2c1d6c..4528c8e 100644 --- a/libraries/MySensors/core/MyGatewayTransportMQTTClient.cpp +++ b/libraries/MySensors/core/MyGatewayTransportMQTTClient.cpp @@ -59,9 +59,9 @@ MyMessage _mqttMsg; bool gatewayTransportSend(MyMessage &message) { if (!_client.connected()) return false; - 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 _client.publish(_fmtBuffer, message.getString(_convBuffer)); + char *mqttMsg = protocolFormatMQTT(message); + debug(PSTR("Sending message on topic: %s\n"), mqttMsg); + return _client.publish(mqttMsg, message.getString(_convBuffer)); } diff --git a/libraries/MySensors/core/MyProtocolMySensors.cpp b/libraries/MySensors/core/MyProtocolMySensors.cpp index f9ffe12..109f749 100644 --- a/libraries/MySensors/core/MyProtocolMySensors.cpp +++ b/libraries/MySensors/core/MyProtocolMySensors.cpp @@ -100,6 +100,16 @@ 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; +} + uint8_t protocolH2i(char c) { uint8_t i = 0; if (c <= '9') From b50a651e5ef55e7edbafffc986571b9ad8baf5a9 Mon Sep 17 00:00:00 2001 From: aaron832 Date: Mon, 22 Feb 2016 22:04:38 -0800 Subject: [PATCH 03/33] Reform definition logic Reform definition logic to allow MY_GATEWAY_MQTT_CLIENT along side MY_GATEWAY_FEATURE as there was some redundancy. Add some specific exclusions for Linux implementation of MQTT. --- libraries/MySensors/MySensor.h | 65 ++++++++++++++++++---------------- 1 file changed, 35 insertions(+), 30 deletions(-) diff --git a/libraries/MySensors/MySensor.h b/libraries/MySensors/MySensor.h index 57bfd27..e073a46 100644 --- a/libraries/MySensors/MySensor.h +++ b/libraries/MySensors/MySensor.h @@ -181,34 +181,7 @@ #endif #endif - - -// GATEWAY - TRANSPORT -#if defined(MY_GATEWAY_MQTT_CLIENT) - #if defined(MY_RADIO_FEATURE) - // We assume that a gateway having a radio also should act as repeater - #define MY_REPEATER_FEATURE - #endif - // GATEWAY - COMMON FUNCTIONS - // We only support MQTT Client using W5100 and ESP8266 at the moment - #if !(defined(MY_CONTROLLER_URL_ADDRESS) || defined(MY_CONTROLLER_IP_ADDRESS)) - #error You must specify MY_CONTROLLER_IP_ADDRESS or MY_CONTROLLER_URL_ADDRESS - #endif - - #if !defined(MY_MQTT_PUBLISH_TOPIC_PREFIX) - #error You must specify a topic publish prefix MY_MQTT_PUBLISH_TOPIC_PREFIX for this MQTT client - #endif - #if !defined(MY_MQTT_SUBSCRIBE_TOPIC_PREFIX) - #error You must specify a topic subscribe prefix MY_MQTT_SUBSCRIBE_TOPIC_PREFIX for this MQTT client - #endif - #if !defined(MY_MQTT_CLIENT_ID) - #error You must define a unique MY_MQTT_CLIENT_ID for this MQTT client - #endif - - #include "drivers/pubsubclient/src/PubSubClient.cpp" - #include "core/MyGatewayTransport.cpp" - #include "core/MyGatewayTransportMQTTClient.cpp" -#elif defined(MY_GATEWAY_FEATURE) +#if defined(MY_GATEWAY_FEATURE) // GATEWAY - COMMON FUNCTIONS #include "core/MyGatewayTransport.cpp" @@ -231,13 +204,23 @@ #endif #if defined(MY_GATEWAY_ESP8266) // GATEWAY - ESP8266 - #include "core/MyGatewayTransportEthernet.cpp" + #if defined(MY_GATEWAY_MQTT_CLIENT) + #include "drivers/pubsubclient/src/PubSubClient.cpp" + #include "core/MyGatewayTransportMQTTClient.cpp" + #else + #include "core/MyGatewayTransportEthernet.cpp" + #endif #elif defined(MY_GATEWAY_LINUX) // GATEWAY - Generic Linux (RaspberryPi, BBB) #include "core/MyGatewayTransportEthernetLinux.cpp" #elif defined(MY_GATEWAY_W5100) // GATEWAY - W5100 - #include "core/MyGatewayTransportEthernet.cpp" + #if defined(MY_GATEWAY_MQTT_CLIENT) + #include "drivers/pubsubclient/src/PubSubClient.cpp" + #include "core/MyGatewayTransportMQTTClient.cpp" + #else + #include "core/MyGatewayTransportEthernet.cpp" + #endif #elif defined(MY_GATEWAY_ENC28J60) // GATEWAY - ENC28J60 #if defined(MY_USE_UDP) @@ -251,6 +234,28 @@ #endif #endif +// GATEWAY - MQTT +#if defined(MY_GATEWAY_MQTT_CLIENT) && !defined(MY_GATEWAY_LINUX) + // GATEWAY - COMMON FUNCTIONS + // We only support MQTT Client using W5100, ESP8266 or Linux/RasperryPi at the moment + #if !(defined(MY_GATEWAY_ESP8266) || defined(MY_GATEWAY_W5100) || defined(MY_GATEWAY_LINUX)) + #error We only support MQTT Client using W5100, ESP8266 or Linux/RasperryPi at the moment + #endif + + #if !(defined(MY_CONTROLLER_URL_ADDRESS) || defined(MY_CONTROLLER_IP_ADDRESS)) + #error You must specify MY_CONTROLLER_IP_ADDRESS or MY_CONTROLLER_URL_ADDRESS + #endif + + #if !defined(MY_MQTT_PUBLISH_TOPIC_PREFIX) + #error You must specify a topic publish prefix MY_MQTT_PUBLISH_TOPIC_PREFIX for this MQTT client + #endif + #if !defined(MY_MQTT_SUBSCRIBE_TOPIC_PREFIX) + #error You must specify a topic subscribe prefix MY_MQTT_SUBSCRIBE_TOPIC_PREFIX for this MQTT client + #endif + #if !defined(MY_MQTT_CLIENT_ID) + #error You must define a unique MY_MQTT_CLIENT_ID for this MQTT client + #endif +#endif // RADIO #if defined(MY_RADIO_NRF24) || defined(MY_RADIO_RFM69) || defined(MY_RS485) From b880a745fe4f3d922a82fabdb94e14964078e6fe Mon Sep 17 00:00:00 2001 From: aaron832 Date: Mon, 22 Feb 2016 22:06:42 -0800 Subject: [PATCH 04/33] Initial implementation of MQTT Initial implementation of MQTT alongside ethernet gateway. --- libraries/MySensors/Makefile | 21 +- .../core/MyGatewayTransportEthernetLinux.cpp | 218 ++++++++++++++++++ .../MySensors/examples_RPi/PiGateway.cpp | 13 ++ 3 files changed, 249 insertions(+), 3 deletions(-) diff --git a/libraries/MySensors/Makefile b/libraries/MySensors/Makefile index 20a46ff..03e671d 100644 --- a/libraries/MySensors/Makefile +++ b/libraries/MySensors/Makefile @@ -33,7 +33,9 @@ ifeq ($(PIREV),$(filter $(PIREV),a01041 a21041 0010)) CXXFLAGS+=-D__PI_BPLUS endif +COMPILER_FLAGS=examples_RPi/compiler_flags GATEWAY=examples_RPi/PiGateway +GATEWAYMQTT=examples_RPi/PiGatewayMQTT GATEWAY_SOURCES=examples_RPi/PiGateway.cpp $(wildcard ./utility/*.cpp) GATEWAY_OBJECTS=$(patsubst %.cpp,%.o,$(GATEWAY_SOURCES)) DEPS+=$(patsubst %.cpp,%.d,$(GATEWAY_SOURCES)) @@ -41,24 +43,37 @@ DEPS+=$(patsubst %.cpp,%.d,$(GATEWAY_SOURCES)) RF24H = /usr/local/include/RF24 CINCLUDE=-I. -I./core -I$(RF24H) -.PHONY: all clean install uninstall +.PHONY: all gateway gatewaymqtt clean install uninstall force all: $(GATEWAY) +mqtt: $(GATEWAYMQTT) + +$(COMPILER_FLAGS): force + echo '$(CXXFLAGS)' | cmp -s - $@ || echo '$(CXXFLAGS)' > $@ + +$(GATEWAY_OBJECTS): $(COMPILER_FLAGS) + # Basic Gateway Build $(GATEWAY): LDFLAGS += -pthread $(GATEWAY): $(GATEWAY_OBJECTS) $(CXX) $(LDFLAGS) -o $@ $(GATEWAY_OBJECTS) + +# MQTT Gateway Build +$(GATEWAYMQTT): CXXFLAGS += -DMY_GATEWAY_MQTT_CLIENT +$(GATEWAYMQTT): LDFLAGS += -pthread -lmosquitto +$(GATEWAYMQTT): $(GATEWAY_OBJECTS) + $(CXX) $(LDFLAGS) -o $@ $(GATEWAY_OBJECTS) # Include all .d files -include $(DEPS) - + %.o: %.cpp $(CXX) $(CXXFLAGS) $(CINCLUDE) -MMD -c -o $@ $< # The Cleaner clean: - rm -rf build $(GATEWAY_OBJECTS) $(GATEWAY) $(DEPS) + rm -rf build $(GATEWAY_OBJECTS) $(GATEWAY) $(GATEWAYMQTT) $(DEPS) $(COMPILER_FLAGS) install: all install-gatewaybasic install-gatewayserial install-gatewayethernet install-initscripts diff --git a/libraries/MySensors/core/MyGatewayTransportEthernetLinux.cpp b/libraries/MySensors/core/MyGatewayTransportEthernetLinux.cpp index e522828..4eadf49 100644 --- a/libraries/MySensors/core/MyGatewayTransportEthernetLinux.cpp +++ b/libraries/MySensors/core/MyGatewayTransportEthernetLinux.cpp @@ -27,6 +27,26 @@ #include #include +#ifdef MY_GATEWAY_MQTT_CLIENT + #include + + #ifndef MQTT_IP + #define MQTT_IP "127.0.0.1" + #endif + #ifndef MQTT_PORT + #define MQTT_PORT 1883 + #endif + #ifndef MQTT_KEEPALIVE + #define MQTT_KEEPALIVE 60 + #endif + #ifndef MY_MQTT_PUBLISH_TOPIC_PREFIX + #define MY_MQTT_PUBLISH_TOPIC_PREFIX "mygateway1-out" + #endif + #ifndef MY_MQTT_SUBSCRIBE_TOPIC_PREFIX + #define MY_MQTT_SUBSCRIBE_TOPIC_PREFIX "mygateway1-in" + #endif +#endif + //TODO #ifdef MY_USE_UDP #error UDP not supported for this type of gateway @@ -49,10 +69,19 @@ void *get_in_addr(struct sockaddr *sa); void *waiting_controllers(void* thread_arg); void *connected_controller(void* thread_arg); +#ifdef MY_GATEWAY_MQTT_CLIENT +void *mqtt_thread(void *); +void parsemqtt_and_send(char *topic, const char *payload); +struct mosquitto *mosq; +#endif + bool gatewayTransportInit() { long int sockfd; pthread_t thread_id; pthread_attr_t attr; +#ifdef MY_GATEWAY_MQTT_CLIENT + pthread_attr_t mqtt_attr; +#endif char *ip = NULL; // ignore SIGPIPE generated by send() to a disconnected client @@ -84,6 +113,14 @@ bool gatewayTransportInit() { pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); pthread_create(&thread_id, &attr, &waiting_controllers, (void *)sockfd); pthread_attr_destroy(&attr); + +#ifdef MY_GATEWAY_MQTT_CLIENT + /* mqtt thread */ + pthread_attr_init(&mqtt_attr); + pthread_attr_setdetachstate(&mqtt_attr, PTHREAD_CREATE_DETACHED); + pthread_create(&thread_id, &mqtt_attr, &mqtt_thread, NULL); + pthread_attr_destroy(&mqtt_attr); +#endif return true; } @@ -98,6 +135,13 @@ bool gatewayTransportSend(MyMessage &message) if (send(controllers[i], ethernetMsg, strlen(ethernetMsg), 0) == -1) perror("send"); } + +#ifdef MY_GATEWAY_MQTT_CLIENT + char *mqttMsg = protocolFormatMQTTTopic(MY_MQTT_PUBLISH_TOPIC_PREFIX, message); + //connection, message id, topic, bytes, data, QOS, retain + mosquitto_publish(mosq, NULL, mqttMsg, strlen(message.getString(_convBuffer)), message.getString(_convBuffer), 0, false); +#endif + return true; } @@ -302,3 +346,177 @@ void *connected_controller(void* thread_arg) close(sockfd); return NULL; } + +#ifdef MY_GATEWAY_MQTT_CLIENT +void mqtt_message_callback(struct mosquitto *mosq, void *userdata, const struct mosquitto_message *message) +{ + (void)mosq; + (void)userdata; + + if(message->payloadlen){ + printf("Got a MQTT message %s %s\n", message->topic, (char*)message->payload); + + std::string strpayload((char *)message->payload); + strpayload = strpayload.substr(0,(int)message->payloadlen); + + parsemqtt_and_send(message->topic, strpayload.c_str()); + }else{ + printf("%s (null)\n", message->topic); + } + fflush(stdout); +} + +void mqtt_connect_callback(struct mosquitto *mosq, void *userdata, int result) +{ + (void)mosq; + (void)userdata; + + if(!result){ + printf("Connected!\n"); + /* Subscribe to broker information topics on successful connect. */ + //connection, message id, topic, qos + mosquitto_subscribe(mosq, NULL, protocolFormatMQTTSubscribe(MY_MQTT_SUBSCRIBE_TOPIC_PREFIX), 0); + }else{ + fprintf(stderr, "Connect failed\n"); + } +} + +void mqtt_subscribe_callback(struct mosquitto *mosq, void *userdata, int mid, int qos_count, const int *granted_qos) +{ + (void)mosq; + (void)userdata; + + int i; + + printf("Subscribed (mid: %d): %d", mid, granted_qos[0]); + for(i=1; i using namespace std; From e9a50586ff6aeaecb5d01a0f54804f41725363e9 Mon Sep 17 00:00:00 2001 From: aaron832 Date: Tue, 23 Feb 2016 11:39:08 -0800 Subject: [PATCH 05/33] Move MQTT to message parsing Move MQTT to message parsing into MyProtocolMySensor.cpp to be common amongst all MQTT client implementations. --- .../core/MyGatewayTransportEthernetLinux.cpp | 122 ++++-------------- .../core/MyGatewayTransportMQTTClient.cpp | 65 +--------- .../MySensors/core/MyProtocolMySensors.cpp | 67 ++++++++++ 3 files changed, 96 insertions(+), 158 deletions(-) diff --git a/libraries/MySensors/core/MyGatewayTransportEthernetLinux.cpp b/libraries/MySensors/core/MyGatewayTransportEthernetLinux.cpp index 4eadf49..c5d1091 100644 --- a/libraries/MySensors/core/MyGatewayTransportEthernetLinux.cpp +++ b/libraries/MySensors/core/MyGatewayTransportEthernetLinux.cpp @@ -71,7 +71,6 @@ void *connected_controller(void* thread_arg); #ifdef MY_GATEWAY_MQTT_CLIENT void *mqtt_thread(void *); -void parsemqtt_and_send(char *topic, const char *payload); struct mosquitto *mosq; #endif @@ -355,15 +354,35 @@ void mqtt_message_callback(struct mosquitto *mosq, void *userdata, const struct if(message->payloadlen){ printf("Got a MQTT message %s %s\n", message->topic, (char*)message->payload); - - std::string strpayload((char *)message->payload); - strpayload = strpayload.substr(0,(int)message->payloadlen); - - parsemqtt_and_send(message->topic, strpayload.c_str()); }else{ - printf("%s (null)\n", message->topic); + printf("Got a MQTT message %s (null)\n", message->topic); + } + + MyMessage msg; + protocolMQTTParse(msg, message->topic, (uint8_t*)message->payload, message->payloadlen); + + if(msg.destination != 0 && msg.sensor != 0 && msg.type != 255) + { + ethernetMsg_q.push_back(msg); + + // Forward the data to Ethernet + // Likely this is a duplicate from a C_SET that we received and published + // in msg_callback. Not much we can do to avoid the duplicate as there is + // no way to tell if we performed the publish that is triggering this callback. + char *ethernetMsg = protocolFormat(msg); + + for (uint8_t i = 0; i < MY_GATEWAY_MAX_CLIENTS; i++) { + if (controllers[i] == -1) + continue; + if (send(controllers[i], ethernetMsg, strlen(ethernetMsg), 0) == -1) + perror("send"); + } + } + else + { + printf("Recieved a bad MQTT message: '%s':'%s'\n destination:%i, sensor:%i, type:%i\n", + message->topic, (char*)message->payload, msg.destination, msg.sensor, msg.type); } - fflush(stdout); } void mqtt_connect_callback(struct mosquitto *mosq, void *userdata, int result) @@ -432,91 +451,4 @@ void *mqtt_thread(void *) return NULL; } -void parsemqtt_and_send(char *topic, const char *payload) -{ - //Buffer for parsemqtt_and_send. Do not want to re-create it every time we get a message - MyMessage msg; - char *str, *p; - - char* topic_copy = strdup(topic); - - - // TODO: Check if we should send ack or not. - int i = 0; - for (str = strtok_r(topic_copy,"/",&p) ; str && i<4 ; str = strtok_r(NULL,"/",&p)) - { - // Example: /mySensors/105/4/V_LIGHT - // 0: mySensors - Prefix - // 1: 105 - node - // 2: 4 - sensor - // 3: V_LIGHT - type - if (i == 0) { - //TODO: Add warning when we receive a message from an unexpected broker prefix. - //if (strcmp_P(str,broker)!=0) { //look for MQTT_BROKER_PREFIX - // return; //Message not for us or malformatted! - //} - } else if (i==1) { - printf("Destination parse %s\n", str); - msg.destination = atoi(str); //NodeID - } else if (i==2) { - msg.sensor = atoi(str); //SensorID - } else if (i==3) { - unsigned char match=255; //SensorType - - //Support for numeric types - if ( atoi(str)!=0 || (str[0]=='0' && str[1] =='\0') ) { - match=atoi(str); - } - - /* - //Check through all the data type string attary to find a match. - //? Not sure if there is any need to be checking for other types like sensor and internal - //? as those don't seem like they should be outside settable thought MQTT. - for (uint8_t j=0; match == 255 && j Date: Sun, 6 Mar 2016 20:37:02 -0800 Subject: [PATCH 06/33] Use iostream for min, max functions Use iostream for min, max functions as well as for reading / writing to file. --- libraries/MySensors/core/MyHwLinuxGeneric.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libraries/MySensors/core/MyHwLinuxGeneric.h b/libraries/MySensors/core/MyHwLinuxGeneric.h index 3546e09..cec3b6c 100644 --- a/libraries/MySensors/core/MyHwLinuxGeneric.h +++ b/libraries/MySensors/core/MyHwLinuxGeneric.h @@ -22,16 +22,17 @@ #include "MyHw.h" #include +#include #define MY_SERIALDEVICE Serial -#define min(a,b) ((a)<(b)?(a):(b)) -#define max(a,b) ((a)>(b)?(a):(b)) // Define these as macros (do nothing) #define hwDigitalWrite(__pin, __value) #define hwWatchdogReset() #define hwReboot() +using namespace std; + void hwInit(); void hwReadConfigBlock(void* buf, void* adr, size_t length); void hwWriteConfigBlock(void* buf, void* adr, size_t length); From 79c263291b47225477eebb90ad0b4118b5ca5175 Mon Sep 17 00:00:00 2001 From: aaron832 Date: Sun, 6 Mar 2016 20:56:06 -0800 Subject: [PATCH 07/33] Save eeprom config data to file hwWriteConfig and hwReadConfig now use hwWriteConfigBlock and hwReadConfigBlock calls for simplicity. --- libraries/MySensors/core/MyHwLinuxGeneric.cpp | 74 ++++++++++++++++--- 1 file changed, 65 insertions(+), 9 deletions(-) diff --git a/libraries/MySensors/core/MyHwLinuxGeneric.cpp b/libraries/MySensors/core/MyHwLinuxGeneric.cpp index 35afc37..9aa504d 100644 --- a/libraries/MySensors/core/MyHwLinuxGeneric.cpp +++ b/libraries/MySensors/core/MyHwLinuxGeneric.cpp @@ -22,18 +22,62 @@ #include #include #include +#include +#include +static const char* CONFIG_FILE = "/etc/MySensorGateway.cfg"; static const size_t _length = 1024; // ATMega328 has 1024 bytes -//TODO store _config to a file static uint8_t _config[_length]; static unsigned long millis_at_start; +bool CheckConfigFile() { + struct stat fileInfo; + + if(stat(CONFIG_FILE, &fileInfo) != 0) + { + //File does not exist. Create it. + debug("Config file %s does not exist, creating new config file.\n", CONFIG_FILE); + ofstream myFile(CONFIG_FILE, ios::out | ios::binary); + if(!myFile) + { + debug("Unable to create config file %s.\n", CONFIG_FILE); + return false; + } + myFile.write((const char*)_config, _length); + myFile.close(); + } + else if(fileInfo.st_size != _length) + { + debug("Config file %s is not the correct size of %i. Please remove the file and a new one will be created.\n", CONFIG_FILE, _length); + return false; + } + else + { + //Read config into local memory. + ifstream myFile(CONFIG_FILE, ios::in | ios::binary); + if(!myFile) + { + debug("Unable to open config to file %s for reading.\n", CONFIG_FILE); + return false; + } + myFile.read((char*)_config, _length); + myFile.close(); + } + + return true; +} + void hwInit() { timeval curTime; for (size_t i = 0; i < _length; i++) _config[i] = 0xFF; + + if (!CheckConfigFile()) + { + exit(1); + } gettimeofday(&curTime, NULL); millis_at_start = curTime.tv_sec; @@ -51,23 +95,35 @@ void hwReadConfigBlock(void* buf, void* addr, size_t length) void hwWriteConfigBlock(void* buf, void* addr, size_t length) { unsigned long int offs = reinterpret_cast(addr); - + if (length && offs + length <= _length) { memcpy(_config+offs, buf, length); + + ofstream myFile(CONFIG_FILE, ios::out | ios::binary); + if(!myFile) + { + debug("Unable to write config to file %s.\n", CONFIG_FILE); + return; + } + myFile.write((const char*)buf+offs, _length); + myFile.close(); } } -uint8_t hwReadConfig(int addr) +uint8_t hwReadConfig(int adr) { - if (addr >= 0 && (unsigned)addr < _length) - return _config[addr]; - return 0xFF; + uint8_t value = 0xFF; + hwReadConfigBlock(&value, reinterpret_cast(adr), 1); + return value; } -void hwWriteConfig(int addr, uint8_t value) +void hwWriteConfig(int adr, uint8_t value) { - if (addr >= 0 && (unsigned)addr < _length) - _config[addr] = value; + uint8_t curr = hwReadConfig(adr); + if (curr != value) + { + hwWriteConfigBlock(&value, reinterpret_cast(adr), 1); + } } unsigned long hwMillis() From 6b756335d0d57a7f6150e86cfb65c6bff805cf4a Mon Sep 17 00:00:00 2001 From: Marcelo Aquino Date: Mon, 8 Aug 2016 01:46:15 -0300 Subject: [PATCH 08/33] Refactor RaspberryPi port --- Makefile | 63 +++++ MyConfig.h | 25 +- MySensors.h | 69 ++---- configure | 322 ++++++++++++++++++++++++++ core/MyGatewayTransportEthernet.cpp | 79 ++++--- core/MyGatewayTransportMQTTClient.cpp | 101 +------- core/MyHwATMega328.h | 8 +- core/MyHwESP8266.h | 8 +- core/MyHwLinuxGeneric.cpp | 190 +++++++++++++++ core/MyHwLinuxGeneric.h | 75 ++++++ core/MyMainLinux.cpp | 39 ++++ core/MyMessage.cpp | 4 + core/MyMessage.h | 1 - core/MyProtocolMySensors.cpp | 69 +++--- core/MySensorsCore.cpp | 12 +- core/MyTransport.cpp | 4 +- core/MyTransportNRF24.cpp | 8 +- core/MyTransportRS485.cpp | 5 - drivers/Linux/Arduino.h | 212 +++++++++++++++++ drivers/Linux/Client.h | 46 ++++ drivers/Linux/EthernetClient.cpp | 253 ++++++++++++++++++++ drivers/Linux/EthernetClient.h | 80 +++++++ drivers/Linux/EthernetServer.cpp | 241 +++++++++++++++++++ drivers/Linux/EthernetServer.h | 64 +++++ drivers/Linux/IPAddress.cpp | 105 +++++++++ drivers/Linux/IPAddress.h | 87 +++++++ drivers/Linux/Server.h | 28 +++ drivers/Linux/Stream.h | 15 ++ drivers/RF24/RF24_RPi.cpp | 210 +++++++++++++++++ examples_RPi/MySGateway.cpp | 93 ++++++++ initscripts/mysgateway.systemd | 9 + initscripts/mysgateway.sysvinit | 159 +++++++++++++ 32 files changed, 2460 insertions(+), 224 deletions(-) create mode 100644 Makefile create mode 100755 configure create mode 100644 core/MyHwLinuxGeneric.cpp create mode 100644 core/MyHwLinuxGeneric.h create mode 100644 core/MyMainLinux.cpp create mode 100644 drivers/Linux/Arduino.h create mode 100644 drivers/Linux/Client.h create mode 100644 drivers/Linux/EthernetClient.cpp create mode 100644 drivers/Linux/EthernetClient.h create mode 100644 drivers/Linux/EthernetServer.cpp create mode 100644 drivers/Linux/EthernetServer.h create mode 100644 drivers/Linux/IPAddress.cpp create mode 100644 drivers/Linux/IPAddress.h create mode 100644 drivers/Linux/Server.h create mode 100644 drivers/Linux/Stream.h create mode 100644 drivers/RF24/RF24_RPi.cpp create mode 100644 examples_RPi/MySGateway.cpp create mode 100644 initscripts/mysgateway.systemd create mode 100644 initscripts/mysgateway.sysvinit diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..825d83d --- /dev/null +++ b/Makefile @@ -0,0 +1,63 @@ +############################################################################# +# +# Makefile for MySensors +# +# Description: +# ------------ +# use make all and make install to install the gateway +# + +CONFIG_FILE=Makefile.inc + +include $(CONFIG_FILE) + +GATEWAY=examples_RPi/MySGateway +GATEWAY_SOURCES=examples_RPi/MySGateway.cpp +GATEWAY_OBJECTS=$(patsubst %.cpp,%.o,$(GATEWAY_SOURCES)) +DEPS+=$(patsubst %.cpp,%.d,$(GATEWAY_SOURCES)) + +CINCLUDE=-I. -I./core -I./drivers/Linux -I$(RF24H_LIB_DIR) + +.PHONY: all gateway cleanconfig clean install uninstall force + +all: $(GATEWAY) + +# Gateway Build +$(GATEWAY): $(GATEWAY_OBJECTS) + $(CXX) $(LDFLAGS) -o $@ $(GATEWAY_OBJECTS) + +# Include all .d files +-include $(DEPS) + +%.o: %.cpp + $(CXX) $(CXXFLAGS) $(CINCLUDE) -MMD -c -o $@ $< + +# clear configuration files +cleanconfig: + @echo "[Cleaning configuration]" + rm -rf $(CONFIG_FILE) + +# clear build files +clean: + @echo "[Cleaning]" + rm -rf build $(GATEWAY_OBJECTS) $(GATEWAY) $(DEPS) + +$(CONFIG_FILE): + @echo "[Running configure]" + @./configure --no-clean + +install: all install-gateway install-initscripts + +install-gateway: + @echo "Installing $(GATEWAY) to $(GATEWAY_DIR)" + @install -m 0755 $(GATEWAY) $(GATEWAY_DIR) + +install-initscripts: + if [[ `systemctl` =~ -\.mount ]]; then \ + install -m0644 initscripts/mysgateway.systemd /etc/systemd/system/mysgateway.service && \ + sed -i -e "s|%gateway_dir%|${GATEWAY_DIR}|g" /etc/systemd/system/mysgateway.service && \ + systemctl daemon-reload; \ + elif [[ -f /etc/init.d/cron && ! -h /etc/init.d/cron ]]; then \ + install -m0755 initscripts/mysgateway.sysvinit /etc/init.d/mysgateway && \ + sed -i -e "s|%gateway_dir%|${GATEWAY_DIR}|g" /etc/init.d/mysgateway; \ + fi diff --git a/MyConfig.h b/MyConfig.h index cb775ce..5d4cec8 100644 --- a/MyConfig.h +++ b/MyConfig.h @@ -413,7 +413,7 @@ #elif defined(RASPBERRYPI_ARCH) #include #include - #ifdef __PI_BPLUS + #ifdef __RPI_BPLUS #define MY_RF24_CE_PIN RPI_BPLUS_GPIO_J8_22 #define MY_RF24_CS_PIN RPI_BPLUS_GPIO_J8_24 #else @@ -716,25 +716,34 @@ #endif /************************************** -* RaspberryPi Settings +* Linux Settings ***************************************/ /** - * @def MY_RASPBERRYPI_TTY_NAME + * @def MY_LINUX_TTY_NAME * @brief Set the name of predictable tty */ -#ifndef MY_RASPBERRYPI_TTY_NAME -#define MY_RASPBERRYPI_TTY_NAME /dev/ttyMySensorsGateway +#ifndef MY_LINUX_TTY_NAME +#define MY_LINUX_TTY_NAME "/dev/ttyMySensorsGateway" #endif /** - * @def MY_RASPBERRYPI_TTY_GROUPNAME + * @def MY_LINUX_TTY_GROUPNAME * @brief Set the group name for the raw tty */ -#ifndef MY_RASPBERRYPI_TTY_GROUPNAME -#define MY_RASPBERRYPI_TTY_GROUPNAME tty +#ifndef MY_LINUX_TTY_GROUPNAME +#define MY_LINUX_TTY_GROUPNAME tty #endif +/** + * @def MY_LINUX_CONFIG_FILE + * @brief Set the filepath for the gateway config file + * + * For now the configuration file is only used to store the emulated eeprom state + */ + #ifndef MY_LINUX_CONFIG_FILE + #define MY_LINUX_CONFIG_FILE "/etc/MySensorGateway.cfg" + #endif // Doxygen specific constructs, not included when built normally // This is used to enable disabled macros/definitions to be included in the documentation as well. diff --git a/MySensors.h b/MySensors.h index d849874..05d145f 100644 --- a/MySensors.h +++ b/MySensors.h @@ -52,6 +52,10 @@ #define MY_NODE_TYPE "NODE" #endif +#if defined(MY_GATEWAY_RASPBERRYPI) + #define MY_GATEWAY_LINUX +#endif + // Enable radio "feature" if one of the radio types was enabled #if defined(MY_RADIO_NRF24) || defined(MY_RADIO_RFM69) || defined(MY_RS485) #define MY_RADIO_FEATURE @@ -68,7 +72,7 @@ #elif defined(ARDUINO_ARCH_AVR) #include "core/MyHwATMega328.cpp" #elif defined(ARDUINO_ARCH_SAMD) - #include "core/MyHwSAMD.cpp" + #include "core/MyHwSAMD.cpp" #elif defined(LINUX_ARCH_GENERIC) // Remove PSTR macros from debug prints #undef PSTR @@ -80,6 +84,7 @@ #define snprintf_P(...) snprintf( __VA_ARGS__ ) #define memcpy_P memcpy #define pgm_read_dword(x) (*x) + #define pgm_read_byte_near(x) (*x) #include "core/MyHwLinuxGeneric.cpp" #endif @@ -197,7 +202,7 @@ #define MY_REPEATER_FEATURE #endif // GATEWAY - COMMON FUNCTIONS - // We only support MQTT Client using W5100 and ESP8266 at the moment + // We support MQTT Client using W5100, ESP8266 and Linux #if !(defined(MY_CONTROLLER_URL_ADDRESS) || defined(MY_CONTROLLER_IP_ADDRESS)) #error You must specify MY_CONTROLLER_IP_ADDRESS or MY_CONTROLLER_URL_ADDRESS #endif @@ -208,18 +213,25 @@ #if !defined(MY_MQTT_SUBSCRIBE_TOPIC_PREFIX) #error You must specify a topic subscribe prefix MY_MQTT_SUBSCRIBE_TOPIC_PREFIX for this MQTT client #endif - #if !defined(MY_MQTT_CLIENT_ID) + + #if !defined(MY_MQTT_CLIENT_ID) #error You must define a unique MY_MQTT_CLIENT_ID for this MQTT client #endif - #include "drivers/PubSubClient/PubSubClient.cpp" #include "core/MyGatewayTransport.cpp" + #include "core/MyProtocolMySensors.cpp" + + #if defined(MY_GATEWAY_LINUX) + #include "drivers/Linux/EthernetClient.cpp" + #include "drivers/Linux/EthernetServer.cpp" + #include "drivers/Linux/IPAddress.cpp" + #endif + #include "drivers/PubSubClient/PubSubClient.cpp" #include "core/MyGatewayTransportMQTTClient.cpp" #elif defined(MY_GATEWAY_FEATURE) // GATEWAY - COMMON FUNCTIONS #include "core/MyGatewayTransport.cpp" - // We currently only support one protocol at the moment, enable it. #include "core/MyProtocolMySensors.cpp" // GATEWAY - CONFIGURATION @@ -227,9 +239,6 @@ // We assume that a gateway having a radio also should act as repeater #define MY_REPEATER_FEATURE #endif - #if defined(MY_GATEWAY_RASPBERRYPI) - #define MY_GATEWAY_LINUX - #endif #if defined(MY_CONTROLLER_IP_ADDRESS) #define MY_GATEWAY_CLIENT_MODE #endif @@ -238,23 +247,16 @@ #endif #if defined(MY_GATEWAY_ESP8266) // GATEWAY - ESP8266 - #if defined(MY_GATEWAY_MQTT_CLIENT) - #include "drivers/pubsubclient/src/PubSubClient.cpp" - #include "core/MyGatewayTransportMQTTClient.cpp" - #else - #include "core/MyGatewayTransportEthernet.cpp" - #endif + #include "core/MyGatewayTransportEthernet.cpp" #elif defined(MY_GATEWAY_LINUX) - // GATEWAY - Generic Linux (RaspberryPi, BBB) - #include "core/MyGatewayTransportEthernetLinux.cpp" + // GATEWAY - Generic Linux (RaspberryPi, BBB, ...) + #include "drivers/Linux/EthernetClient.cpp" + #include "drivers/Linux/EthernetServer.cpp" + #include "drivers/Linux/IPAddress.cpp" + #include "core/MyGatewayTransportEthernet.cpp" #elif defined(MY_GATEWAY_W5100) // GATEWAY - W5100 - #if defined(MY_GATEWAY_MQTT_CLIENT) - #include "drivers/pubsubclient/src/PubSubClient.cpp" - #include "core/MyGatewayTransportMQTTClient.cpp" - #else - #include "core/MyGatewayTransportEthernet.cpp" - #endif + #include "core/MyGatewayTransportEthernet.cpp" #elif defined(MY_GATEWAY_ENC28J60) // GATEWAY - ENC28J60 #if defined(MY_USE_UDP) @@ -264,32 +266,9 @@ #elif defined(MY_GATEWAY_SERIAL) // GATEWAY - SERIAL #include "core/MyGatewayTransportSerial.cpp" - #endif #endif -// GATEWAY - MQTT -#if defined(MY_GATEWAY_MQTT_CLIENT) && !defined(MY_GATEWAY_LINUX) - // GATEWAY - COMMON FUNCTIONS - // We only support MQTT Client using W5100, ESP8266 or Linux/RasperryPi at the moment - #if !(defined(MY_GATEWAY_ESP8266) || defined(MY_GATEWAY_W5100) || defined(MY_GATEWAY_LINUX)) - #error We only support MQTT Client using W5100, ESP8266 or Linux/RasperryPi at the moment - #endif - - #if !(defined(MY_CONTROLLER_URL_ADDRESS) || defined(MY_CONTROLLER_IP_ADDRESS)) - #error You must specify MY_CONTROLLER_IP_ADDRESS or MY_CONTROLLER_URL_ADDRESS - #endif - - #if !defined(MY_MQTT_PUBLISH_TOPIC_PREFIX) - #error You must specify a topic publish prefix MY_MQTT_PUBLISH_TOPIC_PREFIX for this MQTT client - #endif - #if !defined(MY_MQTT_SUBSCRIBE_TOPIC_PREFIX) - #error You must specify a topic subscribe prefix MY_MQTT_SUBSCRIBE_TOPIC_PREFIX for this MQTT client - #endif - #if !defined(MY_MQTT_CLIENT_ID) - #error You must define a unique MY_MQTT_CLIENT_ID for this MQTT client - #endif -#endif // RADIO #if defined(MY_RADIO_NRF24) || defined(MY_RADIO_RFM69) || defined(MY_RS485) diff --git a/configure b/configure new file mode 100755 index 0000000..ad46759 --- /dev/null +++ b/configure @@ -0,0 +1,322 @@ +#!/bin/bash +# This version is heavily based on the work of mz-fuzzy (https://github.com/mz-fuzzy) +# adapted to work with MySensors project. +# Original work: https://github.com/TMRh20/RF24/blob/master/configure + +function help { +cat < CPU defining/optimizing flags to be used. [configure autodetected] + --extra-cxxflags= Extra C flags passed to C/C++ compilation. [] + --extra-ldflags= Extra C flags passed to linking. [] + --libname= Library name. [rf24] + --cxx_compiler= C++ compiler [arm-linux-gnueabihf-g++][g++] + --no-clean Don't clean previous build artifacts. + --rf24h-lib-dir RF24 library files directory. + +Installation options: + --prefix= Installation prefix path. [/usr/local] + --gateway-dir= Gateway files installation directory. [PREFIX/bin] + +MySensors options: + --my-debug Enables debug. + --my-gateway-type=[ethernet|mqtt] + Gateway type, default to ethernet. + --my-config-file= Config file path. [/etc/MySensorGateway.cfg] + --my-rf24-enable Enables RF24 radio. + --my-rf24-channel=<0-125> RF channel for the sensor net, 0-125. + --my-rf24-pa-level=[RF24_PA_MAX|RF24_PA_LOW] + RF24 PA level. + --my-controller-url-address= + Controller or MQTT broker url. + --my-controller-ip-address= + Controller or MQTT broker ip. + Use commas instead of points. Example: 127,0,0,1 + --my-port Controller or MQTT broker port. + --my-mqtt-client-id= MQTT client id. + --my-mqtt-publish-topic-prefix= + MQTT publish topic prefix. + --my-mqtt-subscribe-topic-prefix= + MQTT subscribe topic prefix. + --my-rf24-irq-pin Pin number connected to nRF24L01 IRQ pin. + --my-rx-message-buffer-size Buffer size for incoming messages when using rf24 interrupts. + Default set to 20. +EOF +} + +function die { + echo "[ERROR] $1" + exit $2 +} + +function detect_rpi_revision { + # get PI Revision from cpuinfo + local pirev=$(eval "cat /proc/cpuinfo 2>/dev/null | grep Revision | cut -f 2 -d ':' | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$$//'") + echo ${pirev} +} + +function detect_machine { + local cpu=$(eval "uname -m 2>/dev/null") + local machine=$(eval "cat /sys/firmware/devicetree/base/model 2>/dev/null") + local hardware=$(eval "grep sunxi_platform /sys/class/sunxi_info/sys_info 2>/dev/null | sed 's/^.*: \(.*\)$/\1/'") + if [ -z "$hardware" ]; then + local hardware=$(eval "grep Hardware /proc/cpuinfo 2>/dev/null | sed 's/^.*: \(.*\)$/\1/'") + fi + local soc="unknown" + local tp="unknown" + local rev="unknown" + + if [ -z "$cpu" ]; then + cpu="unknown" + fi + + case $hardware in + BCM2708) + soc="BCM2835" + if [[ $machine == "Raspberry"* ]]; then + tp="RPi" + rev=($(detect_rpi_revision)) + fi + ;; + BCM2709) + soc="BCM2836" + if [[ $machine == "Raspberry"* ]]; then + rev=($(detect_rpi_revision)) + if [[ $rev == "a02082" || $rev == "a22082" ]]; then + tp="RPi3" + else + tp="Rpi2" + fi + fi + ;; + sun4i|Sun4iw1p1) + soc="A10" + ;; + sun5i|Sun4iw2p1) + soc="A13" + ;; + Sun4iw2p2) + soc="A12" + ;; + Sun4iw2p3) + soc="A10s" + ;; + sun6i|Sun8iw1p1) + soc="A31" + ;; + Sun8iw1p2) + soc="A31s" + ;; + sun7i|Sun8iw2p1) + soc="A20" + if [[ $machine == "Banana Pi"* ]]; then + tp="BananaPi" + elif [[ $machine == "Banana Pro"* ]]; then + tp="BananaPro" + fi + ;; + sun8i|Sun8iw7p1) + soc="H3" + ;; + Sun8iw3p1) + soc="A23" + ;; + Sun8iw5p1) + soc="A33" + ;; + Sun8iw6p1) + soc="A83t" + ;; + sun9i|Sun9iw1p1) + soc="A80" + ;; + Sun9iw1p2) + soc="A80t" + ;; + sun50i|Sun50iw1p1) + soc="A64" + ;; + 'Generic AM33XX'*) + soc="AM33XX" + ;; + *) + soc="unknown" + esac + echo "${soc} ${tp} ${cpu} ${rev}" +} + +function gcc_cpu_flags { + local soc=$1 + case $soc in + BCM2835) + flags="-march=armv6zk -mtune=arm1176jzf-s -mfpu=vfp -mfloat-abi=hard -DRASPBERRYPI_ARCH" + ;; + BCM2836) + flags="-march=armv7-a -mtune=cortex-a7 -mfpu=neon-vfpv4 -mfloat-abi=hard -DRASPBERRYPI_ARCH" + ;; + AM33XX) + flags="-march=armv7-a -mtune=cortex-a8 -mfpu=neon -mfloat-abi=hard" + ;; + A10) + flags="-march=armv7-a -mtune=cortex-a8 -mfpu=neon -mfloat-abi=hard" + ;; + A13) + flags="-march=armv7-a -mtune=cortex-a8 -mfpu=neon -mfloat-abi=hard" + ;; + A20) + flags="-march=armv7-a -mtune=cortex-a7 -mfpu=neon-vfpv4 -mfloat-abi=hard" + ;; + H3) + flags="-march=armv8-a -mtune=cortex-a53 -mfpu=neon-vfpv4 -mfloat-abi=hard" + ;; + *) + flags="" + esac + echo ${flags} +} + +params="OS SOC CPUFLAGS CXXFLAGS LDFLAGS PREFIX CXX RF24H_LIB_DIR GATEWAY_DIR" + +for opt do + if [ "$opt" = "-h" ] || [ "$opt" = "--help" ]; then + help + exit 0 + fi + optarg="${opt#*=}" + case "$opt" in + --os=*) + OS="$optarg" + ;; + --soc=*) + SOC="$optarg" + ;; + --cpu-flags=*) + CPUFLAGS="$optarg" + ;; + --extra-cxxflags=*) + CXXFLAGS="$optarg" + ;; + --extra-ldflags=*) + LDFLAGS="$optarg" + ;; + --cxx_compiler=*) + CXX="$optarg" + ;; + --no-clean*) + NO_CLEAN="1" + ;; + --prefix=*) + PREFIX="$optarg" + ;; + --rf24h-lib-dir=*) + RF24H_LIB_DIR="$optarg" + ;; + --gateway-dir=*) + GATEWAY_DIR="$optarg" + ;; + --my-debug*) + CXXFLAGS="-DMY_DEBUG $CXXFLAGS" + ;; + --my-gateway-type=*) + if [[ ${optarg} == "mqtt" ]]; then + LDFLAGS="-lmosquitto $LDFLAGS" + CXXFLAGS="-DMY_GATEWAY_MQTT_CLIENT $CXXFLAGS" + fi + ;; + --my-config-file=*) + CXXFLAGS="-DMY_CONFIG_FILE=${optarg} $CXXFLAGS" + ;; + --my-rf24-enable*) + CXXFLAGS="-DMY_RADIO_NRF24 $CXXFLAGS" + ;; + --my-rf24-channel=*) + CXXFLAGS="-DMY_RF24_CHANNEL=${optarg} $CXXFLAGS" + ;; + --my-rf24-pa-level=*) + CXXFLAGS="-DMY_RF24_PA_LEVEL=${optarg} $CXXFLAGS" + ;; + --my-controller-url-address=*) + CXXFLAGS="-DMY_CONTROLLER_URL_ADDRESS=${optarg} $CXXFLAGS" + ;; + --my-controller-ip-address=*) + CXXFLAGS="-DMY_CONTROLLER_IP_ADDRESS=${optarg} $CXXFLAGS" + ;; + --my-port=*) + CXXFLAGS="-DMY_PORT=${optarg} $CXXFLAGS" + ;; + --my-mqtt-client-id=*) + CXXFLAGS="-DMY_MQTT_CLIENT_ID=${optarg} $CXXFLAGS" + ;; + --my-mqtt-publish-topic-prefix=*) + CXXFLAGS="-DMY_MQTT_PUBLISH_TOPIC_PREFIX=${optarg} $CXXFLAGS" + ;; + --my-mqtt-subscribe-topic-prefix=*) + CXXFLAGS="-DMY_MQTT_SUBSCRIBE_TOPIC_PREFIX=${optarg} $CXXFLAGS" + ;; + --my-rf24-irq-pin=*) + CXXFLAGS="-DMY_RF24_IRQ_PIN=${optarg} $CXXFLAGS" + ;; + --my-rx-message-buffer-size=*) + CXXFLAGS="-DMY_RX_MESSAGE_BUFFER_SIZE=${optarg} $CXXFLAGS" + ;; + *) + echo "[WARNING] Unknown option detected:$opt, ignored" + ;; + esac +done + +PREFIX=${PREFIX:-/usr/local} +RF24H_LIB_DIR=${PREFIX}/include/RF24 +GATEWAY_DIR=${GATEWAY_DIR:-${PREFIX}/bin} +CXX=${CXX:-g++} + +if [ -z "${SOC}" ]; then + echo "[SECTION] Detecting target machine." + info=($(detect_machine)) + SOC=${info[0]} + TYPE=${info[1]} + CPU=${info[2]} + REV=${info[3]} + echo "[OK] machine detected: SoC=${SOC}, Type=${TYPE}, CPU=${CPU}, REV=${REV}." +fi + +if [ -z "${CPUFLAGS}" ]; then + CPUFLAGS=$(gcc_cpu_flags $SOC) +fi + +CXXFLAGS="$CPUFLAGS -Ofast -g -Wall -Wextra -DLINUX_ARCH_GENERIC $CXXFLAGS" + +if [[ $TYPE == "RPi2" || $TYPE == "RPi3" || $REV == "0010" ]]; then + CXXFLAGS+="-D__RPI_BPLUS" +fi + +LDFLAGS="-pthread -lrf24-bcm $LDFLAGS" + +echo "[SECTION] Saving configuration." +echo -n "" > Makefile.inc +for param in ${params}; do + if [[ ${!param} ]]; then + echo "${param}=${!param}" >> Makefile.inc + fi +done + +if [ -z "${NO_CLEAN}" ]; then + echo "[SECTION] Cleaning previous builds." + make clean >/dev/null +fi + + +echo "[OK] Finished." diff --git a/core/MyGatewayTransportEthernet.cpp b/core/MyGatewayTransportEthernet.cpp index 6c930e7..a8f6599 100644 --- a/core/MyGatewayTransportEthernet.cpp +++ b/core/MyGatewayTransportEthernet.cpp @@ -26,7 +26,7 @@ #if defined(MY_IP_ADDRESS) IPAddress _ethernetGatewayIP(MY_IP_ADDRESS); #endif -byte _ethernetGatewayMAC[] = { MY_MAC_ADDRESS }; +uint8_t _ethernetGatewayMAC[] = { MY_MAC_ADDRESS }; uint16_t _ethernetGatewayPort = MY_PORT; MyMessage _ethernetMsg; @@ -36,8 +36,8 @@ MyMessage _ethernetMsg; typedef struct { - char string[MY_GATEWAY_MAX_RECEIVE_LENGTH]; - uint8_t idx; + char string[MY_GATEWAY_MAX_RECEIVE_LENGTH]; + uint8_t idx; } inputBuffer; #if defined(MY_GATEWAY_ESP8266) @@ -50,7 +50,6 @@ typedef struct IPAddress _gatewayIp(MY_IP_GATEWAY_ADDRESS); IPAddress _subnetIp(MY_IP_SUBNET_ADDRESS); #endif - static bool clientsConnected[MY_GATEWAY_MAX_CLIENTS]; #endif #if defined(MY_USE_UDP) @@ -60,8 +59,9 @@ typedef struct #endif -#if defined(MY_GATEWAY_ESP8266) +#if defined(MY_GATEWAY_ESP8266) || defined(MY_GATEWAY_LINUX) static EthernetClient clients[MY_GATEWAY_MAX_CLIENTS]; + static bool clientsConnected[MY_GATEWAY_MAX_CLIENTS]; static inputBuffer inputString[MY_GATEWAY_MAX_CLIENTS]; #else static EthernetClient client = EthernetClient(); @@ -78,18 +78,18 @@ typedef struct #ifdef MY_W5100_SPI_EN void _w5100_spi_en(bool enable) { - if (enable) - { - // Pull up pin - pinMode(MY_W5100_SPI_EN, INPUT); - digitalWrite(MY_W5100_SPI_EN, HIGH); - } - else - { - // Ground pin - pinMode(MY_W5100_SPI_EN, OUTPUT); - digitalWrite(MY_W5100_SPI_EN, LOW); - } + if (enable) + { + // Pull up pin + pinMode(MY_W5100_SPI_EN, INPUT); + digitalWrite(MY_W5100_SPI_EN, HIGH); + } + else + { + // Ground pin + pinMode(MY_W5100_SPI_EN, OUTPUT); + digitalWrite(MY_W5100_SPI_EN, LOW); + } } #else #define _w5100_spi_en(x) @@ -119,7 +119,8 @@ bool gatewayTransportInit() { MY_SERIALDEVICE.print(F("IP: ")); MY_SERIALDEVICE.println(WiFi.localIP()); #endif - + #elif defined(MY_GATEWAY_LINUX) + // Nothing to do here #else #ifdef MY_IP_ADDRESS Ethernet.begin(_ethernetGatewayMAC, _ethernetGatewayIP); @@ -140,8 +141,12 @@ bool gatewayTransportInit() { #ifdef MY_USE_UDP _ethernetServer.begin(_ethernetGatewayPort); #else - // we have to use pointers due to the constructor of EthernetServer - _ethernetServer.begin(); + #if defined(MY_GATEWAY_LINUX) && defined(MY_IP_ADDRESS) + _ethernetServer.begin(_ethernetGatewayIP); + #else + // we have to use pointers due to the constructor of EthernetServer + _ethernetServer.begin(); + #endif #endif /* USE_UDP */ _w5100_spi_en(false); return true; @@ -152,7 +157,7 @@ bool gatewayTransportSend(MyMessage &message) bool ret = true; char *_ethernetMsg = protocolFormat(message); - setIndication(INDICATION_GW_TX); + setIndication(INDICATION_GW_TX); _w5100_spi_en(true); #if defined(MY_CONTROLLER_IP_ADDRESS) @@ -164,16 +169,16 @@ bool gatewayTransportSend(MyMessage &message) #else EthernetClient client; #if defined(MY_CONTROLLER_URL_ADDRESS) - if (client.connected() || client.connect(MY_CONTROLLER_URL_ADDRESS, MY_PORT)) { - #else - if (client.connected() || client.connect(_ethernetControllerIP, MY_PORT)) { - #endif - client.write(_ethernetMsg, strlen(_ethernetMsg)); - } - else { - // connecting to the server failed! - ret = false; - } + if (client.connected() || client.connect(MY_CONTROLLER_URL_ADDRESS, MY_PORT)) { + #else + if (client.connected() || client.connect(_ethernetControllerIP, MY_PORT)) { + #endif + client.write(_ethernetMsg, strlen(_ethernetMsg)); + } + else { + // connecting to the server failed! + ret = false; + } #endif #else // Send message to connected clients @@ -195,7 +200,7 @@ bool gatewayTransportSend(MyMessage &message) } -#if defined(MY_GATEWAY_ESP8266) +#if defined(MY_GATEWAY_ESP8266) || defined(MY_GATEWAY_LINUX) bool _readFromClient(uint8_t i) { while (clients[i].connected() && clients[i].available()) { char inChar = clients[i].read(); @@ -270,7 +275,7 @@ bool gatewayTransportAvailable() if (packet_size) { //debug(PSTR("UDP packet available. Size:%d\n"), packet_size); - setIndication(INDICATION_GW_RX); + setIndication(INDICATION_GW_RX); #if defined(MY_GATEWAY_ESP8266) _ethernetServer.read(inputString[0].string, MY_GATEWAY_MAX_RECEIVE_LENGTH); inputString[0].string[packet_size] = 0; @@ -285,7 +290,7 @@ bool gatewayTransportAvailable() #endif } #else - #if defined(MY_GATEWAY_ESP8266) + #if defined(MY_GATEWAY_ESP8266) || defined(MY_GATEWAY_LINUX) // ESP8266: Go over list of clients and stop any that are no longer connected. // If the server has a new client connection it will be assigned to a free slot. bool allSlotsOccupied = true; @@ -318,7 +323,7 @@ bool gatewayTransportAvailable() // Loop over clients connect and read available data for (uint8_t i = 0; i < ARRAY_SIZE(clients); i++) { if (_readFromClient(i)) { - setIndication(INDICATION_GW_RX); + setIndication(INDICATION_GW_RX); _w5100_spi_en(false); return true; } @@ -344,7 +349,7 @@ bool gatewayTransportAvailable() client.stop(); } else { if (_readFromClient()) { - setIndication(INDICATION_GW_RX); + setIndication(INDICATION_GW_RX); _w5100_spi_en(false); return true; } @@ -363,7 +368,7 @@ MyMessage& gatewayTransportReceive() } -#if !defined(MY_IP_ADDRESS) && !defined(MY_GATEWAY_ESP8266) +#if !defined(MY_IP_ADDRESS) && !defined(MY_GATEWAY_ESP8266) && !defined(MY_GATEWAY_LINUX) void gatewayTransportRenewIP() { /* renew/rebind IP address diff --git a/core/MyGatewayTransportMQTTClient.cpp b/core/MyGatewayTransportMQTTClient.cpp index b6ca377..85f5186 100644 --- a/core/MyGatewayTransportMQTTClient.cpp +++ b/core/MyGatewayTransportMQTTClient.cpp @@ -32,8 +32,10 @@ IPAddress _gatewayIp(MY_IP_GATEWAY_ADDRESS); IPAddress _subnetIp(MY_IP_SUBNET_ADDRESS); #endif +#elif defined(MY_GATEWAY_LINUX) + // Nothing to do here #else - byte _MQTT_clientMAC[] = { MY_MAC_ADDRESS }; + uint8_t _MQTT_clientMAC[] = { MY_MAC_ADDRESS }; #endif #if defined(MY_IP_ADDRESS) @@ -46,98 +48,20 @@ static bool _MQTT_connecting = true; static bool _MQTT_available = false; static MyMessage _MQTT_msg; -uint8_t protocolH2i(char c) { - uint8_t i = 0; - if (c <= '9') - i += c - '0'; - else if (c >= 'a') - i += c - 'a' + 10; - else - i += c - 'A' + 10; - return i; -} - - bool gatewayTransportSend(MyMessage &message) { if (!_MQTT_client.connected()) return false; setIndication(INDICATION_GW_TX); - char _fmtBuffer[MY_GATEWAY_MAX_SEND_LENGTH]; - char _convBuffer[MAX_PAYLOAD * 2 + 1]; - snprintf_P(_fmtBuffer, MY_GATEWAY_MAX_SEND_LENGTH, PSTR(MY_MQTT_PUBLISH_TOPIC_PREFIX "/%d/%d/%d/%d/%d"), message.sender, message.sensor, mGetCommand(message), mGetAck(message), message.type); - debug(PSTR("Sending message on topic: %s\n"), _fmtBuffer); - return _MQTT_client.publish(_fmtBuffer, message.getString(_convBuffer)); + char *topic = protocolFormatMQTTTopic(MY_MQTT_PUBLISH_TOPIC_PREFIX, message); + debug(PSTR("Sending message on topic: %s\n"), topic); + return _MQTT_client.publish(topic, message.getString(_convBuffer)); } -void incomingMQTT(char* topic, byte* payload, unsigned int length) { +void incomingMQTT(char* topic, uint8_t* payload, unsigned int length) { debug(PSTR("Message arrived on topic: %s\n"), topic); - char *str, *p; - uint8_t i = 0; - uint8_t bvalue[MAX_PAYLOAD]; - uint8_t blen = 0; - uint8_t command = 0; - for (str = strtok_r(topic, "/", &p); str && i <= 5; - str = strtok_r(NULL, "/", &p)) { - switch (i) { - case 0: { - // Topic prefix - if (strcmp(str, MY_MQTT_SUBSCRIBE_TOPIC_PREFIX) != 0) { - // Message not for us or malformed! - return; - } - break; - } - case 1: { - // Node id - _MQTT_msg.destination = atoi(str); - break; - } - case 2: { - // Sensor id - _MQTT_msg.sensor = atoi(str); - break; - } - case 3: { - // Command type - command = atoi(str); - mSetCommand(_MQTT_msg, command); - break; - } - case 4: { - // Ack flag - mSetRequestAck(_MQTT_msg, atoi(str) ? 1 : 0); - break; - } - case 5: { - // Sub type - _MQTT_msg.type = atoi(str); - // Add payload - if (command == C_STREAM) { - blen = 0; - uint8_t val; - while (*payload) { - val = protocolH2i(*payload++) << 4; - val += protocolH2i(*payload++); - bvalue[blen] = val; - blen++; - } - _MQTT_msg.set(bvalue, blen); - } - else { - char* ca; - ca = (char *)payload; - ca += length; - *ca = '\0'; - _MQTT_msg.set((const char*)payload); - } - _MQTT_available = true; - } - } - i++; - } + _MQTT_available = protocolMQTTParse(_MQTT_msg, topic, payload, length); } - bool reconnectMQTT() { debug(PSTR("Attempting MQTT connection...\n")); // Attempt to connect @@ -168,6 +92,10 @@ bool gatewayTransportConnect() { } MY_SERIALDEVICE.print("IP: "); MY_SERIALDEVICE.println(WiFi.localIP()); + #elif defined(MY_GATEWAY_LINUX) + #if defined(MY_IP_ADDRESS) + //TODO + #endif #else #ifdef MY_IP_ADDRESS Ethernet.begin(_MQTT_clientMAC, _MQTT_clientIp); @@ -215,8 +143,6 @@ bool gatewayTransportInit() { return true; } - - bool gatewayTransportAvailable() { if (_MQTT_connecting) @@ -239,6 +165,3 @@ MyMessage & gatewayTransportReceive() { _MQTT_available = false; return _MQTT_msg; } - - - diff --git a/core/MyHwATMega328.h b/core/MyHwATMega328.h index 157ecb1..c486239 100644 --- a/core/MyHwATMega328.h +++ b/core/MyHwATMega328.h @@ -57,7 +57,13 @@ do { \ // Define these as macros to save valuable space #define hwDigitalWrite(__pin, __value) (digitalWrite(__pin, __value)) -#define hwInit() MY_SERIALDEVICE.begin(MY_BAUD_RATE) + +#if defined(MY_DISABLED_SERIAL) + #define hwInit() +#else + #define hwInit() MY_SERIALDEVICE.begin(MY_BAUD_RATE) +#endif + #define hwWatchdogReset() wdt_reset() #define hwReboot() wdt_enable(WDTO_15MS); while (1) #define hwMillis() millis() diff --git a/core/MyHwESP8266.h b/core/MyHwESP8266.h index 1495e69..2c5a942 100644 --- a/core/MyHwESP8266.h +++ b/core/MyHwESP8266.h @@ -33,7 +33,13 @@ // Define these as macros to save valuable space #define hwDigitalWrite(__pin, __value) (digitalWrite(__pin, __value)) -#define hwInit() MY_SERIALDEVICE.begin(MY_BAUD_RATE); MY_SERIALDEVICE.setDebugOutput(true) + +#if defined(MY_DISABLED_SERIAL) + #define hwInit() +#else + #define hwInit() MY_SERIALDEVICE.begin(MY_BAUD_RATE); MY_SERIALDEVICE.setDebugOutput(true) +#endif + #define hwWatchdogReset() wdt_reset() #define hwReboot() ESP.restart(); #define hwMillis() millis() diff --git a/core/MyHwLinuxGeneric.cpp b/core/MyHwLinuxGeneric.cpp new file mode 100644 index 0000000..272fe29 --- /dev/null +++ b/core/MyHwLinuxGeneric.cpp @@ -0,0 +1,190 @@ +/** + * The MySensors Arduino library handles the wireless radio link and protocol + * between your home built sensors/actuators and HA controller of choice. + * The sensors forms a self healing radio network with optional repeaters. Each + * repeater and gateway builds a routing tables in EEPROM which keeps track of the + * network topology allowing messages to be routed to nodes. + * + * Created by Marcelo Aquino + * Copyright (C) 2016 Marcelo Aquino + * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors + * + * Documentation: http://www.mysensors.org + * Support Forum: http://forum.mysensors.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + */ + +#include "MyHwLinuxGeneric.h" + +#include +#include +#include +#include +#include + +static const char* CONFIG_FILE = MY_LINUX_CONFIG_FILE; +static const size_t _length = 1024; // ATMega328 has 1024 bytes +static uint8_t _config[_length]; +static unsigned long millis_at_start; + +bool CheckConfigFile() { + struct stat fileInfo; + + if(stat(CONFIG_FILE, &fileInfo) != 0) + { + //File does not exist. Create it. + debug("Config file %s does not exist, creating new config file.\n", CONFIG_FILE); + ofstream myFile(CONFIG_FILE, ios::out | ios::binary); + if(!myFile) + { + debug("Unable to create config file %s.\n", CONFIG_FILE); + return false; + } + myFile.write((const char*)_config, _length); + myFile.close(); + } + else if(fileInfo.st_size < 0 || (size_t)fileInfo.st_size != _length) + { + debug("Config file %s is not the correct size of %i. Please remove the file and a new one will be created.\n", CONFIG_FILE, _length); + return false; + } + else + { + //Read config into local memory. + ifstream myFile(CONFIG_FILE, ios::in | ios::binary); + if(!myFile) + { + debug("Unable to open config to file %s for reading.\n", CONFIG_FILE); + return false; + } + myFile.read((char*)_config, _length); + myFile.close(); + } + + return true; +} + +void hwInit() +{ + timeval curTime; + + for (size_t i = 0; i < _length; i++) + _config[i] = 0xFF; + + if (!CheckConfigFile()) + { + exit(1); + } + + gettimeofday(&curTime, NULL); + millis_at_start = curTime.tv_sec; +} + +void hwReadConfigBlock(void* buf, void* addr, size_t length) +{ + unsigned long int offs = reinterpret_cast(addr); + + if (length && offs + length <= _length) { + memcpy(buf, _config+offs, length); + } +} + +void hwWriteConfigBlock(void* buf, void* addr, size_t length) +{ + unsigned long int offs = reinterpret_cast(addr); + + if (length && offs + length <= _length) { + memcpy(_config+offs, buf, length); + + ofstream myFile(CONFIG_FILE, ios::out | ios::binary); + if(!myFile) + { + debug("Unable to write config to file %s.\n", CONFIG_FILE); + return; + } + myFile.write((const char*)buf+offs, _length); + myFile.close(); + } +} + +uint8_t hwReadConfig(int adr) +{ + uint8_t value = 0xFF; + hwReadConfigBlock(&value, reinterpret_cast(adr), 1); + return value; +} + +void hwWriteConfig(int adr, uint8_t value) +{ + uint8_t curr = hwReadConfig(adr); + if (curr != value) + { + hwWriteConfigBlock(&value, reinterpret_cast(adr), 1); + } +} + +unsigned long hwMillis() +{ + timeval curTime; + + gettimeofday(&curTime, NULL); + return ((curTime.tv_sec - millis_at_start) * 1000) + (curTime.tv_usec / 1000); +} + +// TODO: Not supported! +int8_t hwSleep(unsigned long ms) +{ + (void)ms; + + return -2; +} + +// TODO: Not supported! +int8_t hwSleep(uint8_t interrupt, uint8_t mode, unsigned long ms) +{ + (void)interrupt; + (void)mode; + (void)ms; + + return -2; +} + +// TODO: Not supported! +int8_t hwSleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2, unsigned long ms) +{ + (void)interrupt1; + (void)mode1; + (void)interrupt2; + (void)mode2; + (void)ms; + + return -2; +} + +uint16_t hwCPUVoltage() { + // TODO: Not supported! + return 0; +} + +uint16_t hwCPUFrequency() { + // TODO: Not supported! + return 0; +} + +uint16_t hwFreeMem() { + // TODO: Not supported! + return 0; +} + +#ifdef MY_DEBUG +void hwDebugPrint(const char *fmt, ... ) +{ + va_list arglist; + va_start(arglist, fmt); + vprintf(fmt, arglist); + va_end(arglist); +} +#endif diff --git a/core/MyHwLinuxGeneric.h b/core/MyHwLinuxGeneric.h new file mode 100644 index 0000000..32e8406 --- /dev/null +++ b/core/MyHwLinuxGeneric.h @@ -0,0 +1,75 @@ +/** + * The MySensors Arduino library handles the wireless radio link and protocol + * between your home built sensors/actuators and HA controller of choice. + * The sensors forms a self healing radio network with optional repeaters. Each + * repeater and gateway builds a routing tables in EEPROM which keeps track of the + * network topology allowing messages to be routed to nodes. + * + * Created by Marcelo Aquino + * Copyright (C) 2016 Marcelo Aquino + * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors + * + * Documentation: http://www.mysensors.org + * Support Forum: http://forum.mysensors.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + */ + +#ifndef MyHwLinuxGeneric_h +#define MyHwLinuxGeneric_h + +#include "MyHw.h" +#include +#include +#include + +#define MY_SERIALDEVICE Serial + +// Define these as macros (do nothing) +#define hwDigitalWrite(__pin, __value) +#define hwWatchdogReset() +#define hwReboot() + +using namespace std; + +void hwInit(); +void hwReadConfigBlock(void* buf, void* adr, size_t length); +void hwWriteConfigBlock(void* buf, void* adr, size_t length); +void hwWriteConfig(int adr, uint8_t value); +uint8_t hwReadConfig(int adr); +unsigned long hwMillis(); + +#ifdef MY_RF24_IRQ_PIN +static pthread_mutex_t hw_mutex = PTHREAD_MUTEX_INITIALIZER; + +static __inline__ void __hwUnlock(const uint8_t *__s) { + pthread_mutex_unlock(&hw_mutex); + (void)__s; +} + +static __inline__ void __hwLock() { + pthread_mutex_lock(&hw_mutex); +} +#endif + +#if defined(__DOXYGEN__) + #define ATOMIC_BLOCK_CLEANUP +#elif defined(MY_RF24_IRQ_PIN) + #define ATOMIC_BLOCK_CLEANUP uint8_t __atomic_loop \ + __attribute__((__cleanup__( __hwUnlock ))) = 1 +#endif /* __DOXYGEN__ */ + +#if defined(__DOXYGEN__) + #define ATOMIC_BLOCK +#elif defined(MY_RF24_IRQ_PIN) + #define ATOMIC_BLOCK for ( ATOMIC_BLOCK_CLEANUP, __hwLock(); \ + __atomic_loop ; __atomic_loop = 0 ) +#endif /* __DOXYGEN__ */ + +#ifndef DOXYGEN + #define MY_CRITICAL_SECTION ATOMIC_BLOCK +#endif /* DOXYGEN */ + +#endif diff --git a/core/MyMainLinux.cpp b/core/MyMainLinux.cpp new file mode 100644 index 0000000..5ecab96 --- /dev/null +++ b/core/MyMainLinux.cpp @@ -0,0 +1,39 @@ +// Initialize library and handle sketch functions like we want to + +#include +#include +#include "MySensorsCore.h" + +volatile static int running = 1; + +/* + * handler for SIGINT signal + */ +void handle_sigint(int sig) +{ + running = 0; + + if (sig == SIGINT) { + std::cout << "Received SIGINT\n" << std::endl; + } else if (sig == SIGTERM) { + std::cout << "Received SIGTERM\n" << std::endl; + } + + #ifdef MY_RF24_IRQ_PIN + detachInterrupt(MY_RF24_IRQ_PIN); + #endif +} + +int main(void) { + /* register the signal handler */ + signal(SIGINT, handle_sigint); + signal(SIGTERM, handle_sigint); + + _begin(); // Startup MySensors library + + while (running) { + _process(); // Process incoming data + if (loop) loop(); // Call sketch loop + } + return 0; +} diff --git a/core/MyMessage.cpp b/core/MyMessage.cpp index 027b273..1179a0b 100644 --- a/core/MyMessage.cpp +++ b/core/MyMessage.cpp @@ -21,7 +21,11 @@ #include "MyMessage.h" #include #include +#include +#ifndef ARDUINO + #define min(a,b) ((a)<(b)?(a):(b)) +#endif MyMessage::MyMessage() { diff --git a/core/MyMessage.h b/core/MyMessage.h index aa84b49..b11a7ff 100644 --- a/core/MyMessage.h +++ b/core/MyMessage.h @@ -32,7 +32,6 @@ #ifdef __cplusplus #include -#include #include #endif diff --git a/core/MyProtocolMySensors.cpp b/core/MyProtocolMySensors.cpp index 18fdbd5..8c96b42 100644 --- a/core/MyProtocolMySensors.cpp +++ b/core/MyProtocolMySensors.cpp @@ -20,6 +20,7 @@ #include "MyConfig.h" #include "MyTransport.h" #include "MyProtocol.h" +#include uint8_t protocolH2i(char c); @@ -32,7 +33,6 @@ bool protocolParse(MyMessage &message, char *inputString) { uint8_t blen = 0; int i = 0; uint8_t command = 0; - uint8_t ack = 0; // Extract command data coming on serial line for (str = strtok_r(inputString, ";", &p); // split using semicolon @@ -51,7 +51,7 @@ bool protocolParse(MyMessage &message, char *inputString) { mSetCommand(message, command); break; case 3: // Should we request ack from destination? - ack = atoi(str); + mSetRequestAck(message, atoi(str)?1:0); break; case 4: // Data type message.type = atoi(str); @@ -86,8 +86,7 @@ bool protocolParse(MyMessage &message, char *inputString) { message.sender = GATEWAY_ADDRESS; message.last = GATEWAY_ADDRESS; - mSetRequestAck(message, ack?1:0); - mSetAck(message, false); + mSetAck(message, false); if (command == C_STREAM) message.set(bvalue, blen); else @@ -110,23 +109,23 @@ char * protocolFormatMQTTSubscribe(const char* prefix) { return _fmtBuffer; } -void protocolMQTTParse(MyMessage &message, char* topic, uint8_t* payload, unsigned int length) { - //TODO: Not sure if we should copy the topic and payload into a buffer so we are not modifying original. +#ifdef MY_GATEWAY_MQTT_CLIENT +bool protocolMQTTParse(MyMessage &message, char* topic, uint8_t* payload, unsigned int length) { char *str, *p; uint8_t i = 0; uint8_t bvalue[MAX_PAYLOAD]; uint8_t blen = 0; uint8_t command = 0; + for (str = strtok_r(topic, "/", &p); str && i <= 5; str = strtok_r(NULL, "/", &p)) { switch (i) { case 0: { // Topic prefix - //TODO: Should be checking the topic prefix - //if (strcmp(str, MY_MQTT_SUBSCRIBE_TOPIC_PREFIX) != 0) { + if (strcmp(str, MY_MQTT_SUBSCRIBE_TOPIC_PREFIX) != 0) { // Message not for us or malformed! - // return; - //} + return false; + } break; } case 1: { @@ -153,29 +152,41 @@ void protocolMQTTParse(MyMessage &message, char* topic, uint8_t* payload, unsign case 5: { // Sub type message.type = atoi(str); - // Add payload - if (command == C_STREAM) { - blen = 0; - uint8_t val; - while (*payload) { - val = protocolH2i(*payload++) << 4; - val += protocolH2i(*payload++); - bvalue[blen] = val; - blen++; - } - message.set(bvalue, blen); - } else { - char* ca; - ca = (char *) payload; - ca += length; - *ca = '\0'; - message.set((const char*) payload); - } + break; } } i++; } + + if (i != 6) + return false; + + message.sender = GATEWAY_ADDRESS; + message.last = GATEWAY_ADDRESS; + mSetAck(message, false); + + // Add payload + if (command == C_STREAM) { + blen = 0; + uint8_t val; + while (*payload) { + val = protocolH2i(*payload++) << 4; + val += protocolH2i(*payload++); + bvalue[blen] = val; + blen++; + } + message.set(bvalue, blen); + } else { + char* ca; + ca = (char *) payload; + ca += length; + *ca = '\0'; + message.set((const char*) payload); + } + + return true; } +#endif uint8_t protocolH2i(char c) { uint8_t i = 0; @@ -187,5 +198,3 @@ uint8_t protocolH2i(char c) { i += c - 'A' + 10; return i; } - - diff --git a/core/MySensorsCore.cpp b/core/MySensorsCore.cpp index 0be5781..959e33b 100644 --- a/core/MySensorsCore.cpp +++ b/core/MySensorsCore.cpp @@ -51,6 +51,10 @@ void _process() { transportProcess(); #endif + #if defined(LINUX_ARCH_GENERIC) + // To avoid high cpu usage + usleep(10000); // 10ms + #endif } void _infiniteLoop() { @@ -61,17 +65,17 @@ void _infiniteLoop() { #if defined (MY_LEDS_BLINKING_FEATURE) ledsProcess(); #endif + #if defined(LINUX_ARCH_GENERIC) + exit(1); + #endif } } void _begin() { - if (preHwInit) preHwInit(); - #if !defined(MY_DISABLED_SERIAL) - hwInit(); - #endif + hwInit(); debug(PSTR("MCO:BGN:INIT " MY_NODE_TYPE ",CP=" MY_CAPABILITIES ",VER=" MYSENSORS_LIBRARY_VERSION "\n")); diff --git a/core/MyTransport.cpp b/core/MyTransport.cpp index a54c34d..61925e7 100644 --- a/core/MyTransport.cpp +++ b/core/MyTransport.cpp @@ -467,7 +467,7 @@ void transportProcessMessage() { // Is message addressed to this node? if (destination == _nc.nodeId) { // prevent buffer overflow by limiting max. possible message length (5 bits=31 bytes max) to MAX_PAYLOAD (25 bytes) - mSetLength(_msg, min(mGetLength(_msg),MAX_PAYLOAD)); + mSetLength(_msg, min(mGetLength(_msg),(uint8_t)MAX_PAYLOAD)); // null terminate data _msg.data[mGetLength(_msg)] = 0x00; @@ -717,4 +717,4 @@ bool transportSendWrite(uint8_t to, MyMessage &message) { return (ok || to==BROADCAST_ADDRESS); } -// EOF MyTransport.cpp \ No newline at end of file +// EOF MyTransport.cpp diff --git a/core/MyTransportNRF24.cpp b/core/MyTransportNRF24.cpp index 6d7f407..fa72736 100644 --- a/core/MyTransportNRF24.cpp +++ b/core/MyTransportNRF24.cpp @@ -19,7 +19,13 @@ #include "MyConfig.h" #include "MyTransport.h" -#include "drivers/RF24/RF24.h" + +#ifdef RASPBERRYPI_ARCH + #include "drivers/RF24/RF24_RPi.cpp" +#else + #include "drivers/RF24/RF24.h" +#endif + #include "drivers/CircularBuffer/CircularBuffer.h" #if defined(MY_RF24_ENABLE_ENCRYPTION) diff --git a/core/MyTransportRS485.cpp b/core/MyTransportRS485.cpp index 814f721..285c1d1 100644 --- a/core/MyTransportRS485.cpp +++ b/core/MyTransportRS485.cpp @@ -57,12 +57,7 @@ #include "MyTransport.h" #include - -#if defined(ARDUINO) && ARDUINO >= 100 #include -#else -#include -#endif #include "MyTransport.h" diff --git a/drivers/Linux/Arduino.h b/drivers/Linux/Arduino.h new file mode 100644 index 0000000..7ae8162 --- /dev/null +++ b/drivers/Linux/Arduino.h @@ -0,0 +1,212 @@ +#ifndef Arduino_h +#define Arduino_h + +#include + +#ifndef LSBFIRST + #define LSBFIRST 0 +#endif + +#ifndef MSBFIRST + #define MSBFIRST 1 +#endif + +#define abs(x) ((x)>0?(x):-(x)) +#define constrain(amt,low,high) ((amt)<(low)?(low):((amt)>(high)?(high):(amt))) +#define round(x) ((x)>=0?(long)((x)+0.5):(long)((x)-0.5)) +#define radians(deg) ((deg)*DEG_TO_RAD) +#define degrees(rad) ((rad)*RAD_TO_DEG) +#define sq(x) ((x)*(x)) + +#define lowByte(w) ((uint8_t) ((w) & 0xff)) +#define highByte(w) ((uint8_t) ((w) >> 8)) + +#define bit(b) (1UL << (b)) + +typedef bool boolean; +typedef uint8_t byte; + +#ifdef __cplusplus + extern "C" { +#endif + +/** +* C++ version 0.4 char* style "itoa": +* Written by Lukás Chmela +* Released under GPLv3. +*/ +char *itoa(int value, char* result, int base) { + // check that the base if valid + if (base < 2 || base > 36) { *result = '\0'; return result; } + + char* ptr = result, *ptr1 = result, tmp_char; + int tmp_value; + + do { + tmp_value = value; + value /= base; + *ptr++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz" [35 + (tmp_value - value * base)]; + } while ( value ); + + // Apply negative sign + // Apply negative sign + if (tmp_value < 0) *ptr++ = '-'; + *ptr-- = '\0'; + while(ptr1 < ptr) { + tmp_char = *ptr; + *ptr--= *ptr1; + *ptr1++ = tmp_char; + } + return result; +} + +/** + * C++ version 0.4 char* style "itoa": + * Written by Lukás Chmela + * Released under GPLv3. + */ +char *ltoa(long value, char* result, int base) { + // check that the base if valid + if (base < 2 || base > 36) { *result = '\0'; return result; } + + char* ptr = result, *ptr1 = result, tmp_char; + long tmp_value; + + do { + tmp_value = value; + value /= base; + *ptr++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz" [35 + (tmp_value - value * base)]; + } while ( value ); + + // Apply negative sign + if (tmp_value < 0) *ptr++ = '-'; + *ptr-- = '\0'; + while(ptr1 < ptr) { + tmp_char = *ptr; + *ptr--= *ptr1; + *ptr1++ = tmp_char; + } + return result; +} + +char *ultoa(long num, char *str, int radix) +{ + unsigned long value; + char *sp = str; + char *sp2; + + value = num; + + /* Store sign at start of buffer for negative base-10 values */ + if (10 == radix && 0 > num) { + *sp++ = '-'; + value = -num; + } + + sp2 = sp; + + do { + char rem = value % radix; + value /= radix; + if (10 > rem) { + *sp++ = '0' + rem; + } else { + *sp++ = 'A' + rem - 10; + } + } while (0 < value); + + /* Mark end of string */ + *sp-- = 0; + + /* Reverse string contents (excluding sign) in place */ + while (sp2 < sp) { + char tmp = *sp2; + *sp2++ = *sp; + *sp-- = tmp; + } + + return str; +} + +/** + * Copyright (c) 2012, Peter A. Bigot + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of the software nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +char *utoa(int num, char *str, int radix) +{ + unsigned int value; + char *sp = str; + char *sp2; + + value = num; + + /* Store sign at start of buffer for negative base-10 values */ + if (10 == radix && 0 > num) { + *sp++ = '-'; + value = -num; + } + + sp2 = sp; + + do { + char rem = value % radix; + value /= radix; + if (10 > rem) { + *sp++ = '0' + rem; + } else { + *sp++ = 'A' + rem - 10; + } + } while (0 < value); + + /* Mark end of string */ + *sp-- = 0; + + /* Reverse string contents (excluding sign) in place */ + while (sp2 < sp) { + char tmp = *sp2; + *sp2++ = *sp; + *sp-- = tmp; + } + + return str; +} + +char *dtostrf(float f, int width, int decimals, char *result) +{ + sprintf(result,"%*.*f", width, decimals, f); + return result; +} + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/drivers/Linux/Client.h b/drivers/Linux/Client.h new file mode 100644 index 0000000..0169890 --- /dev/null +++ b/drivers/Linux/Client.h @@ -0,0 +1,46 @@ +/* + Client.h - Base class that provides Client + Copyright (c) 2011 Adrian McEwen. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Modified by Marcelo Aquino for MySensors use +*/ + +#ifndef client_h +#define client_h +#include "Stream.h" +#include "IPAddress.h" + +class Client : public Stream { + +public: + virtual int connect(IPAddress ip, uint16_t port) = 0; + virtual int connect(const char *host, uint16_t port) = 0; + virtual size_t write(uint8_t) = 0; + virtual size_t write(const uint8_t *buf, size_t size) = 0; + virtual int available() = 0; + virtual int read() = 0; + virtual int read(uint8_t *buf, size_t size) = 0; + virtual int peek() = 0; + virtual void flush() = 0; + virtual void stop() = 0; + virtual uint8_t connected() = 0; + virtual operator bool() = 0; +protected: + uint8_t* rawIPAddress(IPAddress& addr) { return addr.raw_address(); }; +}; + +#endif diff --git a/drivers/Linux/EthernetClient.cpp b/drivers/Linux/EthernetClient.cpp new file mode 100644 index 0000000..1777429 --- /dev/null +++ b/drivers/Linux/EthernetClient.cpp @@ -0,0 +1,253 @@ +/* +* The MySensors Arduino library handles the wireless radio link and protocol +* between your home built sensors/actuators and HA controller of choice. +* The sensors forms a self healing radio network with optional repeaters. Each +* repeater and gateway builds a routing tables in EEPROM which keeps track of the +* network topology allowing messages to be routed to nodes. +* +* Created by Marcelo Aquino +* Copyright (C) 2016 Marcelo Aquino +* Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors +* +* Documentation: http://www.mysensors.org +* Support Forum: http://forum.mysensors.org +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* version 2 as published by the Free Software Foundation. +* +* Based on Arduino ethernet library, Copyright (c) 2010 Arduino LLC. All right reserved. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "EthernetClient.h" + +EthernetClient::EthernetClient() : _sock(-1) { +} + +EthernetClient::EthernetClient(int sock) : _sock(sock) { +} + +int EthernetClient::connect(const char* host, uint16_t port) { + int sockfd; + struct addrinfo hints, *servinfo, *p; + int rv; + char s[INET6_ADDRSTRLEN]; + char port_str[6]; + + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + sprintf(port_str, "%hu", port); + if ((rv = getaddrinfo(host, port_str, &hints, &servinfo)) != 0) { + fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv)); + return -1; + } + + // loop through all the results and connect to the first we can + for (p = servinfo; p != NULL; p = p->ai_next) { + if ((sockfd = socket(p->ai_family, p->ai_socktype, + p->ai_protocol)) == -1) { + perror("socket"); + continue; + } + + if (::connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) { + close(sockfd); + perror("connect"); + continue; + } + + break; + } + + if (p == NULL) { + fprintf(stderr, "failed to connect\n"); + return -1; + } + + _sock = sockfd; + + void *addr = &(((struct sockaddr_in*)p->ai_addr)->sin_addr); + inet_ntop(p->ai_family, addr, s, sizeof s); + ETHERNETCLIENT_DEBUG("connected to %s\n", s); + + freeaddrinfo(servinfo); // all done with this structure + + return 1; +} + +int EthernetClient::connect(IPAddress ip, uint16_t port) { + return connect(ip.toString().c_str(), port); +} + +size_t EthernetClient::write(uint8_t b) { + return write(&b, 1); +} + +size_t EthernetClient::write(const uint8_t *buf, size_t size) { + int rc = 0; + int bytes = 0; + + if (_sock == -1) { + return 0; + } + + while (size > 0) { + rc = send(_sock, buf + bytes, size, MSG_NOSIGNAL | MSG_DONTWAIT); + if (rc == -1) { + perror("send"); + close(_sock); + _sock = -1; + break; + } + bytes += rc; + size -= rc; + } + + return bytes; +} + +size_t EthernetClient::write(const char *str) { + if (str == NULL) return 0; + return write((const uint8_t *)str, strlen(str)); +} +size_t EthernetClient::write(const char *buffer, size_t size) { + return write((const uint8_t *)buffer, size); +} + +int EthernetClient::available() { + int count = 0; + + if (_sock != -1) { + ioctl(_sock, FIONREAD, &count); + return count; + } + return 0; +} + +int EthernetClient::read() { + uint8_t b; + if ( recv(_sock, &b, 1, 0) > 0 ) + { + // recv worked + return b; + } else { + // No data available + return -1; + } +} + +int EthernetClient::read(uint8_t *buf, size_t size) { + return recv(_sock, buf, size, 0); +} + +int EthernetClient::peek() { + uint8_t b; + + return recv(_sock, &b, 1, MSG_PEEK | MSG_DONTWAIT); +} + +void EthernetClient::flush() { + // There isn't much we can do here + return; +} + +void EthernetClient::stop() { + if (_sock == -1) + return; + + // attempt to close the connection gracefully (send a FIN to other side) + shutdown(_sock, SHUT_RDWR); + + timeval startTime, curTime; + gettimeofday(&startTime, NULL); + + // wait up to a second for the connection to close + uint8_t s; + do { + s = status(); + if (s == ETHERNETCLIENT_W5100_CLOSED) + break; // exit the loop + usleep(1000); + gettimeofday(&curTime, NULL); + } while (((curTime.tv_sec - startTime.tv_sec) * 1000000) + (curTime.tv_usec - startTime.tv_usec) < 1000000); + + // if it hasn't closed, close it forcefully + if (s != ETHERNETCLIENT_W5100_CLOSED) + close(_sock); + + _sock = -1; +} + +uint8_t EthernetClient::connected() { + if (_sock == -1) return 0; + + if (peek() < 0) { + if (errno == EAGAIN) { + return 1; + } + return 0; + } + return 1; +} + +uint8_t EthernetClient::status() { + if (_sock == -1) return ETHERNETCLIENT_W5100_CLOSED; + + struct tcp_info tcp_info; + int tcp_info_length = sizeof(tcp_info); + + if ( getsockopt( _sock, SOL_TCP, TCP_INFO, (void *)&tcp_info, (socklen_t *)&tcp_info_length ) == 0 ) { + switch (tcp_info.tcpi_state) { + case TCP_ESTABLISHED: + return ETHERNETCLIENT_W5100_ESTABLISHED; + case TCP_SYN_SENT: + return ETHERNETCLIENT_W5100_SYNSENT; + case TCP_SYN_RECV: + return ETHERNETCLIENT_W5100_SYNRECV; + case TCP_FIN_WAIT1: + case TCP_FIN_WAIT2: + return ETHERNETCLIENT_W5100_FIN_WAIT; + case TCP_TIME_WAIT: + return TCP_TIME_WAIT; + case TCP_CLOSE: + return ETHERNETCLIENT_W5100_CLOSED; + case TCP_CLOSE_WAIT: + return ETHERNETCLIENT_W5100_CLOSING; + case TCP_LAST_ACK: + return ETHERNETCLIENT_W5100_LAST_ACK; + case TCP_LISTEN: + return ETHERNETCLIENT_W5100_LISTEN; + case TCP_CLOSING: + return ETHERNETCLIENT_W5100_CLOSING; + } + } else { + perror("getsockopt"); + } + + return ETHERNETCLIENT_W5100_CLOSED; +} + +// the next function allows us to use the client returned by +// EthernetServer::available() as the condition in an if-statement. + +EthernetClient::operator bool() { + return _sock != -1; +} + +bool EthernetClient::operator==(const EthernetClient& rhs) { + return _sock == rhs._sock && _sock != -1 && rhs._sock != -1; +} + +uint8_t EthernetClient::getSocketNumber() { + return _sock; +} diff --git a/drivers/Linux/EthernetClient.h b/drivers/Linux/EthernetClient.h new file mode 100644 index 0000000..b2ad1b1 --- /dev/null +++ b/drivers/Linux/EthernetClient.h @@ -0,0 +1,80 @@ +/* +* The MySensors Arduino library handles the wireless radio link and protocol +* between your home built sensors/actuators and HA controller of choice. +* The sensors forms a self healing radio network with optional repeaters. Each +* repeater and gateway builds a routing tables in EEPROM which keeps track of the +* network topology allowing messages to be routed to nodes. +* +* Created by Marcelo Aquino +* Copyright (C) 2016 Marcelo Aquino +* Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors +* +* Documentation: http://www.mysensors.org +* Support Forum: http://forum.mysensors.org +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* version 2 as published by the Free Software Foundation. +* +* Based on Arduino ethernet library, Copyright (c) 2010 Arduino LLC. All right reserved. +*/ + +#ifndef ethernetclient_h +#define ethernetclient_h + +#include "Client.h" +#include "IPAddress.h" + +// State codes from W5100 library +#define ETHERNETCLIENT_W5100_CLOSED 0x00 +#define ETHERNETCLIENT_W5100_LISTEN 0x14 +#define ETHERNETCLIENT_W5100_SYNSENT 0x15 +#define ETHERNETCLIENT_W5100_SYNRECV 0x16 +#define ETHERNETCLIENT_W5100_ESTABLISHED 0x17 +#define ETHERNETCLIENT_W5100_FIN_WAIT 0x18 +#define ETHERNETCLIENT_W5100_CLOSING 0x1A +#define ETHERNETCLIENT_W5100_TIME_WAIT 0x1B +#define ETHERNETCLIENT_W5100_CLOSE_WAIT 0x1C +#define ETHERNETCLIENT_W5100_LAST_ACK 0x1D + +// debug +#if defined(ETHERNETCLIENT_VERBOSE) + #define ETHERNETCLIENT_DEBUG(x,...) debug(x, ##__VA_ARGS__) +#else + #define ETHERNETCLIENT_DEBUG(x,...) +#endif + +class EthernetClient : public Client { + +public: + EthernetClient(); + EthernetClient(int sock); + + uint8_t status(); + virtual int connect(IPAddress ip, uint16_t port); + virtual int connect(const char *host, uint16_t port); + virtual size_t write(uint8_t); + virtual size_t write(const uint8_t *buf, size_t size); + size_t write(const char *str); + size_t write(const char *buffer, size_t size); + virtual int available(); + virtual int read(); + virtual int read(uint8_t *buf, size_t size); + virtual int peek(); + virtual void flush(); + virtual void stop(); + virtual uint8_t connected(); + virtual operator bool(); + virtual bool operator==(const bool value) { return bool() == value; } + virtual bool operator!=(const bool value) { return bool() != value; } + virtual bool operator==(const EthernetClient&); + virtual bool operator!=(const EthernetClient& rhs) { return !this->operator==(rhs); }; + uint8_t getSocketNumber(); + + friend class EthernetServer; + +private: + int _sock; +}; + +#endif diff --git a/drivers/Linux/EthernetServer.cpp b/drivers/Linux/EthernetServer.cpp new file mode 100644 index 0000000..506eb15 --- /dev/null +++ b/drivers/Linux/EthernetServer.cpp @@ -0,0 +1,241 @@ +/* +* The MySensors Arduino library handles the wireless radio link and protocol +* between your home built sensors/actuators and HA controller of choice. +* The sensors forms a self healing radio network with optional repeaters. Each +* repeater and gateway builds a routing tables in EEPROM which keeps track of the +* network topology allowing messages to be routed to nodes. +* +* Created by Marcelo Aquino +* Copyright (C) 2016 Marcelo Aquino +* Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors +* +* Documentation: http://www.mysensors.org +* Support Forum: http://forum.mysensors.org +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* version 2 as published by the Free Software Foundation. +* +* Based on Arduino ethernet library, Copyright (c) 2010 Arduino LLC. All right reserved. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include "EthernetServer.h" + +struct server_vars { + uint16_t port; + struct IPAddress address; + std::list *new_clients; + std::vector *clients; +}; + +static pthread_mutex_t new_clients_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t clients_mutex = PTHREAD_MUTEX_INITIALIZER; + +void *incoming_connections(void *); + +EthernetServer::EthernetServer(uint16_t port) : port(port) +{ + clients.reserve(MY_GATEWAY_MAX_CLIENTS); +} + +void EthernetServer::begin() +{ + begin(IPAddress(0,0,0,0)); +} + +void EthernetServer::begin(IPAddress address) +{ + pthread_t thread_id; + pthread_attr_t detached_attr; + + struct server_vars *vars = new struct server_vars; + vars->port = port; + vars->address = address; + vars->new_clients = &new_clients; + vars->clients = &clients; + + pthread_attr_init(&detached_attr); + pthread_attr_setdetachstate(&detached_attr, PTHREAD_CREATE_DETACHED); + pthread_create(&thread_id, &detached_attr, &incoming_connections, vars); +} + +void EthernetServer::beginPacket(IPAddress address, uint16_t port) +{ + //TODO: UDP + (void)address; + (void)port; +} + +int EthernetServer::parsePacket() +{ + //TODO: UDP + + return 0; +} + +bool EthernetServer::hasClient() +{ + bool empty; + + pthread_mutex_lock(&new_clients_mutex); + empty = new_clients.empty(); + pthread_mutex_unlock(&new_clients_mutex); + + return !empty; +} + +EthernetClient EthernetServer::available() +{ + int sock; + + pthread_mutex_lock(&new_clients_mutex); + if (new_clients.empty()) { + sock = -1; + } else { + sock = new_clients.front(); + new_clients.pop_front(); + } + pthread_mutex_unlock(&new_clients_mutex); + + return EthernetClient(sock); +} + +size_t EthernetServer::write(uint8_t b) +{ + return write(&b, 1); +} + +size_t EthernetServer::write(const uint8_t *buffer, size_t size) +{ + size_t n = 0; + size_t i = 0; + + pthread_mutex_lock(&clients_mutex); + while (i < clients.size()) { + EthernetClient client(clients[i]); + if (client.connected()) { + n += client.write(buffer, size); + i++; + } else { + clients[i] = clients.back(); + clients.pop_back(); + } + } + pthread_mutex_unlock(&clients_mutex); + + return n; +} + +size_t EthernetServer::write(const char *str) { + if (str == NULL) return 0; + return write((const uint8_t *)str, strlen(str)); +} +size_t EthernetServer::write(const char *buffer, size_t size) { + return write((const uint8_t *)buffer, size); +} + +void *incoming_connections(void* thread_arg) +{ + struct server_vars *vars = (struct server_vars*) thread_arg; + int sockfd, new_fd; + struct addrinfo hints, *servinfo, *p; + struct sockaddr_storage client_addr; + socklen_t sin_size; + int yes=1; + int rv; + char ipstr[INET_ADDRSTRLEN]; + char portstr[6]; + + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; + + sprintf(portstr, "%d", vars->port); + if ((rv = getaddrinfo(vars->address.toString().c_str(), portstr, &hints, &servinfo)) != 0) { + fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv)); + return NULL; + } + + // loop through all the results and bind to the first we can + for (p = servinfo; p != NULL; p = p->ai_next) { + if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) { + perror("socket"); + continue; + } + + if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) { + perror("setsockopt"); + freeaddrinfo(servinfo); + return NULL; + } + + if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) { + close(sockfd); + perror("bind"); + continue; + } + + break; + } + + if (p == NULL) { + fprintf(stderr, "failed to bind\n"); + freeaddrinfo(servinfo); + return NULL; + } + + if (listen(sockfd, ETHERNETSERVER_BACKLOG) == -1) { + perror("listen"); + freeaddrinfo(servinfo); + return NULL; + } + + freeaddrinfo(servinfo); + + struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr; + void *addr = &(ipv4->sin_addr); + inet_ntop(p->ai_family, addr, ipstr, sizeof ipstr); + ETHERNETSERVER_DEBUG("Listening for connections on %s:%s\n", ipstr, portstr); + + while (1) { // accept() loop + sin_size = sizeof client_addr; + new_fd = accept(sockfd, (struct sockaddr *)&client_addr, &sin_size); + if (new_fd == -1) { + perror("accept"); + continue; + } + + void *addr = &(((struct sockaddr_in*)&client_addr)->sin_addr); + inet_ntop(client_addr.ss_family, addr, ipstr, sizeof ipstr); + ETHERNETSERVER_DEBUG("New connection from %s\n", ipstr); + + pthread_mutex_lock(&new_clients_mutex); + pthread_mutex_lock(&clients_mutex); + int connected_clients = vars->new_clients->size() + vars->clients->size(); + + if (connected_clients == MY_GATEWAY_MAX_CLIENTS) { + pthread_mutex_unlock(&clients_mutex); + pthread_mutex_unlock(&new_clients_mutex); + ETHERNETSERVER_DEBUG("Max number of ethernet clients reached."); + close(new_fd); + usleep(5000000); + } else { + vars->new_clients->push_back(new_fd); + vars->clients->push_back(new_fd); + pthread_mutex_unlock(&clients_mutex); + pthread_mutex_unlock(&new_clients_mutex); + } + } + + delete vars; + + return NULL; +} diff --git a/drivers/Linux/EthernetServer.h b/drivers/Linux/EthernetServer.h new file mode 100644 index 0000000..10f1a4d --- /dev/null +++ b/drivers/Linux/EthernetServer.h @@ -0,0 +1,64 @@ +/* +* The MySensors Arduino library handles the wireless radio link and protocol +* between your home built sensors/actuators and HA controller of choice. +* The sensors forms a self healing radio network with optional repeaters. Each +* repeater and gateway builds a routing tables in EEPROM which keeps track of the +* network topology allowing messages to be routed to nodes. +* +* Created by Marcelo Aquino +* Copyright (C) 2016 Marcelo Aquino +* Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors +* +* Documentation: http://www.mysensors.org +* Support Forum: http://forum.mysensors.org +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* version 2 as published by the Free Software Foundation. +* +* Based on Arduino ethernet library, Copyright (c) 2010 Arduino LLC. All right reserved. +*/ + +#ifndef ethernetserver_h +#define ethernetserver_h + +#include +#include +#include "Server.h" +#include "EthernetClient.h" + +#ifndef ETHERNETSERVER_BACKLOG + #define ETHERNETSERVER_BACKLOG 10 +#endif + +// debug +#if defined(ETHERNETSERVER_VERBOSE) + #define ETHERNETSERVER_DEBUG(x,...) debug(x, ##__VA_ARGS__) +#else + #define ETHERNETSERVER_DEBUG(x,...) +#endif + +class EthernetClient; + +class EthernetServer : public Server { + +private: + uint16_t port; + std::list new_clients; + std::vector clients; + +public: + EthernetServer(uint16_t); + virtual void begin(); + void begin(IPAddress); + void beginPacket(IPAddress, uint16_t); + int parsePacket(); + bool hasClient(); + EthernetClient available(); + virtual size_t write(uint8_t); + virtual size_t write(const uint8_t *, size_t); + size_t write(const char *str); + size_t write(const char *buffer, size_t size); +}; + +#endif diff --git a/drivers/Linux/IPAddress.cpp b/drivers/Linux/IPAddress.cpp new file mode 100644 index 0000000..bec1728 --- /dev/null +++ b/drivers/Linux/IPAddress.cpp @@ -0,0 +1,105 @@ +/* + IPAddress.cpp - Base class that provides IPAddress + Copyright (c) 2011 Adrian McEwen. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + + Modified by Marcelo Aquino for MySensors use + */ + +#include +#include "IPAddress.h" + +IPAddress::IPAddress() { + _address.dword = 0; +} + +IPAddress::IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet) { + _address.bytes[0] = first_octet; + _address.bytes[1] = second_octet; + _address.bytes[2] = third_octet; + _address.bytes[3] = fourth_octet; +} + +IPAddress::IPAddress(uint32_t address) { + _address.dword = address; +} + +IPAddress::IPAddress(const uint8_t *address) { + memcpy(_address.bytes, address, sizeof(_address.bytes)); +} + +bool IPAddress::fromString(const char *address) { + // TODO: add support for "a", "a.b", "a.b.c" formats + + uint16_t acc = 0; // Accumulator + uint8_t dots = 0; + + while (*address) + { + char c = *address++; + if (c >= '0' && c <= '9') + { + acc = acc * 10 + (c - '0'); + if (acc > 255) { + // Value out of [0..255] range + return false; + } + } + else if (c == '.') + { + if (dots == 3) { + // Too much dots (there must be 3 dots) + return false; + } + _address.bytes[dots++] = acc; + acc = 0; + } + else + { + // Invalid char + return false; + } + } + + if (dots != 3) { + // Too few dots (there must be 3 dots) + return false; + } + _address.bytes[3] = acc; + return true; +} + +IPAddress& IPAddress::operator=(const uint8_t *address) { + memcpy(_address.bytes, address, sizeof(_address.bytes)); + return *this; +} + +IPAddress& IPAddress::operator=(uint32_t address) { + _address.dword = address; + return *this; +} + +bool IPAddress::operator==(const uint8_t* addr) const { + return memcmp(addr, _address.bytes, sizeof(_address.bytes)) == 0; +} + +std::string IPAddress::toString() +{ + char szRet[16]; + sprintf(szRet,"%u.%u.%u.%u", _address.bytes[0], _address.bytes[1], _address.bytes[2], _address.bytes[3]); + return std::string(szRet); +} diff --git a/drivers/Linux/IPAddress.h b/drivers/Linux/IPAddress.h new file mode 100644 index 0000000..2fb9c6f --- /dev/null +++ b/drivers/Linux/IPAddress.h @@ -0,0 +1,87 @@ +/* + IPAddress.h - Base class that provides IPAddress + Copyright (c) 2011 Adrian McEwen. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + + Modified by Marcelo Aquino for MySensors use + */ + +#ifndef IPAddress_h +#define IPAddress_h + +#include +#include + +// A class to make it easier to handle and pass around IP addresses + +// class IPAddress: public Printable { +class IPAddress { + private: + union { + uint8_t bytes[4]; // IPv4 address + uint32_t dword; + } _address; + + // Access the raw byte array containing the address. Because this returns a pointer + // to the internal structure rather than a copy of the address this function should only + // be used when you know that the usage of the returned uint8_t* will be transient and not + // stored. + uint8_t* raw_address() { + return _address.bytes; + } + + public: + // Constructors + IPAddress(); + IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet); + IPAddress(uint32_t address); + IPAddress(const uint8_t *address); + + bool fromString(const char *address); + bool fromString(const std::string &address) { return fromString(address.c_str()); } + + // Overloaded cast operator to allow IPAddress objects to be used where a pointer + // to a four-byte uint8_t array is expected + operator uint32_t() const { + return _address.dword; + } + bool operator==(const IPAddress& addr) const { + return _address.dword == addr._address.dword; + } + bool operator==(uint32_t addr) const { + return _address.dword == addr; + } + bool operator==(const uint8_t* addr) const; + + // Overloaded index operator to allow getting and setting individual octets of the address + uint8_t operator[](int index) const { + return _address.bytes[index]; + } + uint8_t& operator[](int index) { + return _address.bytes[index]; + } + + // Overloaded copy operators to allow initialisation of IPAddress objects from other types + IPAddress& operator=(const uint8_t *address); + IPAddress& operator=(uint32_t address); + + std::string toString(); + + friend class Client; +}; + +#endif diff --git a/drivers/Linux/Server.h b/drivers/Linux/Server.h new file mode 100644 index 0000000..d1b4046 --- /dev/null +++ b/drivers/Linux/Server.h @@ -0,0 +1,28 @@ +/* + Server.h - Base class that provides Server + Copyright (c) 2011 Adrian McEwen. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef server_h +#define server_h + +class Server { + public: + virtual void begin() =0; +}; + +#endif diff --git a/drivers/Linux/Stream.h b/drivers/Linux/Stream.h new file mode 100644 index 0000000..0241f6a --- /dev/null +++ b/drivers/Linux/Stream.h @@ -0,0 +1,15 @@ +#ifndef Stream_h +#define Stream_h + +class Stream { + public: + virtual size_t write(uint8_t) = 0; + virtual size_t write(const uint8_t *buffer, size_t size) = 0; + + virtual int available() = 0; + virtual int read() = 0; + virtual int peek() = 0; + virtual void flush() = 0; +}; + +#endif diff --git a/drivers/RF24/RF24_RPi.cpp b/drivers/RF24/RF24_RPi.cpp new file mode 100644 index 0000000..3b1a091 --- /dev/null +++ b/drivers/RF24/RF24_RPi.cpp @@ -0,0 +1,210 @@ +/** + * The MySensors Arduino library handles the wireless radio link and protocol + * between your home built sensors/actuators and HA controller of choice. + * The sensors forms a self healing radio network with optional repeaters. Each + * repeater and gateway builds a routing tables in EEPROM which keeps track of the + * network topology allowing messages to be routed to nodes. + * + * Created by Marcelo Aquino + * Copyright (C) 2016 Marcelo Aquino + * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors + * + * Documentation: http://www.mysensors.org + * Support Forum: http://forum.mysensors.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * Based on Olivier Mauti work(RF24.h), copyright (C) 2016 Olivier Mauti + */ + +#include "RF24.h" + +// pipes +#define BROADCAST_PIPE 1 +#define NODE_PIPE 2 + +// debug +#if defined(MY_DEBUG_VERBOSE_RF24) + #define RF24_DEBUG(x,...) debug(x, ##__VA_ARGS__) +#else + #define RF24_DEBUG(x,...) +#endif + +#ifdef MY_RF24_IRQ_PIN + typedef void (*RF24_receiveCallbackType)(void); + + void (* RF24_receiveCallback)(void) = NULL; + + static pthread_mutex_t rf24_mutex = PTHREAD_MUTEX_INITIALIZER; +#endif + +static uint8_t MY_RF24_BASE_ADDR[MY_RF24_ADDR_WIDTH] = { MY_RF24_BASE_RADIO_ID }; +static uint8_t MY_RF24_NODE_ADDRESS = AUTO; + +RF24 _rf24(MY_RF24_CE_PIN, MY_RF24_CS_PIN); + +static void RF24_startListening(void) { + RF24_DEBUG(PSTR("start listening\n")); + + _rf24.startListening(); +} + +static void RF24_powerDown(void) { +#ifdef MY_RF24_IRQ_PIN + pthread_mutex_lock(&rf24_mutex); +#endif + _rf24.powerDown(); +#ifdef MY_RF24_IRQ_PIN + pthread_mutex_unlock(&rf24_mutex); +#endif +} + +static bool RF24_sendMessage( uint8_t recipient, const void* buf, uint8_t len ) { +#ifdef MY_RF24_IRQ_PIN + pthread_mutex_lock(&rf24_mutex); +#endif + // Make sure radio has powered up + _rf24.powerUp(); + _rf24.stopListening(); + + RF24_DEBUG(PSTR("send message to %d, len=%d\n"), recipient,len); + + MY_RF24_BASE_ADDR[0] = recipient; + _rf24.openWritingPipe(MY_RF24_BASE_ADDR); + bool ok = _rf24.write(buf, len, recipient == BROADCAST_ADDRESS); + _rf24.startListening(); +#ifdef MY_RF24_IRQ_PIN + pthread_mutex_unlock(&rf24_mutex); +#endif + return ok; +} + +static bool RF24_isDataAvailable() { +#ifdef MY_RF24_IRQ_PIN + pthread_mutex_lock(&rf24_mutex); +#endif + uint8_t pipe_num = 255; + _rf24.available(&pipe_num); +#ifdef MY_RF24_IRQ_PIN + pthread_mutex_unlock(&rf24_mutex); +#endif + return (pipe_num <= 5); +} + +static uint8_t RF24_readMessage( void* buf) { +#ifdef MY_RF24_IRQ_PIN + pthread_mutex_lock(&rf24_mutex); +#endif + uint8_t len = _rf24.getDynamicPayloadSize(); + _rf24.read(buf, len); +#ifdef MY_RF24_IRQ_PIN + pthread_mutex_unlock(&rf24_mutex); +#endif + return len; +} + +static void RF24_setNodeAddress(uint8_t address) { +#ifdef MY_RF24_IRQ_PIN + pthread_mutex_lock(&rf24_mutex); +#endif + if (address != AUTO){ + MY_RF24_NODE_ADDRESS = address; + // enable node pipe + MY_RF24_BASE_ADDR[0] = MY_RF24_NODE_ADDRESS; + _rf24.openReadingPipe(NODE_PIPE, MY_RF24_BASE_ADDR); + // enable autoACK on node pipe + _rf24.setAutoAck(NODE_PIPE, true); + } +#ifdef MY_RF24_IRQ_PIN + pthread_mutex_unlock(&rf24_mutex); +#endif +} + +static uint8_t RF24_getNodeID(void) { + return MY_RF24_NODE_ADDRESS; +} + +bool RF24_sanityCheck(void) { + bool ok = true; +#ifdef MY_RF24_IRQ_PIN + pthread_mutex_lock(&rf24_mutex); +#endif + // detect HW defect ot interrupted SPI line, CE disconnect cannot be detected + if (_rf24.getPALevel() != MY_RF24_PA_LEVEL || _rf24.getDataRate() != MY_RF24_DATARATE || + _rf24.getChannel() != MY_RF24_CHANNEL) { + ok = false; + } +#ifdef MY_RF24_IRQ_PIN + pthread_mutex_unlock(&rf24_mutex); +#endif + return ok; +} + +#ifdef MY_RF24_IRQ_PIN +void RF24_irqHandler(void) { + if (RF24_receiveCallback) { + while (RF24_isDataAvailable()) { + RF24_receiveCallback(); + } + } else { + // clear RX interrupt + bool tx_ok,tx_fail,rx; + _rf24.whatHappened(tx_ok,tx_fail,rx); + } +} + +void RF24_registerReceiveCallback(RF24_receiveCallbackType cb) { + RF24_receiveCallback = cb; +} +#endif + +static bool RF24_initialize(void) { + // start up the radio library + _rf24.begin(); + // determine whether the hardware is an nRF24L01+ or not + if (!_rf24.isPVariant()) { + RF24_DEBUG(PSTR("radio hardware not compatible")); + return false; + } + // set CRC + _rf24.setCRCLength(RF24_CRC_16); + // set address width + _rf24.setAddressWidth(MY_RF24_ADDR_WIDTH); + // auto retransmit delay 1500us, auto retransmit count 15 + _rf24.setRetries(5, 15); + // channel + _rf24.setChannel(MY_RF24_CHANNEL); + // PA level + _rf24.setPALevel(MY_RF24_PA_LEVEL); + // data rate + _rf24.setDataRate(MY_RF24_DATARATE); + // sanity check + #if defined(MY_RF24_SANITY_CHECK) + if (!RF24_sanityCheck()) { + RF24_DEBUG(PSTR("RF24:Sanity check failed: configuration mismatch! Check wiring, replace module or non-P version\n")); + return false; + } + #endif + // enable Dynamic payload + _rf24.enableDynamicPayloads(); + // enable ACK payload + _rf24.enableAckPayload(); + // disable AA on all pipes, activate when node pipe set + _rf24.setAutoAck(false); + // all nodes listen to broadcast pipe (for FIND_PARENT_RESPONSE messages) + MY_RF24_BASE_ADDR[0] = BROADCAST_ADDRESS; + _rf24.openReadingPipe(BROADCAST_PIPE, MY_RF24_BASE_ADDR); + + //_rf24.printDetails(); + + #ifdef MY_RF24_IRQ_PIN + // Mask all interrupts except the receive interrupt + _rf24.maskIRQ(1,1,0); + + attachInterrupt(MY_RF24_IRQ_PIN, INT_EDGE_FALLING, RF24_irqHandler); + #endif + + return true; +} diff --git a/examples_RPi/MySGateway.cpp b/examples_RPi/MySGateway.cpp new file mode 100644 index 0000000..014d12a --- /dev/null +++ b/examples_RPi/MySGateway.cpp @@ -0,0 +1,93 @@ +/** + * The MySensors Arduino library handles the wireless radio link and protocol + * between your home built sensors/actuators and HA controller of choice. + * The sensors forms a self healing radio network with optional repeaters. Each + * repeater and gateway builds a routing tables in EEPROM which keeps track of the + * network topology allowing messages to be routed to nodes. + * + * Created by Marcelo Aquino + * Copyleft (c) 2016, Marcelo Aquino + * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors + * + * Documentation: http://www.mysensors.org + * Support Forum: http://forum.mysensors.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + */ + +#include +#include +#include + +// Enable debug prints to monitor +#ifndef MY_DEBUG + #define MY_DEBUG +#endif + +// Config file +#ifndef MY_LINUX_CONFIG_FILE + #define MY_LINUX_CONFIG_FILE "/etc/MySensorGateway.cfg" +#endif + +// Enables and select radio type (if attached) +#ifndef MY_RADIO_NRF24 + #define MY_RADIO_NRF24 +#endif + +#define MY_GATEWAY_RASPBERRYPI + +// How many clients should be able to connect to this gateway (default 1) +#define MY_GATEWAY_MAX_CLIENTS 2 + +#ifdef MY_GATEWAY_MQTT_CLIENT + // Set MQTT client id + #ifndef MY_MQTT_CLIENT_ID + #define MY_MQTT_CLIENT_ID "mysensors-1" + #endif + // MQTT broker ip address. + #ifndef MY_CONTROLLER_IP_ADDRESS + #define MY_CONTROLLER_IP_ADDRESS 192, 168, 178, 68 + #endif + // The MQTT broker port to to open + #ifndef MY_PORT + #define MY_PORT 1883 + #endif + + // Enable these if your MQTT broker requires usenrame/password + //#define MY_MQTT_USER "username" + //#define MY_MQTT_PASSWORD "password" + + // Set this node's subscribe and publish topic prefix + #ifndef MY_MQTT_PUBLISH_TOPIC_PREFIX + #define MY_MQTT_PUBLISH_TOPIC_PREFIX "mygateway1-out" + #endif + #ifndef MY_MQTT_SUBSCRIBE_TOPIC_PREFIX + #define MY_MQTT_SUBSCRIBE_TOPIC_PREFIX "mygateway1-in" + #endif +#else + // Enable MY_IP_ADDRESS here if you want to listen for incoming network + // connections on the specified IP address/hostname only + //#define MY_IP_ADDRESS 192,168,178,87 + + // The port to keep open on node server mode + #ifndef MY_PORT + #define MY_PORT 5003 + #endif +#endif + +#include + +using namespace std; + +void setup() { +} + +void presentation() { + // Present locally attached sensors here +} + +void loop() { + // Send locally attached sensors data here +} diff --git a/initscripts/mysgateway.systemd b/initscripts/mysgateway.systemd new file mode 100644 index 0000000..9763c66 --- /dev/null +++ b/initscripts/mysgateway.systemd @@ -0,0 +1,9 @@ +[Unit] +Description=MySensors Gateway daemon +Requires=network.target + +[Service] +ExecStart=%gateway_dir%/MySGateway + +[Install] +WantedBy=multi-user.target diff --git a/initscripts/mysgateway.sysvinit b/initscripts/mysgateway.sysvinit new file mode 100644 index 0000000..df01c6c --- /dev/null +++ b/initscripts/mysgateway.sysvinit @@ -0,0 +1,159 @@ +#! /bin/sh +### BEGIN INIT INFO +# Provides: MySGateway +# Required-Start: $remote_fs $syslog +# Required-Stop: $remote_fs $syslog +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: MySensors Gateway +# Description: This file should be used to construct scripts to be +# placed in /etc/init.d. +### END INIT INFO + +# Author: Foo Bar +# +# Please remove the "Author" lines above and replace them +# with your own name if you copy and modify this script. + +# Do NOT "set -e" + +# PATH should only include /usr/* if it runs after the mountnfs.sh script +PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/sbin:/usr/local/bin +DESC="MySensors Gateway" +NAME=MySGateway +DAEMON=%gateway_dir%/$NAME +DAEMON_ARGS="-d" +PIDFILE=/var/run/$NAME.pid +SCRIPTNAME=/etc/init.d/$NAME + +# Exit if the package is not installed +[ -x "$DAEMON" ] || exit 0 + +# Read configuration variable file if it is present +[ -r /etc/default/$NAME ] && . /etc/default/$NAME + +# Load the VERBOSE setting and other rcS variables +. /lib/init/vars.sh + +# Define LSB log_* functions. +# Depend on lsb-base (>= 3.2-14) to ensure that this file is present +# and status_of_proc is working. +. /lib/lsb/init-functions + +# +# Function that starts the daemon/service +# +do_start() +{ + # Return + # 0 if daemon has been started + # 1 if daemon was already running + # 2 if daemon could not be started + start-stop-daemon --start --quiet --exec $DAEMON --test > /dev/null \ + || return 1 + start-stop-daemon --start --quiet --exec $DAEMON -- \ + $DAEMON_ARGS \ + || return 2 + # Add code here, if necessary, that waits for the process to be ready + # to handle requests from services started subsequently which depend + # on this one. As a last resort, sleep for some time. +} + +# +# Function that stops the daemon/service +# +do_stop() +{ + # Return + # 0 if daemon has been stopped + # 1 if daemon was already stopped + # 2 if daemon could not be stopped + # other if a failure occurred + start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --name $NAME + RETVAL="$?" + [ "$RETVAL" = 2 ] && return 2 + # Wait for children to finish too if this is a daemon that forks + # and if the daemon is only ever run from this initscript. + # If the above conditions are not satisfied then add some other code + # that waits for the process to drop all resources that could be + # needed by services started subsequently. A last resort is to + # sleep for some time. + start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON + [ "$?" = 2 ] && return 2 + # Many daemons don't delete their pidfiles when they exit. + rm -f $PIDFILE + return "$RETVAL" +} + +# +# Function that sends a SIGHUP to the daemon/service +# +do_reload() { + # + # If the daemon can reload its configuration without + # restarting (for example, when it is sent a SIGHUP), + # then implement that here. + # + start-stop-daemon --stop --signal 1 --quiet --name $NAME + return 0 +} + +case "$1" in + start) + [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME" + do_start + case "$?" in + 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; + 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; + esac + ;; + stop) + [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME" + do_stop + case "$?" in + 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; + 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; + esac + ;; + status) + status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $? + ;; + #reload|force-reload) + # + # If do_reload() is not implemented then leave this commented out + # and leave 'force-reload' as an alias for 'restart'. + # + #log_daemon_msg "Reloading $DESC" "$NAME" + #do_reload + #log_end_msg $? + #;; + restart|force-reload) + # + # If the "reload" option is implemented then remove the + # 'force-reload' alias + # + log_daemon_msg "Restarting $DESC" "$NAME" + do_stop + case "$?" in + 0|1) + do_start + case "$?" in + 0) log_end_msg 0 ;; + 1) log_end_msg 1 ;; # Old process is still running + *) log_end_msg 1 ;; # Failed to start + esac + ;; + *) + # Failed to stop + log_end_msg 1 + ;; + esac + ;; + *) + #echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2 + echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2 + exit 3 + ;; +esac + +: From 43e5a7ddf96c26e66c073c8d2a2f37cf347d50c8 Mon Sep 17 00:00:00 2001 From: Konst Kolesnichenko Date: Mon, 1 Aug 2016 22:49:53 +0300 Subject: [PATCH 09/33] Fixed build error --- drivers/Linux/EthernetClient.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/Linux/EthernetClient.cpp b/drivers/Linux/EthernetClient.cpp index 1777429..c6a9389 100644 --- a/drivers/Linux/EthernetClient.cpp +++ b/drivers/Linux/EthernetClient.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include "EthernetClient.h" EthernetClient::EthernetClient() : _sock(-1) { From 899f992d112b4adcb46227deaf41498d397819b9 Mon Sep 17 00:00:00 2001 From: Marcelo Aquino Date: Mon, 8 Aug 2016 19:37:08 -0300 Subject: [PATCH 10/33] Fix Doxygen preprocessor variable * Fix build error when not using RF24 interrupt pin --- core/MyHwLinuxGeneric.h | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/core/MyHwLinuxGeneric.h b/core/MyHwLinuxGeneric.h index 32e8406..cb01c78 100644 --- a/core/MyHwLinuxGeneric.h +++ b/core/MyHwLinuxGeneric.h @@ -54,19 +54,23 @@ static __inline__ void __hwLock() { } #endif -#if defined(__DOXYGEN__) +#if defined(DOXYGEN) #define ATOMIC_BLOCK_CLEANUP #elif defined(MY_RF24_IRQ_PIN) #define ATOMIC_BLOCK_CLEANUP uint8_t __atomic_loop \ __attribute__((__cleanup__( __hwUnlock ))) = 1 -#endif /* __DOXYGEN__ */ +#else + #define ATOMIC_BLOCK_CLEANUP +#endif /* DOXYGEN */ -#if defined(__DOXYGEN__) +#if defined(DOXYGEN) #define ATOMIC_BLOCK #elif defined(MY_RF24_IRQ_PIN) #define ATOMIC_BLOCK for ( ATOMIC_BLOCK_CLEANUP, __hwLock(); \ __atomic_loop ; __atomic_loop = 0 ) -#endif /* __DOXYGEN__ */ +#else + #define ATOMIC_BLOCK +#endif /* DOXYGEN */ #ifndef DOXYGEN #define MY_CRITICAL_SECTION ATOMIC_BLOCK From 9132e7a42ad53fcf47bd43336bf633c36610bf1a Mon Sep 17 00:00:00 2001 From: Marcelo Aquino Date: Fri, 12 Aug 2016 12:42:49 -0300 Subject: [PATCH 11/33] Exit immediately when receiving a signal --- core/MyMainLinux.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/core/MyMainLinux.cpp b/core/MyMainLinux.cpp index 5ecab96..5798cec 100644 --- a/core/MyMainLinux.cpp +++ b/core/MyMainLinux.cpp @@ -1,18 +1,15 @@ // Initialize library and handle sketch functions like we want to #include -#include +#include +#include #include "MySensorsCore.h" -volatile static int running = 1; - /* * handler for SIGINT signal */ void handle_sigint(int sig) { - running = 0; - if (sig == SIGINT) { std::cout << "Received SIGINT\n" << std::endl; } else if (sig == SIGTERM) { @@ -22,6 +19,8 @@ void handle_sigint(int sig) #ifdef MY_RF24_IRQ_PIN detachInterrupt(MY_RF24_IRQ_PIN); #endif + + exit(0); } int main(void) { @@ -31,7 +30,7 @@ int main(void) { _begin(); // Startup MySensors library - while (running) { + for (;;) { _process(); // Process incoming data if (loop) loop(); // Call sketch loop } From c4b2a809aa7289fb4b8927a470245753208f5f8e Mon Sep 17 00:00:00 2001 From: Marcelo Aquino Date: Fri, 12 Aug 2016 12:53:14 -0300 Subject: [PATCH 12/33] Move init system detection to configure --- Makefile | 16 ++++++++-------- configure | 22 +++++++++++++++++----- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/Makefile b/Makefile index 825d83d..3f74ade 100644 --- a/Makefile +++ b/Makefile @@ -53,11 +53,11 @@ install-gateway: @install -m 0755 $(GATEWAY) $(GATEWAY_DIR) install-initscripts: - if [[ `systemctl` =~ -\.mount ]]; then \ - install -m0644 initscripts/mysgateway.systemd /etc/systemd/system/mysgateway.service && \ - sed -i -e "s|%gateway_dir%|${GATEWAY_DIR}|g" /etc/systemd/system/mysgateway.service && \ - systemctl daemon-reload; \ - elif [[ -f /etc/init.d/cron && ! -h /etc/init.d/cron ]]; then \ - install -m0755 initscripts/mysgateway.sysvinit /etc/init.d/mysgateway && \ - sed -i -e "s|%gateway_dir%|${GATEWAY_DIR}|g" /etc/init.d/mysgateway; \ - fi +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 +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 +endif diff --git a/configure b/configure index ad46759..d686549 100755 --- a/configure +++ b/configure @@ -45,15 +45,17 @@ MySensors options: --my-controller-ip-address= Controller or MQTT broker ip. Use commas instead of points. Example: 127,0,0,1 - --my-port Controller or MQTT broker port. + --my-port= Controller or MQTT broker port. --my-mqtt-client-id= MQTT client id. --my-mqtt-publish-topic-prefix= MQTT publish topic prefix. --my-mqtt-subscribe-topic-prefix= MQTT subscribe topic prefix. - --my-rf24-irq-pin Pin number connected to nRF24L01 IRQ pin. - --my-rx-message-buffer-size Buffer size for incoming messages when using rf24 interrupts. - Default set to 20. + --my-rf24-irq-pin= Pin number connected to nRF24L01 IRQ pin. + For RaspberryPi use BCM numbering. + --my-rx-message-buffer-size= + Buffer size for incoming messages when using rf24 interrupts. + Default is 20. EOF } @@ -188,7 +190,7 @@ function gcc_cpu_flags { echo ${flags} } -params="OS SOC CPUFLAGS CXXFLAGS LDFLAGS PREFIX CXX RF24H_LIB_DIR GATEWAY_DIR" +params="OS SOC CPUFLAGS CXXFLAGS LDFLAGS PREFIX CXX RF24H_LIB_DIR GATEWAY_DIR INIT_SYSTEM" for opt do if [ "$opt" = "-h" ] || [ "$opt" = "--help" ]; then @@ -305,6 +307,16 @@ fi LDFLAGS="-pthread -lrf24-bcm $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 From 768b70e882683addd3ba8c8fc5fe1ea980bdd19c Mon Sep 17 00:00:00 2001 From: Marcelo Aquino Date: Fri, 12 Aug 2016 15:10:19 -0300 Subject: [PATCH 13/33] Fix conflicts with arduino macros and stdlib --- core/MyHwLinuxGeneric.cpp | 1 + core/MyHwLinuxGeneric.h | 9 +++------ core/MyMessage.cpp | 10 +++------- drivers/Linux/Arduino.h | 35 +++++++++++++++++++++++++---------- examples_RPi/MySGateway.cpp | 2 -- 5 files changed, 32 insertions(+), 25 deletions(-) diff --git a/core/MyHwLinuxGeneric.cpp b/core/MyHwLinuxGeneric.cpp index 272fe29..e288bd6 100644 --- a/core/MyHwLinuxGeneric.cpp +++ b/core/MyHwLinuxGeneric.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include diff --git a/core/MyHwLinuxGeneric.h b/core/MyHwLinuxGeneric.h index cb01c78..c44d912 100644 --- a/core/MyHwLinuxGeneric.h +++ b/core/MyHwLinuxGeneric.h @@ -21,8 +21,7 @@ #define MyHwLinuxGeneric_h #include "MyHw.h" -#include -#include +#include #include #define MY_SERIALDEVICE Serial @@ -32,8 +31,6 @@ #define hwWatchdogReset() #define hwReboot() -using namespace std; - void hwInit(); void hwReadConfigBlock(void* buf, void* adr, size_t length); void hwWriteConfigBlock(void* buf, void* adr, size_t length); @@ -73,7 +70,7 @@ static __inline__ void __hwLock() { #endif /* DOXYGEN */ #ifndef DOXYGEN - #define MY_CRITICAL_SECTION ATOMIC_BLOCK -#endif /* DOXYGEN */ + #define MY_CRITICAL_SECTION ATOMIC_BLOCK +#endif /* DOXYGEN */ #endif diff --git a/core/MyMessage.cpp b/core/MyMessage.cpp index fc945ed..d95977e 100644 --- a/core/MyMessage.cpp +++ b/core/MyMessage.cpp @@ -23,10 +23,6 @@ #include #include -#ifndef ARDUINO - #define min(a,b) ((a)<(b)?(a):(b)) -#endif - MyMessage::MyMessage() { clear(); @@ -121,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); } @@ -216,12 +212,12 @@ MyMessage& MyMessage::setDestination(uint8_t _destination) { MyMessage& MyMessage::set(void* value, uint8_t length) { miSetPayloadType(P_CUSTOM); miSetLength(length); - memcpy(data, value, min(length, MAX_PAYLOAD)); + memcpy(data, value, min(length, (uint8_t)MAX_PAYLOAD)); return *this; } 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) { diff --git a/drivers/Linux/Arduino.h b/drivers/Linux/Arduino.h index 7ae8162..e352dd1 100644 --- a/drivers/Linux/Arduino.h +++ b/drivers/Linux/Arduino.h @@ -3,17 +3,31 @@ #include -#ifndef LSBFIRST - #define LSBFIRST 0 +#ifdef __cplusplus + #include + + using namespace std; + + extern "C" { #endif -#ifndef MSBFIRST - #define MSBFIRST 1 +void yield(void); + +#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 + +#ifndef __cplusplus + #define min(a,b) ((a)<(b)?(a):(b)) + #define max(a,b) ((a)>(b)?(a):(b)) + #define abs(x) ((x)>0?(x):-(x)) + #define round(x) ((x)>=0?(long)((x)+0.5):(long)((x)-0.5)) #endif -#define abs(x) ((x)>0?(x):-(x)) #define constrain(amt,low,high) ((amt)<(low)?(low):((amt)>(high)?(high):(amt))) -#define round(x) ((x)>=0?(long)((x)+0.5):(long)((x)-0.5)) #define radians(deg) ((deg)*DEG_TO_RAD) #define degrees(rad) ((rad)*RAD_TO_DEG) #define sq(x) ((x)*(x)) @@ -21,15 +35,16 @@ #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)) typedef bool boolean; typedef uint8_t byte; -#ifdef __cplusplus - extern "C" { -#endif - /** * C++ version 0.4 char* style "itoa": * Written by Lukás Chmela diff --git a/examples_RPi/MySGateway.cpp b/examples_RPi/MySGateway.cpp index 014d12a..dc61f85 100644 --- a/examples_RPi/MySGateway.cpp +++ b/examples_RPi/MySGateway.cpp @@ -79,8 +79,6 @@ #include -using namespace std; - void setup() { } From dad6732f3801960a6c3d43b0b509be47ce1ba3bb Mon Sep 17 00:00:00 2001 From: Marcelo Aquino Date: Sat, 13 Aug 2016 04:45:08 -0300 Subject: [PATCH 14/33] Include .h instead of .cpp when possible * Move implementation code from Arduino.h --- Makefile | 2 +- MySensors.h | 12 +- core/MyGatewayTransportEthernet.cpp | 2 + drivers/Linux/Arduino.h | 180 +--------------------------- drivers/Linux/EthernetClient.cpp | 41 +++---- drivers/Linux/EthernetClient.h | 44 +++---- drivers/Linux/EthernetServer.cpp | 48 ++++---- drivers/Linux/EthernetServer.h | 49 ++++---- drivers/Linux/IPAddress.cpp | 39 +++--- drivers/Linux/IPAddress.h | 38 +++--- drivers/Linux/utility.cpp | 177 +++++++++++++++++++++++++++ 11 files changed, 327 insertions(+), 305 deletions(-) create mode 100644 drivers/Linux/utility.cpp diff --git a/Makefile b/Makefile index 3f74ade..b5ba3a8 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ CONFIG_FILE=Makefile.inc include $(CONFIG_FILE) GATEWAY=examples_RPi/MySGateway -GATEWAY_SOURCES=examples_RPi/MySGateway.cpp +GATEWAY_SOURCES=$(wildcard drivers/Linux/*.cpp) examples_RPi/MySGateway.cpp GATEWAY_OBJECTS=$(patsubst %.cpp,%.o,$(GATEWAY_SOURCES)) DEPS+=$(patsubst %.cpp,%.d,$(GATEWAY_SOURCES)) diff --git a/MySensors.h b/MySensors.h index 05d145f..b2f6890 100644 --- a/MySensors.h +++ b/MySensors.h @@ -222,9 +222,9 @@ #include "core/MyProtocolMySensors.cpp" #if defined(MY_GATEWAY_LINUX) - #include "drivers/Linux/EthernetClient.cpp" - #include "drivers/Linux/EthernetServer.cpp" - #include "drivers/Linux/IPAddress.cpp" + #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" @@ -250,9 +250,9 @@ #include "core/MyGatewayTransportEthernet.cpp" #elif defined(MY_GATEWAY_LINUX) // GATEWAY - Generic Linux (RaspberryPi, BBB, ...) - #include "drivers/Linux/EthernetClient.cpp" - #include "drivers/Linux/EthernetServer.cpp" - #include "drivers/Linux/IPAddress.cpp" + #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 diff --git a/core/MyGatewayTransportEthernet.cpp b/core/MyGatewayTransportEthernet.cpp index f974593..ee8b7a8 100644 --- a/core/MyGatewayTransportEthernet.cpp +++ b/core/MyGatewayTransportEthernet.cpp @@ -56,6 +56,8 @@ typedef struct #if defined(MY_USE_UDP) EthernetUDP _ethernetServer; +#elif defined(MY_GATEWAY_LINUX) + EthernetServer _ethernetServer(_ethernetGatewayPort, MY_GATEWAY_MAX_CLIENTS); #else EthernetServer _ethernetServer(_ethernetGatewayPort); #endif diff --git a/drivers/Linux/Arduino.h b/drivers/Linux/Arduino.h index e352dd1..39f0f6e 100644 --- a/drivers/Linux/Arduino.h +++ b/drivers/Linux/Arduino.h @@ -2,6 +2,7 @@ #define Arduino_h #include +#include #ifdef __cplusplus #include @@ -45,180 +46,11 @@ void yield(void); typedef bool boolean; typedef uint8_t byte; -/** -* C++ version 0.4 char* style "itoa": -* Written by Lukás Chmela -* Released under GPLv3. -*/ -char *itoa(int value, char* result, int base) { - // check that the base if valid - if (base < 2 || base > 36) { *result = '\0'; return result; } - - char* ptr = result, *ptr1 = result, tmp_char; - int tmp_value; - - do { - tmp_value = value; - value /= base; - *ptr++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz" [35 + (tmp_value - value * base)]; - } while ( value ); - - // Apply negative sign - // Apply negative sign - if (tmp_value < 0) *ptr++ = '-'; - *ptr-- = '\0'; - while(ptr1 < ptr) { - tmp_char = *ptr; - *ptr--= *ptr1; - *ptr1++ = tmp_char; - } - return result; -} - -/** - * C++ version 0.4 char* style "itoa": - * Written by Lukás Chmela - * Released under GPLv3. - */ -char *ltoa(long value, char* result, int base) { - // check that the base if valid - if (base < 2 || base > 36) { *result = '\0'; return result; } - - char* ptr = result, *ptr1 = result, tmp_char; - long tmp_value; - - do { - tmp_value = value; - value /= base; - *ptr++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz" [35 + (tmp_value - value * base)]; - } while ( value ); - - // Apply negative sign - if (tmp_value < 0) *ptr++ = '-'; - *ptr-- = '\0'; - while(ptr1 < ptr) { - tmp_char = *ptr; - *ptr--= *ptr1; - *ptr1++ = tmp_char; - } - return result; -} - -char *ultoa(long num, char *str, int radix) -{ - unsigned long value; - char *sp = str; - char *sp2; - - value = num; - - /* Store sign at start of buffer for negative base-10 values */ - if (10 == radix && 0 > num) { - *sp++ = '-'; - value = -num; - } - - sp2 = sp; - - do { - char rem = value % radix; - value /= radix; - if (10 > rem) { - *sp++ = '0' + rem; - } else { - *sp++ = 'A' + rem - 10; - } - } while (0 < value); - - /* Mark end of string */ - *sp-- = 0; - - /* Reverse string contents (excluding sign) in place */ - while (sp2 < sp) { - char tmp = *sp2; - *sp2++ = *sp; - *sp-- = tmp; - } - - return str; -} - -/** - * Copyright (c) 2012, Peter A. Bigot - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * * Neither the name of the software nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -char *utoa(int num, char *str, int radix) -{ - unsigned int value; - char *sp = str; - char *sp2; - - value = num; - - /* Store sign at start of buffer for negative base-10 values */ - if (10 == radix && 0 > num) { - *sp++ = '-'; - value = -num; - } - - sp2 = sp; - - do { - char rem = value % radix; - value /= radix; - if (10 > rem) { - *sp++ = '0' + rem; - } else { - *sp++ = 'A' + rem - 10; - } - } while (0 < value); - - /* Mark end of string */ - *sp-- = 0; - - /* Reverse string contents (excluding sign) in place */ - while (sp2 < sp) { - char tmp = *sp2; - *sp2++ = *sp; - *sp-- = tmp; - } - - return str; -} - -char *dtostrf(float f, int width, int decimals, char *result) -{ - sprintf(result,"%*.*f", width, decimals, f); - return result; -} +char *itoa(int value, char* result, int base); +char *ltoa(long value, char* result, int base); +char *ultoa(long num, char *str, int radix); +char *utoa(int num, char *str, int radix); +char *dtostrf(float f, int width, int decimals, char *result); #ifdef __cplusplus } diff --git a/drivers/Linux/EthernetClient.cpp b/drivers/Linux/EthernetClient.cpp index c6a9389..5e80848 100644 --- a/drivers/Linux/EthernetClient.cpp +++ b/drivers/Linux/EthernetClient.cpp @@ -1,24 +1,25 @@ /* -* The MySensors Arduino library handles the wireless radio link and protocol -* between your home built sensors/actuators and HA controller of choice. -* The sensors forms a self healing radio network with optional repeaters. Each -* repeater and gateway builds a routing tables in EEPROM which keeps track of the -* network topology allowing messages to be routed to nodes. -* -* Created by Marcelo Aquino -* Copyright (C) 2016 Marcelo Aquino -* Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors -* -* Documentation: http://www.mysensors.org -* Support Forum: http://forum.mysensors.org -* -* This program is free software; you can redistribute it and/or -* modify it under the terms of the GNU General Public License -* version 2 as published by the Free Software Foundation. -* -* Based on Arduino ethernet library, Copyright (c) 2010 Arduino LLC. All right reserved. -*/ + * The MySensors Arduino library handles the wireless radio link and protocol + * between your home built sensors/actuators and HA controller of choice. + * The sensors forms a self healing radio network with optional repeaters. Each + * repeater and gateway builds a routing tables in EEPROM which keeps track of the + * network topology allowing messages to be routed to nodes. + * + * Created by Marcelo Aquino + * Copyright (C) 2016 Marcelo Aquino + * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors + * + * Documentation: http://www.mysensors.org + * Support Forum: http://forum.mysensors.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * Based on Arduino ethernet library, Copyright (c) 2010 Arduino LLC. All right reserved. + */ +#include #include #include #include @@ -249,6 +250,6 @@ bool EthernetClient::operator==(const EthernetClient& rhs) { return _sock == rhs._sock && _sock != -1 && rhs._sock != -1; } -uint8_t EthernetClient::getSocketNumber() { +int EthernetClient::getSocketNumber() { return _sock; } diff --git a/drivers/Linux/EthernetClient.h b/drivers/Linux/EthernetClient.h index b2ad1b1..0627436 100644 --- a/drivers/Linux/EthernetClient.h +++ b/drivers/Linux/EthernetClient.h @@ -1,26 +1,26 @@ /* -* The MySensors Arduino library handles the wireless radio link and protocol -* between your home built sensors/actuators and HA controller of choice. -* The sensors forms a self healing radio network with optional repeaters. Each -* repeater and gateway builds a routing tables in EEPROM which keeps track of the -* network topology allowing messages to be routed to nodes. -* -* Created by Marcelo Aquino -* Copyright (C) 2016 Marcelo Aquino -* Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors -* -* Documentation: http://www.mysensors.org -* Support Forum: http://forum.mysensors.org -* -* This program is free software; you can redistribute it and/or -* modify it under the terms of the GNU General Public License -* version 2 as published by the Free Software Foundation. -* -* Based on Arduino ethernet library, Copyright (c) 2010 Arduino LLC. All right reserved. -*/ + * The MySensors Arduino library handles the wireless radio link and protocol + * between your home built sensors/actuators and HA controller of choice. + * The sensors forms a self healing radio network with optional repeaters. Each + * repeater and gateway builds a routing tables in EEPROM which keeps track of the + * network topology allowing messages to be routed to nodes. + * + * Created by Marcelo Aquino + * Copyright (C) 2016 Marcelo Aquino + * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors + * + * Documentation: http://www.mysensors.org + * Support Forum: http://forum.mysensors.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * Based on Arduino ethernet library, Copyright (c) 2010 Arduino LLC. All right reserved. + */ -#ifndef ethernetclient_h -#define ethernetclient_h +#ifndef EthernetClient_h +#define EthernetClient_h #include "Client.h" #include "IPAddress.h" @@ -69,7 +69,7 @@ public: virtual bool operator!=(const bool value) { return bool() != value; } virtual bool operator==(const EthernetClient&); virtual bool operator!=(const EthernetClient& rhs) { return !this->operator==(rhs); }; - uint8_t getSocketNumber(); + int getSocketNumber(); friend class EthernetServer; diff --git a/drivers/Linux/EthernetServer.cpp b/drivers/Linux/EthernetServer.cpp index 506eb15..be82704 100644 --- a/drivers/Linux/EthernetServer.cpp +++ b/drivers/Linux/EthernetServer.cpp @@ -1,24 +1,25 @@ /* -* The MySensors Arduino library handles the wireless radio link and protocol -* between your home built sensors/actuators and HA controller of choice. -* The sensors forms a self healing radio network with optional repeaters. Each -* repeater and gateway builds a routing tables in EEPROM which keeps track of the -* network topology allowing messages to be routed to nodes. -* -* Created by Marcelo Aquino -* Copyright (C) 2016 Marcelo Aquino -* Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors -* -* Documentation: http://www.mysensors.org -* Support Forum: http://forum.mysensors.org -* -* This program is free software; you can redistribute it and/or -* modify it under the terms of the GNU General Public License -* version 2 as published by the Free Software Foundation. -* -* Based on Arduino ethernet library, Copyright (c) 2010 Arduino LLC. All right reserved. -*/ + * The MySensors Arduino library handles the wireless radio link and protocol + * between your home built sensors/actuators and HA controller of choice. + * The sensors forms a self healing radio network with optional repeaters. Each + * repeater and gateway builds a routing tables in EEPROM which keeps track of the + * network topology allowing messages to be routed to nodes. + * + * Created by Marcelo Aquino + * Copyright (C) 2016 Marcelo Aquino + * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors + * + * Documentation: http://www.mysensors.org + * Support Forum: http://forum.mysensors.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * Based on Arduino ethernet library, Copyright (c) 2010 Arduino LLC. All right reserved. + */ +#include #include #include #include @@ -33,6 +34,7 @@ struct server_vars { struct IPAddress address; std::list *new_clients; std::vector *clients; + uint16_t max_clients; }; static pthread_mutex_t new_clients_mutex = PTHREAD_MUTEX_INITIALIZER; @@ -40,9 +42,9 @@ static pthread_mutex_t clients_mutex = PTHREAD_MUTEX_INITIALIZER; void *incoming_connections(void *); -EthernetServer::EthernetServer(uint16_t port) : port(port) +EthernetServer::EthernetServer(uint16_t port, uint16_t max_clients) : port(port), max_clients(max_clients) { - clients.reserve(MY_GATEWAY_MAX_CLIENTS); + clients.reserve(max_clients); } void EthernetServer::begin() @@ -60,6 +62,7 @@ void EthernetServer::begin(IPAddress address) vars->address = address; vars->new_clients = &new_clients; vars->clients = &clients; + vars->max_clients = max_clients; pthread_attr_init(&detached_attr); pthread_attr_setdetachstate(&detached_attr, PTHREAD_CREATE_DETACHED); @@ -137,6 +140,7 @@ 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); } @@ -221,7 +225,7 @@ void *incoming_connections(void* thread_arg) pthread_mutex_lock(&clients_mutex); int connected_clients = vars->new_clients->size() + vars->clients->size(); - if (connected_clients == MY_GATEWAY_MAX_CLIENTS) { + if (connected_clients == vars->max_clients) { pthread_mutex_unlock(&clients_mutex); pthread_mutex_unlock(&new_clients_mutex); ETHERNETSERVER_DEBUG("Max number of ethernet clients reached."); diff --git a/drivers/Linux/EthernetServer.h b/drivers/Linux/EthernetServer.h index 10f1a4d..2127829 100644 --- a/drivers/Linux/EthernetServer.h +++ b/drivers/Linux/EthernetServer.h @@ -1,26 +1,26 @@ /* -* The MySensors Arduino library handles the wireless radio link and protocol -* between your home built sensors/actuators and HA controller of choice. -* The sensors forms a self healing radio network with optional repeaters. Each -* repeater and gateway builds a routing tables in EEPROM which keeps track of the -* network topology allowing messages to be routed to nodes. -* -* Created by Marcelo Aquino -* Copyright (C) 2016 Marcelo Aquino -* Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors -* -* Documentation: http://www.mysensors.org -* Support Forum: http://forum.mysensors.org -* -* This program is free software; you can redistribute it and/or -* modify it under the terms of the GNU General Public License -* version 2 as published by the Free Software Foundation. -* -* Based on Arduino ethernet library, Copyright (c) 2010 Arduino LLC. All right reserved. -*/ + * The MySensors Arduino library handles the wireless radio link and protocol + * between your home built sensors/actuators and HA controller of choice. + * The sensors forms a self healing radio network with optional repeaters. Each + * repeater and gateway builds a routing tables in EEPROM which keeps track of the + * network topology allowing messages to be routed to nodes. + * + * Created by Marcelo Aquino + * Copyright (C) 2016 Marcelo Aquino + * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors + * + * Documentation: http://www.mysensors.org + * Support Forum: http://forum.mysensors.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * Based on Arduino ethernet library, Copyright (c) 2010 Arduino LLC. All right reserved. + */ -#ifndef ethernetserver_h -#define ethernetserver_h +#ifndef EthernetServer_h +#define EthernetServer_h #include #include @@ -31,6 +31,10 @@ #define ETHERNETSERVER_BACKLOG 10 #endif +#ifndef ETHERNETSERVER_MAX_CLIENTS + #define ETHERNETSERVER_MAX_CLIENTS ETHERNETSERVER_BACKLOG +#endif + // debug #if defined(ETHERNETSERVER_VERBOSE) #define ETHERNETSERVER_DEBUG(x,...) debug(x, ##__VA_ARGS__) @@ -46,9 +50,10 @@ private: uint16_t port; std::list new_clients; std::vector clients; + uint16_t max_clients; public: - EthernetServer(uint16_t); + EthernetServer(uint16_t port, uint16_t max_clients = ETHERNETSERVER_MAX_CLIENTS); virtual void begin(); void begin(IPAddress); void beginPacket(IPAddress, uint16_t); diff --git a/drivers/Linux/IPAddress.cpp b/drivers/Linux/IPAddress.cpp index bec1728..dd67b59 100644 --- a/drivers/Linux/IPAddress.cpp +++ b/drivers/Linux/IPAddress.cpp @@ -1,25 +1,26 @@ /* - IPAddress.cpp - Base class that provides IPAddress - Copyright (c) 2011 Adrian McEwen. All right reserved. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - - - Modified by Marcelo Aquino for MySensors use + * IPAddress.cpp - Base class that provides IPAddress + * Copyright (c) 2011 Adrian McEwen. All right reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * + * Modified by Marcelo Aquino for MySensors use */ +#include #include #include "IPAddress.h" diff --git a/drivers/Linux/IPAddress.h b/drivers/Linux/IPAddress.h index 2fb9c6f..f4b05c0 100644 --- a/drivers/Linux/IPAddress.h +++ b/drivers/Linux/IPAddress.h @@ -1,23 +1,23 @@ /* - IPAddress.h - Base class that provides IPAddress - Copyright (c) 2011 Adrian McEwen. All right reserved. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - - - Modified by Marcelo Aquino for MySensors use + * IPAddress.h - Base class that provides IPAddress + * Copyright (c) 2011 Adrian McEwen. All right reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * + * Modified by Marcelo Aquino for MySensors use */ #ifndef IPAddress_h diff --git a/drivers/Linux/utility.cpp b/drivers/Linux/utility.cpp new file mode 100644 index 0000000..aa36905 --- /dev/null +++ b/drivers/Linux/utility.cpp @@ -0,0 +1,177 @@ +#include +#include "Arduino.h" + +/** +* C++ version 0.4 char* style "itoa": +* Written by Lukás Chmela +* Released under GPLv3. +*/ +char *itoa(int value, char* result, int base) { + // check that the base if valid + if (base < 2 || base > 36) { *result = '\0'; return result; } + + char* ptr = result, *ptr1 = result, tmp_char; + int tmp_value; + + do { + tmp_value = value; + value /= base; + *ptr++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz" [35 + (tmp_value - value * base)]; + } while ( value ); + + // Apply negative sign + // Apply negative sign + if (tmp_value < 0) *ptr++ = '-'; + *ptr-- = '\0'; + while(ptr1 < ptr) { + tmp_char = *ptr; + *ptr--= *ptr1; + *ptr1++ = tmp_char; + } + return result; +} + +/** + * C++ version 0.4 char* style "itoa": + * Written by Lukás Chmela + * Released under GPLv3. + */ +char *ltoa(long value, char* result, int base) { + // check that the base if valid + if (base < 2 || base > 36) { *result = '\0'; return result; } + + char* ptr = result, *ptr1 = result, tmp_char; + long tmp_value; + + do { + tmp_value = value; + value /= base; + *ptr++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz" [35 + (tmp_value - value * base)]; + } while ( value ); + + // Apply negative sign + if (tmp_value < 0) *ptr++ = '-'; + *ptr-- = '\0'; + while(ptr1 < ptr) { + tmp_char = *ptr; + *ptr--= *ptr1; + *ptr1++ = tmp_char; + } + return result; +} + +char *ultoa(long num, char *str, int radix) +{ + unsigned long value; + char *sp = str; + char *sp2; + + value = num; + + /* Store sign at start of buffer for negative base-10 values */ + if (10 == radix && 0 > num) { + *sp++ = '-'; + value = -num; + } + + sp2 = sp; + + do { + char rem = value % radix; + value /= radix; + if (10 > rem) { + *sp++ = '0' + rem; + } else { + *sp++ = 'A' + rem - 10; + } + } while (0 < value); + + /* Mark end of string */ + *sp-- = 0; + + /* Reverse string contents (excluding sign) in place */ + while (sp2 < sp) { + char tmp = *sp2; + *sp2++ = *sp; + *sp-- = tmp; + } + + return str; +} + +/** + * Copyright (c) 2012, Peter A. Bigot + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of the software nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +char *utoa(int num, char *str, int radix) +{ + unsigned int value; + char *sp = str; + char *sp2; + + value = num; + + /* Store sign at start of buffer for negative base-10 values */ + if (10 == radix && 0 > num) { + *sp++ = '-'; + value = -num; + } + + sp2 = sp; + + do { + char rem = value % radix; + value /= radix; + if (10 > rem) { + *sp++ = '0' + rem; + } else { + *sp++ = 'A' + rem - 10; + } + } while (0 < value); + + /* Mark end of string */ + *sp-- = 0; + + /* Reverse string contents (excluding sign) in place */ + while (sp2 < sp) { + char tmp = *sp2; + *sp2++ = *sp; + *sp-- = tmp; + } + + return str; +} + +char *dtostrf(float f, int width, int decimals, char *result) +{ + sprintf(result,"%*.*f", width, decimals, f); + return result; +} From 490037d2851002ad0ce00dafcdc872fcd1a6d086 Mon Sep 17 00:00:00 2001 From: Marcelo Aquino Date: Mon, 15 Aug 2016 12:13:18 -0300 Subject: [PATCH 15/33] Use non-blocking socket for EthernetServer * Non-blocking socket To simplify the class design and better mimics the Arduino version * Doxygenize EthernetServer class --- drivers/Linux/EthernetServer.cpp | 255 ++++++++++++++----------------- drivers/Linux/EthernetServer.h | 94 +++++++++--- drivers/Linux/Server.h | 2 + 3 files changed, 190 insertions(+), 161 deletions(-) diff --git a/drivers/Linux/EthernetServer.cpp b/drivers/Linux/EthernetServer.cpp index be82704..0537bb6 100644 --- a/drivers/Linux/EthernetServer.cpp +++ b/drivers/Linux/EthernetServer.cpp @@ -23,25 +23,14 @@ #include #include #include -#include #include #include #include +#include +#include +#include "EthernetClient.h" #include "EthernetServer.h" -struct server_vars { - uint16_t port; - struct IPAddress address; - std::list *new_clients; - std::vector *clients; - uint16_t max_clients; -}; - -static pthread_mutex_t new_clients_mutex = PTHREAD_MUTEX_INITIALIZER; -static pthread_mutex_t clients_mutex = PTHREAD_MUTEX_INITIALIZER; - -void *incoming_connections(void *); - EthernetServer::EthernetServer(uint16_t port, uint16_t max_clients) : port(port), max_clients(max_clients) { clients.reserve(max_clients); @@ -54,104 +43,7 @@ void EthernetServer::begin() void EthernetServer::begin(IPAddress address) { - pthread_t thread_id; - pthread_attr_t detached_attr; - - struct server_vars *vars = new struct server_vars; - vars->port = port; - vars->address = address; - vars->new_clients = &new_clients; - vars->clients = &clients; - vars->max_clients = max_clients; - - pthread_attr_init(&detached_attr); - pthread_attr_setdetachstate(&detached_attr, PTHREAD_CREATE_DETACHED); - pthread_create(&thread_id, &detached_attr, &incoming_connections, vars); -} - -void EthernetServer::beginPacket(IPAddress address, uint16_t port) -{ - //TODO: UDP - (void)address; - (void)port; -} - -int EthernetServer::parsePacket() -{ - //TODO: UDP - - return 0; -} - -bool EthernetServer::hasClient() -{ - bool empty; - - pthread_mutex_lock(&new_clients_mutex); - empty = new_clients.empty(); - pthread_mutex_unlock(&new_clients_mutex); - - return !empty; -} - -EthernetClient EthernetServer::available() -{ - int sock; - - pthread_mutex_lock(&new_clients_mutex); - if (new_clients.empty()) { - sock = -1; - } else { - sock = new_clients.front(); - new_clients.pop_front(); - } - pthread_mutex_unlock(&new_clients_mutex); - - return EthernetClient(sock); -} - -size_t EthernetServer::write(uint8_t b) -{ - return write(&b, 1); -} - -size_t EthernetServer::write(const uint8_t *buffer, size_t size) -{ - size_t n = 0; - size_t i = 0; - - pthread_mutex_lock(&clients_mutex); - while (i < clients.size()) { - EthernetClient client(clients[i]); - if (client.connected()) { - n += client.write(buffer, size); - i++; - } else { - clients[i] = clients.back(); - clients.pop_back(); - } - } - pthread_mutex_unlock(&clients_mutex); - - return n; -} - -size_t EthernetServer::write(const char *str) { - if (str == NULL) return 0; - return write((const uint8_t *)str, strlen(str)); -} - -size_t EthernetServer::write(const char *buffer, size_t size) { - return write((const uint8_t *)buffer, size); -} - -void *incoming_connections(void* thread_arg) -{ - struct server_vars *vars = (struct server_vars*) thread_arg; - int sockfd, new_fd; struct addrinfo hints, *servinfo, *p; - struct sockaddr_storage client_addr; - socklen_t sin_size; int yes=1; int rv; char ipstr[INET_ADDRSTRLEN]; @@ -162,10 +54,10 @@ void *incoming_connections(void* thread_arg) hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; - sprintf(portstr, "%d", vars->port); - if ((rv = getaddrinfo(vars->address.toString().c_str(), portstr, &hints, &servinfo)) != 0) { + sprintf(portstr, "%d", port); + if ((rv = getaddrinfo(address.toString().c_str(), portstr, &hints, &servinfo)) != 0) { fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv)); - return NULL; + return; } // loop through all the results and bind to the first we can @@ -178,7 +70,7 @@ void *incoming_connections(void* thread_arg) if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) { perror("setsockopt"); freeaddrinfo(servinfo); - return NULL; + return; } if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) { @@ -193,53 +85,130 @@ void *incoming_connections(void* thread_arg) if (p == NULL) { fprintf(stderr, "failed to bind\n"); freeaddrinfo(servinfo); - return NULL; + return; } if (listen(sockfd, ETHERNETSERVER_BACKLOG) == -1) { perror("listen"); freeaddrinfo(servinfo); - return NULL; + 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); ETHERNETSERVER_DEBUG("Listening for connections on %s:%s\n", ipstr, portstr); +} - while (1) { // accept() loop - sin_size = sizeof client_addr; - new_fd = accept(sockfd, (struct sockaddr *)&client_addr, &sin_size); - if (new_fd == -1) { - perror("accept"); - continue; - } +bool EthernetServer::hasClient() +{ + _accept(); - void *addr = &(((struct sockaddr_in*)&client_addr)->sin_addr); - inet_ntop(client_addr.ss_family, addr, ipstr, sizeof ipstr); - ETHERNETSERVER_DEBUG("New connection from %s\n", ipstr); + return !new_clients.empty(); +} - pthread_mutex_lock(&new_clients_mutex); - pthread_mutex_lock(&clients_mutex); - int connected_clients = vars->new_clients->size() + vars->clients->size(); +EthernetClient EthernetServer::available() +{ + if (new_clients.empty()) { + return EthernetClient(); + } else { + int sock = new_clients.front(); + new_clients.pop_front(); + return EthernetClient(sock); + } +} - if (connected_clients == vars->max_clients) { - pthread_mutex_unlock(&clients_mutex); - pthread_mutex_unlock(&new_clients_mutex); - ETHERNETSERVER_DEBUG("Max number of ethernet clients reached."); - close(new_fd); - usleep(5000000); - } else { - vars->new_clients->push_back(new_fd); - vars->clients->push_back(new_fd); - pthread_mutex_unlock(&clients_mutex); - pthread_mutex_unlock(&new_clients_mutex); +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(); + ETHERNETSERVER_DEBUG("Client disconnected."); } } - delete vars; + return n; +} - return NULL; +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::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) { + ETHERNETSERVER_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) { + perror("accept"); + } + 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); + ETHERNETSERVER_DEBUG("New connection from %s\n", ipstr); } diff --git a/drivers/Linux/EthernetServer.h b/drivers/Linux/EthernetServer.h index 2127829..8bb4c6d 100644 --- a/drivers/Linux/EthernetServer.h +++ b/drivers/Linux/EthernetServer.h @@ -25,44 +25,102 @@ #include #include #include "Server.h" -#include "EthernetClient.h" +#include "IPAddress.h" -#ifndef ETHERNETSERVER_BACKLOG - #define ETHERNETSERVER_BACKLOG 10 -#endif - -#ifndef ETHERNETSERVER_MAX_CLIENTS - #define ETHERNETSERVER_MAX_CLIENTS ETHERNETSERVER_BACKLOG +#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 // debug #if defined(ETHERNETSERVER_VERBOSE) - #define ETHERNETSERVER_DEBUG(x,...) debug(x, ##__VA_ARGS__) + #define ETHERNETSERVER_DEBUG(x,...) printf(x, ##__VA_ARGS__) //!< debug #else - #define ETHERNETSERVER_DEBUG(x,...) + #define ETHERNETSERVER_DEBUG(x,...) //!< debug NULL #endif class EthernetClient; +/** + * @brief EthernetServer class + */ class EthernetServer : public Server { private: - uint16_t port; - std::list new_clients; - std::vector clients; - uint16_t max_clients; + uint16_t port; //!< @brief Port number for the network socket. + std::list new_clients; //!< Socket list of new clients. + std::vector 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(); - void begin(IPAddress); - void beginPacket(IPAddress, uint16_t); - int parsePacket(); + /** + * @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(); - virtual size_t write(uint8_t); - virtual size_t write(const uint8_t *, size_t); + /** + * @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); }; diff --git a/drivers/Linux/Server.h b/drivers/Linux/Server.h index d1b4046..7910e83 100644 --- a/drivers/Linux/Server.h +++ b/drivers/Linux/Server.h @@ -20,9 +20,11 @@ #ifndef server_h #define server_h +#if !DOXYGEN class Server { public: virtual void begin() =0; }; +#endif #endif From 211afb17c43ebf8e90639d44af7c7891b78f3dfa Mon Sep 17 00:00:00 2001 From: Marcelo Aquino Date: Mon, 15 Aug 2016 18:47:09 -0300 Subject: [PATCH 16/33] Doxygenize Linux drivers --- drivers/Linux/Client.h | 3 + drivers/Linux/EthernetClient.cpp | 34 +++--- drivers/Linux/EthernetClient.h | 176 ++++++++++++++++++++++++++----- drivers/Linux/IPAddress.h | 106 +++++++++++++++---- drivers/Linux/Stream.h | 2 + 5 files changed, 255 insertions(+), 66 deletions(-) diff --git a/drivers/Linux/Client.h b/drivers/Linux/Client.h index 0169890..067b57d 100644 --- a/drivers/Linux/Client.h +++ b/drivers/Linux/Client.h @@ -24,6 +24,7 @@ #include "Stream.h" #include "IPAddress.h" +#if !DOXYGEN class Client : public Stream { public: @@ -39,8 +40,10 @@ public: 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 diff --git a/drivers/Linux/EthernetClient.cpp b/drivers/Linux/EthernetClient.cpp index 5e80848..387fcaa 100644 --- a/drivers/Linux/EthernetClient.cpp +++ b/drivers/Linux/EthernetClient.cpp @@ -190,18 +190,6 @@ void EthernetClient::stop() { _sock = -1; } -uint8_t EthernetClient::connected() { - if (_sock == -1) return 0; - - if (peek() < 0) { - if (errno == EAGAIN) { - return 1; - } - return 0; - } - return 1; -} - uint8_t EthernetClient::status() { if (_sock == -1) return ETHERNETCLIENT_W5100_CLOSED; @@ -232,13 +220,27 @@ uint8_t EthernetClient::status() { case TCP_CLOSING: return ETHERNETCLIENT_W5100_CLOSING; } - } else { - perror("getsockopt"); } 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. @@ -249,7 +251,3 @@ EthernetClient::operator bool() { bool EthernetClient::operator==(const EthernetClient& rhs) { return _sock == rhs._sock && _sock != -1 && rhs._sock != -1; } - -int EthernetClient::getSocketNumber() { - return _sock; -} diff --git a/drivers/Linux/EthernetClient.h b/drivers/Linux/EthernetClient.h index 0627436..f21f5d3 100644 --- a/drivers/Linux/EthernetClient.h +++ b/drivers/Linux/EthernetClient.h @@ -39,42 +39,160 @@ // debug #if defined(ETHERNETCLIENT_VERBOSE) - #define ETHERNETCLIENT_DEBUG(x,...) debug(x, ##__VA_ARGS__) + #define ETHERNETCLIENT_DEBUG(x,...) debug(x, ##__VA_ARGS__) //!< debug #else - #define ETHERNETCLIENT_DEBUG(x,...) + #define ETHERNETCLIENT_DEBUG(x,...) //!< debug NULL #endif +/** + * EthernetClient class + */ class EthernetClient : public Client { -public: - EthernetClient(); - EthernetClient(int sock); - - uint8_t status(); - virtual int connect(IPAddress ip, uint16_t port); - virtual int connect(const char *host, uint16_t port); - virtual size_t write(uint8_t); - virtual size_t write(const uint8_t *buf, size_t size); - size_t write(const char *str); - size_t write(const char *buffer, size_t size); - virtual int available(); - virtual int read(); - virtual int read(uint8_t *buf, size_t size); - virtual int peek(); - virtual void flush(); - virtual void stop(); - virtual uint8_t connected(); - virtual operator bool(); - virtual bool operator==(const bool value) { return bool() == value; } - virtual bool operator!=(const bool value) { return bool() != value; } - virtual bool operator==(const EthernetClient&); - virtual bool operator!=(const EthernetClient& rhs) { return !this->operator==(rhs); }; - int getSocketNumber(); - - friend class EthernetServer; - private: int _sock; + +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 diff --git a/drivers/Linux/IPAddress.h b/drivers/Linux/IPAddress.h index f4b05c0..c8e336c 100644 --- a/drivers/Linux/IPAddress.h +++ b/drivers/Linux/IPAddress.h @@ -26,59 +26,127 @@ #include #include -// A class to make it easier to handle and pass around IP addresses - -// class IPAddress: public Printable { +/** + * @brief A class to make it easier to handle and pass around IP addresses + */ class IPAddress { private: union { - uint8_t bytes[4]; // IPv4 address - uint32_t dword; + uint8_t bytes[4]; //!< IPv4 address as an array + uint32_t dword; //!< IPv4 address in 32 bits format } _address; - // Access the raw byte array containing the address. Because this returns a pointer - // to the internal structure rather than a copy of the address this function should only - // be used when you know that the usage of the returned uint8_t* will be transient and not - // stored. + /** + * @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: - // Constructors + /** + * @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()); } - - // Overloaded cast operator to allow IPAddress objects to be used where a pointer - // to a four-byte uint8_t array is expected + /** + * @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; - - // Overloaded index operator to allow getting and setting individual octets of the address + /** + * @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]; } - - // Overloaded copy operators to allow initialisation of IPAddress objects from other types + /** + * @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; diff --git a/drivers/Linux/Stream.h b/drivers/Linux/Stream.h index 0241f6a..226543b 100644 --- a/drivers/Linux/Stream.h +++ b/drivers/Linux/Stream.h @@ -1,6 +1,7 @@ #ifndef Stream_h #define Stream_h +#if !DOXYGEN class Stream { public: virtual size_t write(uint8_t) = 0; @@ -11,5 +12,6 @@ class Stream { virtual int peek() = 0; virtual void flush() = 0; }; +#endif #endif From dab1906c56815c0934c5b0e6505a9ca3b6adec59 Mon Sep 17 00:00:00 2001 From: Marcelo Aquino Date: Tue, 16 Aug 2016 14:44:48 -0300 Subject: [PATCH 17/33] Remove leftover files from Rpi port --- libraries/MySensors/Makefile | 82 ---- .../core/MyGatewayTransportEthernetLinux.cpp | 454 ------------------ libraries/MySensors/core/MyHwLinuxGeneric.cpp | 163 ------- libraries/MySensors/core/MyHwLinuxGeneric.h | 43 -- libraries/MySensors/core/MyMainLinux.cpp | 13 - .../MySensors/examples_RPi/PiGateway.cpp | 68 --- 6 files changed, 823 deletions(-) delete mode 100644 libraries/MySensors/Makefile delete mode 100644 libraries/MySensors/core/MyGatewayTransportEthernetLinux.cpp delete mode 100644 libraries/MySensors/core/MyHwLinuxGeneric.cpp delete mode 100644 libraries/MySensors/core/MyHwLinuxGeneric.h delete mode 100644 libraries/MySensors/core/MyMainLinux.cpp delete mode 100644 libraries/MySensors/examples_RPi/PiGateway.cpp diff --git a/libraries/MySensors/Makefile b/libraries/MySensors/Makefile deleted file mode 100644 index 03e671d..0000000 --- a/libraries/MySensors/Makefile +++ /dev/null @@ -1,82 +0,0 @@ -########################################################################## -# Configurable options # -########################################################################## -# Install Base location -PREFIX=/usr/local -# Bin Dir -BINDIR=$(PREFIX)/sbin - -########################################################################## -# Please do not change anything below this line # -########################################################################## -CXX ?= g++ - -ifeq "$(shell uname -m)" "armv6l" - ARCH=armv6zk -endif -ifeq "$(shell uname -m)" "armv7l" - ARCH=armv7-a -endif -ifdef ARCH - # The recommended compiler flags for the Raspberry Pi - CXXFLAGS+=-Ofast -mfpu=vfp -mfloat-abi=hard -march=$(ARCH) -mtune=arm1176jzf-s -DRASPBERRYPI_ARCH $(OPTFLAGS) - LDFLAGS+=-lrf24-bcm -endif - -CXXFLAGS+=-g -Wall -Wextra -CXXFLAGS+=-DLINUX_ARCH_GENERIC $(OPTFLAGS) - -# get PI Revision from cpuinfo -PIREV := $(shell cat /proc/cpuinfo | grep Revision | cut -f 2 -d ":" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$$//') -ifeq ($(PIREV),$(filter $(PIREV),a01041 a21041 0010)) - # a01041 and a21041 are PI 2 Model B with BPLUS Layout and 0010 is Pi Model B+ with BPLUS Layout - CXXFLAGS+=-D__PI_BPLUS -endif - -COMPILER_FLAGS=examples_RPi/compiler_flags -GATEWAY=examples_RPi/PiGateway -GATEWAYMQTT=examples_RPi/PiGatewayMQTT -GATEWAY_SOURCES=examples_RPi/PiGateway.cpp $(wildcard ./utility/*.cpp) -GATEWAY_OBJECTS=$(patsubst %.cpp,%.o,$(GATEWAY_SOURCES)) -DEPS+=$(patsubst %.cpp,%.d,$(GATEWAY_SOURCES)) - -RF24H = /usr/local/include/RF24 -CINCLUDE=-I. -I./core -I$(RF24H) - -.PHONY: all gateway gatewaymqtt clean install uninstall force - -all: $(GATEWAY) - -mqtt: $(GATEWAYMQTT) - -$(COMPILER_FLAGS): force - echo '$(CXXFLAGS)' | cmp -s - $@ || echo '$(CXXFLAGS)' > $@ - -$(GATEWAY_OBJECTS): $(COMPILER_FLAGS) - -# Basic Gateway Build -$(GATEWAY): LDFLAGS += -pthread -$(GATEWAY): $(GATEWAY_OBJECTS) - $(CXX) $(LDFLAGS) -o $@ $(GATEWAY_OBJECTS) - -# MQTT Gateway Build -$(GATEWAYMQTT): CXXFLAGS += -DMY_GATEWAY_MQTT_CLIENT -$(GATEWAYMQTT): LDFLAGS += -pthread -lmosquitto -$(GATEWAYMQTT): $(GATEWAY_OBJECTS) - $(CXX) $(LDFLAGS) -o $@ $(GATEWAY_OBJECTS) - -# Include all .d files --include $(DEPS) - -%.o: %.cpp - $(CXX) $(CXXFLAGS) $(CINCLUDE) -MMD -c -o $@ $< - -# The Cleaner -clean: - rm -rf build $(GATEWAY_OBJECTS) $(GATEWAY) $(GATEWAYMQTT) $(DEPS) $(COMPILER_FLAGS) - -install: all install-gatewaybasic install-gatewayserial install-gatewayethernet install-initscripts - -install-gatewaybasic: - @echo "Installing $(GATEWAY) to $(BINDIR)" - @install -m 0755 $(GATEWAY) $(BINDIR) diff --git a/libraries/MySensors/core/MyGatewayTransportEthernetLinux.cpp b/libraries/MySensors/core/MyGatewayTransportEthernetLinux.cpp deleted file mode 100644 index c5d1091..0000000 --- a/libraries/MySensors/core/MyGatewayTransportEthernetLinux.cpp +++ /dev/null @@ -1,454 +0,0 @@ -/** - * The MySensors Arduino library handles the wireless radio link and protocol - * between your home built sensors/actuators and HA controller of choice. - * The sensors forms a self healing radio network with optional repeaters. Each - * repeater and gateway builds a routing tables in EEPROM which keeps track of the - * network topology allowing messages to be routed to nodes. - * - * Created by Marcelo Aquino - * Copyleft (c) 2016, Marcelo Aquino - * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors - * - * Documentation: http://www.mysensors.org - * Support Forum: http://forum.mysensors.org - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - */ - -#include "MyGatewayTransport.h" -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef MY_GATEWAY_MQTT_CLIENT - #include - - #ifndef MQTT_IP - #define MQTT_IP "127.0.0.1" - #endif - #ifndef MQTT_PORT - #define MQTT_PORT 1883 - #endif - #ifndef MQTT_KEEPALIVE - #define MQTT_KEEPALIVE 60 - #endif - #ifndef MY_MQTT_PUBLISH_TOPIC_PREFIX - #define MY_MQTT_PUBLISH_TOPIC_PREFIX "mygateway1-out" - #endif - #ifndef MY_MQTT_SUBSCRIBE_TOPIC_PREFIX - #define MY_MQTT_SUBSCRIBE_TOPIC_PREFIX "mygateway1-in" - #endif -#endif - -//TODO -#ifdef MY_USE_UDP - #error UDP not supported for this type of gateway -#endif - -union _addrIPv4{ - uint8_t bytes[4]; - uint32_t dword; -}; - -static int controllers[MY_GATEWAY_MAX_CLIENTS]; -static MyMessage _ethernetMsg; -static std::list ethernetMsg_q; -static pthread_mutex_t ethernetMsg_mutex = PTHREAD_MUTEX_INITIALIZER; - -// Prototypes -int open_listen(char *ip, uint16_t port); -uint32_t getAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet); -void *get_in_addr(struct sockaddr *sa); -void *waiting_controllers(void* thread_arg); -void *connected_controller(void* thread_arg); - -#ifdef MY_GATEWAY_MQTT_CLIENT -void *mqtt_thread(void *); -struct mosquitto *mosq; -#endif - -bool gatewayTransportInit() { - long int sockfd; - pthread_t thread_id; - pthread_attr_t attr; -#ifdef MY_GATEWAY_MQTT_CLIENT - pthread_attr_t mqtt_attr; -#endif - char *ip = NULL; - - // ignore SIGPIPE generated by send() to a disconnected client - signal(SIGPIPE, SIG_IGN); - - for (uint8_t i = 0; i < MY_GATEWAY_MAX_CLIENTS; i++) { - controllers[i] = -1; - } - -#ifdef MY_CONTROLLER_IP_ADDRESS - //TODO - return false; -#else - #ifdef MY_IP_ADDRESS - char ipstr[INET_ADDRSTRLEN]; - union _addrIPv4 addrIPv4 = getAddress(MY_IP_ADDRESS); - sprintf(ipstr, "%d.%d.%d.%d", addrIPv4.bytes[0], addrIPv4.bytes[1], - addrIPv4.bytes[2], addrIPv4.bytes[3]); - ip = ipstr; - #endif - - sockfd = open_listen(ip, MY_PORT); - if (sockfd < 0) { - return false; - } -#endif - - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - pthread_create(&thread_id, &attr, &waiting_controllers, (void *)sockfd); - pthread_attr_destroy(&attr); - -#ifdef MY_GATEWAY_MQTT_CLIENT - /* mqtt thread */ - pthread_attr_init(&mqtt_attr); - pthread_attr_setdetachstate(&mqtt_attr, PTHREAD_CREATE_DETACHED); - pthread_create(&thread_id, &mqtt_attr, &mqtt_thread, NULL); - pthread_attr_destroy(&mqtt_attr); -#endif - - return true; -} - -bool gatewayTransportSend(MyMessage &message) -{ - char *ethernetMsg = protocolFormat(message); - - for (uint8_t i = 0; i < MY_GATEWAY_MAX_CLIENTS; i++) { - if (controllers[i] == -1) - continue; - if (send(controllers[i], ethernetMsg, strlen(ethernetMsg), 0) == -1) - perror("send"); - } - -#ifdef MY_GATEWAY_MQTT_CLIENT - char *mqttMsg = protocolFormatMQTTTopic(MY_MQTT_PUBLISH_TOPIC_PREFIX, message); - //connection, message id, topic, bytes, data, QOS, retain - mosquitto_publish(mosq, NULL, mqttMsg, strlen(message.getString(_convBuffer)), message.getString(_convBuffer), 0, false); -#endif - - return true; -} - -bool gatewayTransportAvailable() -{ - bool empty; - - pthread_mutex_lock(ðernetMsg_mutex); - empty = ethernetMsg_q.empty(); - pthread_mutex_unlock(ðernetMsg_mutex); - - return !empty; -} - -MyMessage& gatewayTransportReceive() -{ - // Return the last parsed message - pthread_mutex_lock(ðernetMsg_mutex); - _ethernetMsg = ethernetMsg_q.front(); - ethernetMsg_q.pop_front(); - pthread_mutex_unlock(ðernetMsg_mutex); - - return _ethernetMsg; -} - -int open_listen(char *address, uint16_t port) -{ - int sockfd; - 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_INET; // IPv4 - hints.ai_socktype = SOCK_STREAM; - hints.ai_flags = AI_PASSIVE; - - sprintf(portstr, "%d", port); - if ((rv = getaddrinfo(address, portstr, &hints, &servinfo)) != 0) { - fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv)); - return -1; - } - - // loop through all the results and bind to the first we can - for (p = servinfo; p != NULL; p = p->ai_next) { - if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) { - perror("socket"); - continue; - } - - if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) { - perror("setsockopt"); - freeaddrinfo(servinfo); - return -1; - } - - if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) { - close(sockfd); - perror("bind"); - continue; - } - - break; - } - - if (p == NULL) { - fprintf(stderr, "failed to bind\n"); - freeaddrinfo(servinfo); - return -1; - } - - if (listen(sockfd, MY_GATEWAY_MAX_CLIENTS) == -1) { - perror("listen"); - freeaddrinfo(servinfo); - return -1; - } - - struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr; - void *addr = &(ipv4->sin_addr); - inet_ntop(p->ai_family, addr, ipstr, sizeof ipstr); - debug("Eth: Listening for connections on %s:%s\n", ipstr, portstr); - - freeaddrinfo(servinfo); - return sockfd; -} - -uint32_t getAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet) -{ - union _addrIPv4 addrIPv4; - - addrIPv4.bytes[0] = first_octet; - addrIPv4.bytes[1] = second_octet; - addrIPv4.bytes[2] = third_octet; - addrIPv4.bytes[3] = fourth_octet; - - return addrIPv4.dword; -} - -void *get_in_addr(struct sockaddr *sa) -{ - if (sa->sa_family == AF_INET) { - return &(((struct sockaddr_in*)sa)->sin_addr); - } - - return &(((struct sockaddr_in6*)sa)->sin6_addr); -} - -void *waiting_controllers(void* thread_arg) -{ - long int sockfd = (long int) thread_arg; - long int new_fd; // new connection on new_fd - struct sockaddr_storage client_addr; // connector's address information - socklen_t sin_size; - char s[INET6_ADDRSTRLEN]; - pthread_t thread_id; - pthread_attr_t attr; - uint8_t i; - - while (1) { // accept() loop - sin_size = sizeof client_addr; - new_fd = accept(sockfd, (struct sockaddr *)&client_addr, &sin_size); - if (new_fd == -1) { - perror("accept"); - continue; - } - - inet_ntop(client_addr.ss_family, - get_in_addr((struct sockaddr *)&client_addr), s, sizeof s); - debug("Eth: New connection from %s\n", s); - - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - pthread_create(&thread_id, &attr, &connected_controller, (void *)new_fd); - pthread_attr_destroy(&attr); - - for (i = 0; i < MY_GATEWAY_MAX_CLIENTS; i++) { - if (controllers[i] == -1) { - controllers[i] = new_fd; - break; - } - } - if (i >= MY_GATEWAY_MAX_CLIENTS) { - // no free/disconnected spot so reject - close(new_fd); - } - } - - return NULL; -} - -void *connected_controller(void* thread_arg) -{ - long int sockfd = (long int) thread_arg; - char inputString[MY_GATEWAY_MAX_RECEIVE_LENGTH]; - char *ptr = inputString; - int nbytes = 0; - MyMessage ethernetMsg; - - while (1) { - if (recv(sockfd, ptr, 1, 0) > 0) { - if (*ptr == '\n' || *ptr == '\r') { - // String terminator - *ptr = 0; - debug("Eth: %s\n", inputString); - if (protocolParse(ethernetMsg, inputString)) { - pthread_mutex_lock(ðernetMsg_mutex); - ethernetMsg_q.push_back(ethernetMsg); - pthread_mutex_unlock(ðernetMsg_mutex); - } - - // Prepare for the next message - ptr = inputString; - nbytes = 0; - continue; - } - nbytes++; - if (nbytes == MY_GATEWAY_MAX_RECEIVE_LENGTH) { - // Incoming message too long. Throw away - debug("Eth: Message too long\n"); - ptr = inputString; - nbytes = 0; - } else { - ptr++; - } - } else { - break; - } - } - - if (nbytes == -1) - perror("recv"); - - for (uint8_t i = 0; i < MY_GATEWAY_MAX_CLIENTS; i++) { - if (controllers[i] == sockfd) { - controllers[i] = -1; - break; - } - } - - close(sockfd); - return NULL; -} - -#ifdef MY_GATEWAY_MQTT_CLIENT -void mqtt_message_callback(struct mosquitto *mosq, void *userdata, const struct mosquitto_message *message) -{ - (void)mosq; - (void)userdata; - - if(message->payloadlen){ - printf("Got a MQTT message %s %s\n", message->topic, (char*)message->payload); - }else{ - printf("Got a MQTT message %s (null)\n", message->topic); - } - - MyMessage msg; - protocolMQTTParse(msg, message->topic, (uint8_t*)message->payload, message->payloadlen); - - if(msg.destination != 0 && msg.sensor != 0 && msg.type != 255) - { - ethernetMsg_q.push_back(msg); - - // Forward the data to Ethernet - // Likely this is a duplicate from a C_SET that we received and published - // in msg_callback. Not much we can do to avoid the duplicate as there is - // no way to tell if we performed the publish that is triggering this callback. - char *ethernetMsg = protocolFormat(msg); - - for (uint8_t i = 0; i < MY_GATEWAY_MAX_CLIENTS; i++) { - if (controllers[i] == -1) - continue; - if (send(controllers[i], ethernetMsg, strlen(ethernetMsg), 0) == -1) - perror("send"); - } - } - else - { - printf("Recieved a bad MQTT message: '%s':'%s'\n destination:%i, sensor:%i, type:%i\n", - message->topic, (char*)message->payload, msg.destination, msg.sensor, msg.type); - } -} - -void mqtt_connect_callback(struct mosquitto *mosq, void *userdata, int result) -{ - (void)mosq; - (void)userdata; - - if(!result){ - printf("Connected!\n"); - /* Subscribe to broker information topics on successful connect. */ - //connection, message id, topic, qos - mosquitto_subscribe(mosq, NULL, protocolFormatMQTTSubscribe(MY_MQTT_SUBSCRIBE_TOPIC_PREFIX), 0); - }else{ - fprintf(stderr, "Connect failed\n"); - } -} - -void mqtt_subscribe_callback(struct mosquitto *mosq, void *userdata, int mid, int qos_count, const int *granted_qos) -{ - (void)mosq; - (void)userdata; - - int i; - - printf("Subscribed (mid: %d): %d", mid, granted_qos[0]); - for(i=1; i - * 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 "MyHwLinuxGeneric.h" - -#include -#include -#include -#include -#include - -static const char* CONFIG_FILE = "/etc/MySensorGateway.cfg"; -static const size_t _length = 1024; // ATMega328 has 1024 bytes -static uint8_t _config[_length]; -static unsigned long millis_at_start; - -bool CheckConfigFile() { - struct stat fileInfo; - - if(stat(CONFIG_FILE, &fileInfo) != 0) - { - //File does not exist. Create it. - debug("Config file %s does not exist, creating new config file.\n", CONFIG_FILE); - ofstream myFile(CONFIG_FILE, ios::out | ios::binary); - if(!myFile) - { - debug("Unable to create config file %s.\n", CONFIG_FILE); - return false; - } - myFile.write((const char*)_config, _length); - myFile.close(); - } - else if(fileInfo.st_size != _length) - { - debug("Config file %s is not the correct size of %i. Please remove the file and a new one will be created.\n", CONFIG_FILE, _length); - return false; - } - else - { - //Read config into local memory. - ifstream myFile(CONFIG_FILE, ios::in | ios::binary); - if(!myFile) - { - debug("Unable to open config to file %s for reading.\n", CONFIG_FILE); - return false; - } - myFile.read((char*)_config, _length); - myFile.close(); - } - - return true; -} - -void hwInit() -{ - timeval curTime; - - for (size_t i = 0; i < _length; i++) - _config[i] = 0xFF; - - if (!CheckConfigFile()) - { - exit(1); - } - - gettimeofday(&curTime, NULL); - millis_at_start = curTime.tv_sec; -} - -void hwReadConfigBlock(void* buf, void* addr, size_t length) -{ - unsigned long int offs = reinterpret_cast(addr); - - if (length && offs + length <= _length) { - memcpy(buf, _config+offs, length); - } -} - -void hwWriteConfigBlock(void* buf, void* addr, size_t length) -{ - unsigned long int offs = reinterpret_cast(addr); - - if (length && offs + length <= _length) { - memcpy(_config+offs, buf, length); - - ofstream myFile(CONFIG_FILE, ios::out | ios::binary); - if(!myFile) - { - debug("Unable to write config to file %s.\n", CONFIG_FILE); - return; - } - myFile.write((const char*)buf+offs, _length); - myFile.close(); - } -} - -uint8_t hwReadConfig(int adr) -{ - uint8_t value = 0xFF; - hwReadConfigBlock(&value, reinterpret_cast(adr), 1); - return value; -} - -void hwWriteConfig(int adr, uint8_t value) -{ - uint8_t curr = hwReadConfig(adr); - if (curr != value) - { - hwWriteConfigBlock(&value, reinterpret_cast(adr), 1); - } -} - -unsigned long hwMillis() -{ - timeval curTime; - - gettimeofday(&curTime, NULL); - return ((curTime.tv_sec - millis_at_start) * 1000) + (curTime.tv_usec / 1000); -} - -int8_t hwSleep(unsigned long ms) -{ - // TODO: Not supported! - return -2; -} - -int8_t hwSleep(uint8_t interrupt, uint8_t mode, unsigned long ms) -{ - // TODO: Not supported! - return -2; -} - -int8_t hwSleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2, unsigned long ms) -{ - // TODO: Not supported! - return -2; -} - -#ifdef MY_DEBUG -void hwDebugPrint(const char *fmt, ... ) -{ - va_list arglist; - va_start(arglist, fmt); - vprintf(fmt, arglist); - va_end(arglist); -} -#endif diff --git a/libraries/MySensors/core/MyHwLinuxGeneric.h b/libraries/MySensors/core/MyHwLinuxGeneric.h deleted file mode 100644 index cec3b6c..0000000 --- a/libraries/MySensors/core/MyHwLinuxGeneric.h +++ /dev/null @@ -1,43 +0,0 @@ -/** - * The MySensors Arduino library handles the wireless radio link and protocol - * between your home built sensors/actuators and HA controller of choice. - * The sensors forms a self healing radio network with optional repeaters. Each - * repeater and gateway builds a routing tables in EEPROM which keeps track of the - * network topology allowing messages to be routed to nodes. - * - * Created by Marcelo Aquino - * Copyleft (c) 2016, Marcelo Aquino - * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors - * - * Documentation: http://www.mysensors.org - * Support Forum: http://forum.mysensors.org - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - */ - -#ifndef MyHwLinuxGeneric_h -#define MyHwLinuxGeneric_h - -#include "MyHw.h" -#include -#include - -#define MY_SERIALDEVICE Serial - -// Define these as macros (do nothing) -#define hwDigitalWrite(__pin, __value) -#define hwWatchdogReset() -#define hwReboot() - -using namespace std; - -void hwInit(); -void hwReadConfigBlock(void* buf, void* adr, size_t length); -void hwWriteConfigBlock(void* buf, void* adr, size_t length); -void hwWriteConfig(int adr, uint8_t value); -uint8_t hwReadConfig(int adr); -unsigned long hwMillis(); - -#endif diff --git a/libraries/MySensors/core/MyMainLinux.cpp b/libraries/MySensors/core/MyMainLinux.cpp deleted file mode 100644 index 98982e5..0000000 --- a/libraries/MySensors/core/MyMainLinux.cpp +++ /dev/null @@ -1,13 +0,0 @@ -// Initialize library and handle sketch functions like we want to - -#include "MySensorCore.h" - -int main(void) { - _begin(); // Startup MySensors library - - for(;;) { - _process(); // Process incoming data - if (loop) loop(); // Call sketch loop - } - return 0; -} diff --git a/libraries/MySensors/examples_RPi/PiGateway.cpp b/libraries/MySensors/examples_RPi/PiGateway.cpp deleted file mode 100644 index 28031d3..0000000 --- a/libraries/MySensors/examples_RPi/PiGateway.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/** - * The MySensors Arduino library handles the wireless radio link and protocol - * between your home built sensors/actuators and HA controller of choice. - * The sensors forms a self healing radio network with optional repeaters. Each - * repeater and gateway builds a routing tables in EEPROM which keeps track of the - * network topology allowing messages to be routed to nodes. - * - * Created by Marcelo Aquino - * Copyleft (c) 2016, Marcelo Aquino - * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors - * - * Documentation: http://www.mysensors.org - * Support Forum: http://forum.mysensors.org - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - */ - -#include -#include -#include - -// Enable debug prints to serial monitor -#define MY_DEBUG - -// Enables and select radio type (if attached) -#define MY_RADIO_NRF24 - -#define MY_GATEWAY_RASPBERRYPI - -// Enable MY_IP_ADDRESS here if you want to listen for incoming network -// connections on the specified IP address/hostname only -//#define MY_IP_ADDRESS 192,168,178,87 - -// The port to keep open on node server mode -#define MY_PORT 5003 - -// How many clients should be able to connect to this gateway (default 1) -#define MY_GATEWAY_MAX_CLIENTS 2 - -/* MQTT BUILD */ -#ifdef MY_GATEWAY_MQTT_CLIENT -//Linux MQTT Defines - Uncomment and modify as needed. Written as defaults. -//#define MQTT_IP "127.0.0.1" -//#define MQTT_PORT 1883 -//#define MQTT_KEEPALIVE 60 -// Enable these if your MQTT broker requires usenrame/password -//#define MY_MQTT_USER "username" -//#define MY_MQTT_PASSWORD "password" -//#define MY_MQTT_PUBLISH_TOPIC_PREFIX "mygateway1-out" -//#define MY_MQTT_SUBSCRIBE_TOPIC_PREFIX "mygateway1-in" -#endif - -#include - -using namespace std; - -void setup() { -} - -void presentation() { - // Present locally attached sensors here -} - -void loop() { - // Send locally attached sensors data here -} From 14e5c9bf4c50602bdeb1f76d7a398cd08f770b28 Mon Sep 17 00:00:00 2001 From: Marcelo Aquino Date: Tue, 16 Aug 2016 15:37:51 -0300 Subject: [PATCH 18/33] Fix RX message buffering for Rpi --- configure | 2 +- drivers/RF24/RF24_RPi.cpp | 44 ++++++++++++++++++++++++++------------- 2 files changed, 30 insertions(+), 16 deletions(-) diff --git a/configure b/configure index d686549..7507807 100755 --- a/configure +++ b/configure @@ -269,7 +269,7 @@ for opt do CXXFLAGS="-DMY_MQTT_SUBSCRIBE_TOPIC_PREFIX=${optarg} $CXXFLAGS" ;; --my-rf24-irq-pin=*) - CXXFLAGS="-DMY_RF24_IRQ_PIN=${optarg} $CXXFLAGS" + 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" diff --git a/drivers/RF24/RF24_RPi.cpp b/drivers/RF24/RF24_RPi.cpp index 3b1a091..f130df0 100644 --- a/drivers/RF24/RF24_RPi.cpp +++ b/drivers/RF24/RF24_RPi.cpp @@ -21,6 +21,20 @@ #include "RF24.h" +// verify RF24 IRQ defs +#if defined(MY_RX_MESSAGE_BUFFER_FEATURE) + #if !defined(MY_RF24_IRQ_PIN) + #error Message buffering feature requires MY_RF24_IRQ_PIN to be defined! + #endif + #ifndef SPI_HAS_TRANSACTION + #error RF24 IRQ usage requires transactional SPI support + #endif +#else + #ifdef MY_RX_MESSAGE_BUFFER_SIZE + #error Receive message buffering requires RF24 IRQ usage + #endif +#endif + // pipes #define BROADCAST_PIPE 1 #define NODE_PIPE 2 @@ -32,7 +46,7 @@ #define RF24_DEBUG(x,...) #endif -#ifdef MY_RF24_IRQ_PIN +#ifdef MY_RX_MESSAGE_BUFFER_FEATURE typedef void (*RF24_receiveCallbackType)(void); void (* RF24_receiveCallback)(void) = NULL; @@ -52,17 +66,17 @@ static void RF24_startListening(void) { } static void RF24_powerDown(void) { -#ifdef MY_RF24_IRQ_PIN +#ifdef MY_RX_MESSAGE_BUFFER_FEATURE pthread_mutex_lock(&rf24_mutex); #endif _rf24.powerDown(); -#ifdef MY_RF24_IRQ_PIN +#ifdef MY_RX_MESSAGE_BUFFER_FEATURE pthread_mutex_unlock(&rf24_mutex); #endif } static bool RF24_sendMessage( uint8_t recipient, const void* buf, uint8_t len ) { -#ifdef MY_RF24_IRQ_PIN +#ifdef MY_RX_MESSAGE_BUFFER_FEATURE pthread_mutex_lock(&rf24_mutex); #endif // Make sure radio has powered up @@ -75,38 +89,38 @@ static bool RF24_sendMessage( uint8_t recipient, const void* buf, uint8_t len ) _rf24.openWritingPipe(MY_RF24_BASE_ADDR); bool ok = _rf24.write(buf, len, recipient == BROADCAST_ADDRESS); _rf24.startListening(); -#ifdef MY_RF24_IRQ_PIN +#ifdef MY_RX_MESSAGE_BUFFER_FEATURE pthread_mutex_unlock(&rf24_mutex); #endif return ok; } static bool RF24_isDataAvailable() { -#ifdef MY_RF24_IRQ_PIN +#ifdef MY_RX_MESSAGE_BUFFER_FEATURE pthread_mutex_lock(&rf24_mutex); #endif uint8_t pipe_num = 255; _rf24.available(&pipe_num); -#ifdef MY_RF24_IRQ_PIN +#ifdef MY_RX_MESSAGE_BUFFER_FEATURE pthread_mutex_unlock(&rf24_mutex); #endif return (pipe_num <= 5); } static uint8_t RF24_readMessage( void* buf) { -#ifdef MY_RF24_IRQ_PIN +#ifdef MY_RX_MESSAGE_BUFFER_FEATURE pthread_mutex_lock(&rf24_mutex); #endif uint8_t len = _rf24.getDynamicPayloadSize(); _rf24.read(buf, len); -#ifdef MY_RF24_IRQ_PIN +#ifdef MY_RX_MESSAGE_BUFFER_FEATURE pthread_mutex_unlock(&rf24_mutex); #endif return len; } static void RF24_setNodeAddress(uint8_t address) { -#ifdef MY_RF24_IRQ_PIN +#ifdef MY_RX_MESSAGE_BUFFER_FEATURE pthread_mutex_lock(&rf24_mutex); #endif if (address != AUTO){ @@ -117,7 +131,7 @@ static void RF24_setNodeAddress(uint8_t address) { // enable autoACK on node pipe _rf24.setAutoAck(NODE_PIPE, true); } -#ifdef MY_RF24_IRQ_PIN +#ifdef MY_RX_MESSAGE_BUFFER_FEATURE pthread_mutex_unlock(&rf24_mutex); #endif } @@ -128,7 +142,7 @@ static uint8_t RF24_getNodeID(void) { bool RF24_sanityCheck(void) { bool ok = true; -#ifdef MY_RF24_IRQ_PIN +#ifdef MY_RX_MESSAGE_BUFFER_FEATURE pthread_mutex_lock(&rf24_mutex); #endif // detect HW defect ot interrupted SPI line, CE disconnect cannot be detected @@ -136,13 +150,13 @@ bool RF24_sanityCheck(void) { _rf24.getChannel() != MY_RF24_CHANNEL) { ok = false; } -#ifdef MY_RF24_IRQ_PIN +#ifdef MY_RX_MESSAGE_BUFFER_FEATURE pthread_mutex_unlock(&rf24_mutex); #endif return ok; } -#ifdef MY_RF24_IRQ_PIN +#ifdef MY_RX_MESSAGE_BUFFER_FEATURE void RF24_irqHandler(void) { if (RF24_receiveCallback) { while (RF24_isDataAvailable()) { @@ -199,7 +213,7 @@ static bool RF24_initialize(void) { //_rf24.printDetails(); - #ifdef MY_RF24_IRQ_PIN + #ifdef MY_RX_MESSAGE_BUFFER_FEATURE // Mask all interrupts except the receive interrupt _rf24.maskIRQ(1,1,0); From 4d431fbebb3ddd3da366982887696d4ecc6279f7 Mon Sep 17 00:00:00 2001 From: Marcelo Aquino Date: Tue, 16 Aug 2016 15:51:43 -0300 Subject: [PATCH 19/33] Increase default number of connected client on Rpi --- examples_RPi/MySGateway.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples_RPi/MySGateway.cpp b/examples_RPi/MySGateway.cpp index dc61f85..8a7e9dd 100644 --- a/examples_RPi/MySGateway.cpp +++ b/examples_RPi/MySGateway.cpp @@ -39,7 +39,7 @@ #define MY_GATEWAY_RASPBERRYPI // How many clients should be able to connect to this gateway (default 1) -#define MY_GATEWAY_MAX_CLIENTS 2 +#define MY_GATEWAY_MAX_CLIENTS 10 #ifdef MY_GATEWAY_MQTT_CLIENT // Set MQTT client id From b8d4a6a94375a582365663690de98ca7627c1078 Mon Sep 17 00:00:00 2001 From: Marcelo Aquino Date: Wed, 17 Aug 2016 23:42:27 -0300 Subject: [PATCH 20/33] Improve build process for Linux --- Makefile | 10 +- MyConfig.h | 16 +- MySensors.h | 12 +- configure | 65 ++-- core/MySensorsCore.cpp | 4 +- core/MyTransportNRF24.cpp | 4 +- drivers/Linux/Arduino.h | 1 + .../Linux/{utility.cpp => compatibility.cpp} | 10 + drivers/RF24/{RF24_RPi.cpp => RF24_Linux.cpp} | 32 +- drivers/RF24/RF24_Linux.h | 302 ++++++++++++++++++ examples_RPi/MySGateway.cpp | 91 ------ examples_linux/mysGateway.cpp | 55 ++++ initscripts/mysgateway.systemd | 2 +- initscripts/mysgateway.sysvinit | 4 +- 14 files changed, 438 insertions(+), 170 deletions(-) rename drivers/Linux/{utility.cpp => compatibility.cpp} (95%) rename drivers/RF24/{RF24_RPi.cpp => RF24_Linux.cpp} (87%) create mode 100644 drivers/RF24/RF24_Linux.h delete mode 100644 examples_RPi/MySGateway.cpp create mode 100644 examples_linux/mysGateway.cpp diff --git a/Makefile b/Makefile index b5ba3a8..05067d1 100644 --- a/Makefile +++ b/Makefile @@ -11,12 +11,16 @@ CONFIG_FILE=Makefile.inc include $(CONFIG_FILE) -GATEWAY=examples_RPi/MySGateway -GATEWAY_SOURCES=$(wildcard drivers/Linux/*.cpp) examples_RPi/MySGateway.cpp +GATEWAY=examples_linux/mysGateway +GATEWAY_SOURCES=$(wildcard drivers/Linux/*.cpp) examples_linux/mysGateway.cpp GATEWAY_OBJECTS=$(patsubst %.cpp,%.o,$(GATEWAY_SOURCES)) DEPS+=$(patsubst %.cpp,%.d,$(GATEWAY_SOURCES)) -CINCLUDE=-I. -I./core -I./drivers/Linux -I$(RF24H_LIB_DIR) +CINCLUDE=-I. -I./core -I./drivers/Linux + +ifneq ($(RF24H_LIB_DIR),) +CINCLUDE+=-I$(RF24H_LIB_DIR) +endif .PHONY: all gateway cleanconfig clean install uninstall force diff --git a/MyConfig.h b/MyConfig.h index 10444f8..3834b5f 100644 --- a/MyConfig.h +++ b/MyConfig.h @@ -410,17 +410,8 @@ #define MY_RF24_CE_PIN 4 #elif defined(ARDUINO_ARCH_SAMD) #define MY_RF24_CE_PIN 27 - #elif defined(RASPBERRYPI_ARCH) - #include - #include - #ifdef __RPI_BPLUS - #define MY_RF24_CE_PIN RPI_BPLUS_GPIO_J8_22 - #define MY_RF24_CS_PIN RPI_BPLUS_GPIO_J8_24 - #else - #define MY_RF24_CE_PIN RPI_V2_GPIO_P1_22 - #define MY_RF24_CS_PIN BCM2835_SPI_CS0 - #endif - #define MY_RF24_SPI_SPEED BCM2835_SPI_SPEED_8MHZ + #elif defined(LINUX) + #define MY_RF24_CE_PIN 22 #else #define MY_RF24_CE_PIN 9 #endif @@ -435,6 +426,8 @@ #define MY_RF24_CS_PIN 15 #elif defined(ARDUINO_ARCH_SAMD) #define MY_RF24_CS_PIN 3 + #elif defined(LINUX) + #define MY_RF24_CS_PIN 24 #else #define MY_RF24_CS_PIN 10 #endif @@ -616,7 +609,6 @@ //#define MY_GATEWAY_W5100 //#define MY_GATEWAY_ENC28J60 //#define MY_GATEWAY_ESP8266 -//#define MY_GATEWAY_RASPBERRYPI //#define MY_GATEWAY_LINUX /** diff --git a/MySensors.h b/MySensors.h index 81b514b..d4c7a5a 100644 --- a/MySensors.h +++ b/MySensors.h @@ -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_RASPBERRYPI) || defined(MY_GATEWAY_LINUX) || 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" @@ -52,10 +52,6 @@ #define MY_NODE_TYPE "NODE" #endif -#if defined(MY_GATEWAY_RASPBERRYPI) - #define MY_GATEWAY_LINUX -#endif - // Enable radio "feature" if one of the radio types was enabled #if defined(MY_RADIO_NRF24) || defined(MY_RADIO_RFM69) || defined(MY_RS485) #define MY_RADIO_FEATURE @@ -73,7 +69,7 @@ #include "core/MyHwATMega328.cpp" #elif defined(ARDUINO_ARCH_SAMD) #include "core/MyHwSAMD.cpp" -#elif defined(LINUX_ARCH_GENERIC) +#elif defined(LINUX) // Remove PSTR macros from debug prints #undef PSTR #define PSTR(x) (x) @@ -288,7 +284,7 @@ #if defined(MY_RF24_ENABLE_ENCRYPTION) #include "drivers/AES/AES.cpp" #endif - #if !defined(RASPBERRYPI_ARCH) + #if !defined(LINUX) #include "drivers/RF24/RF24.cpp" #endif #include "core/MyTransportNRF24.cpp" @@ -333,7 +329,7 @@ #if !defined(MY_CORE_ONLY) #if defined(ARDUINO_ARCH_ESP8266) #include "core/MyMainESP8266.cpp" - #elif defined(LINUX_ARCH_GENERIC) + #elif defined(LINUX) #include "core/MyMainLinux.cpp" #else #include "core/MyMainDefault.cpp" diff --git a/configure b/configure index 7507807..7ad5629 100755 --- a/configure +++ b/configure @@ -32,11 +32,15 @@ Installation options: --gateway-dir= Gateway files installation directory. [PREFIX/bin] MySensors options: - --my-debug Enables debug. - --my-gateway-type=[ethernet|mqtt] - Gateway type, default to ethernet. + --my-debug=[enable|disable] + Enables or disables debugging. + Default to enable. + --my-gateway=[none|ethernet|mqtt] + Gateway type, set to none to disable gateway feature. + Default to ethernet. --my-config-file= Config file path. [/etc/MySensorGateway.cfg] - --my-rf24-enable Enables RF24 radio. + --my-radio=[none|nrf24] Radio type, set to none to disable radio feature. + Default to rf24. --my-rf24-channel=<0-125> RF channel for the sensor net, 0-125. --my-rf24-pa-level=[RF24_PA_MAX|RF24_PA_LOW] RF24 PA level. @@ -45,7 +49,8 @@ MySensors options: --my-controller-ip-address= Controller or MQTT broker ip. Use commas instead of points. Example: 127,0,0,1 - --my-port= Controller or MQTT broker port. + --my-port= The port to keep open on gateway mode. + If gateway is set to mqtt, it sets the broker port. --my-mqtt-client-id= MQTT client id. --my-mqtt-publish-topic-prefix= MQTT publish topic prefix. @@ -164,10 +169,10 @@ function gcc_cpu_flags { local soc=$1 case $soc in BCM2835) - flags="-march=armv6zk -mtune=arm1176jzf-s -mfpu=vfp -mfloat-abi=hard -DRASPBERRYPI_ARCH" + 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 -DRASPBERRYPI_ARCH" + 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" @@ -190,7 +195,12 @@ function gcc_cpu_flags { echo ${flags} } -params="OS SOC CPUFLAGS CXXFLAGS LDFLAGS PREFIX CXX RF24H_LIB_DIR GATEWAY_DIR INIT_SYSTEM" +# Default values +debug=enable +gateway_type=ethernet +radio_type=nrf24 + +params="OS SOC CXXFLAGS LDFLAGS PREFIX CXX RF24H_LIB_DIR GATEWAY_DIR INIT_SYSTEM" for opt do if [ "$opt" = "-h" ] || [ "$opt" = "--help" ]; then @@ -229,20 +239,17 @@ for opt do --gateway-dir=*) GATEWAY_DIR="$optarg" ;; - --my-debug*) - CXXFLAGS="-DMY_DEBUG $CXXFLAGS" + --my-debug=*) + debug=${optarg} ;; - --my-gateway-type=*) - if [[ ${optarg} == "mqtt" ]]; then - LDFLAGS="-lmosquitto $LDFLAGS" - CXXFLAGS="-DMY_GATEWAY_MQTT_CLIENT $CXXFLAGS" - fi + --my-gateway=*) + gateway_type=${optarg} ;; --my-config-file=*) CXXFLAGS="-DMY_CONFIG_FILE=${optarg} $CXXFLAGS" ;; - --my-rf24-enable*) - CXXFLAGS="-DMY_RADIO_NRF24 $CXXFLAGS" + --my-radio=*) + radio_type=${optarg} ;; --my-rf24-channel=*) CXXFLAGS="-DMY_RF24_CHANNEL=${optarg} $CXXFLAGS" @@ -281,7 +288,6 @@ for opt do done PREFIX=${PREFIX:-/usr/local} -RF24H_LIB_DIR=${PREFIX}/include/RF24 GATEWAY_DIR=${GATEWAY_DIR:-${PREFIX}/bin} CXX=${CXX:-g++} @@ -299,13 +305,30 @@ if [ -z "${CPUFLAGS}" ]; then CPUFLAGS=$(gcc_cpu_flags $SOC) fi -CXXFLAGS="$CPUFLAGS -Ofast -g -Wall -Wextra -DLINUX_ARCH_GENERIC $CXXFLAGS" +CXXFLAGS="$CPUFLAGS -Ofast -g -Wall -Wextra -DLINUX $CXXFLAGS" if [[ $TYPE == "RPi2" || $TYPE == "RPi3" || $REV == "0010" ]]; then CXXFLAGS+="-D__RPI_BPLUS" -fi +fi -LDFLAGS="-pthread -lrf24-bcm $LDFLAGS" +if [[ ${debug} == "enable" ]]; then + CXXFLAGS="-DMY_DEBUG $CXXFLAGS" +fi + +if [[ ${gateway_type} == "ethernet" ]]; then + CXXFLAGS="-DMY_GATEWAY_LINUX $CXXFLAGS" +elif [[ ${gateway_type} == "mqtt" ]]; then + LDFLAGS="-lmosquitto $LDFLAGS" + CXXFLAGS="-DMY_GATEWAY_MQTT_CLIENT $CXXFLAGS" +fi + +if [[ ${radio_type} == "nrf24" ]]; then + CXXFLAGS="-DMY_RADIO_NRF24 $CXXFLAGS" + LDFLAGS="-lrf24-bcm $LDFLAGS" + RF24H_LIB_DIR=${PREFIX}/include/RF24 +fi + +LDFLAGS="-pthread $LDFLAGS" if [ -x /usr/bin/systemctl ] || [ -x /bin/systemctl ]; then INIT_SYSTEM=systemd diff --git a/core/MySensorsCore.cpp b/core/MySensorsCore.cpp index b142fe2..2d0a9db 100644 --- a/core/MySensorsCore.cpp +++ b/core/MySensorsCore.cpp @@ -51,7 +51,7 @@ void _process() { transportProcess(); #endif - #if defined(LINUX_ARCH_GENERIC) + #if defined(LINUX) // To avoid high cpu usage usleep(10000); // 10ms #endif @@ -65,7 +65,7 @@ void _infiniteLoop() { #if defined (MY_LEDS_BLINKING_FEATURE) ledsProcess(); #endif - #if defined(LINUX_ARCH_GENERIC) + #if defined(LINUX) exit(1); #endif } diff --git a/core/MyTransportNRF24.cpp b/core/MyTransportNRF24.cpp index 6dce160..ce39008 100644 --- a/core/MyTransportNRF24.cpp +++ b/core/MyTransportNRF24.cpp @@ -20,8 +20,8 @@ #include "MyConfig.h" #include "MyTransport.h" -#ifdef RASPBERRYPI_ARCH - #include "drivers/RF24/RF24_RPi.cpp" +#ifdef LINUX_ARCH_RASPBERRYPI + #include "drivers/RF24/RF24_Linux.cpp" #else #include "drivers/RF24/RF24.h" #endif diff --git a/drivers/Linux/Arduino.h b/drivers/Linux/Arduino.h index 39f0f6e..e928b04 100644 --- a/drivers/Linux/Arduino.h +++ b/drivers/Linux/Arduino.h @@ -51,6 +51,7 @@ char *ltoa(long value, char* result, int base); char *ultoa(long num, char *str, int radix); char *utoa(int num, char *str, int radix); char *dtostrf(float f, int width, int decimals, char *result); +void delay(unsigned int millis); #ifdef __cplusplus } diff --git a/drivers/Linux/utility.cpp b/drivers/Linux/compatibility.cpp similarity index 95% rename from drivers/Linux/utility.cpp rename to drivers/Linux/compatibility.cpp index aa36905..5c1e5fe 100644 --- a/drivers/Linux/utility.cpp +++ b/drivers/Linux/compatibility.cpp @@ -1,4 +1,5 @@ #include +#include #include "Arduino.h" /** @@ -175,3 +176,12 @@ char *dtostrf(float f, int width, int decimals, char *result) sprintf(result,"%*.*f", width, decimals, f); return result; } + +void delay(unsigned int millis) +{ + struct timespec sleeper; + + sleeper.tv_sec = (time_t)(millis / 1000); + sleeper.tv_nsec = (long)(millis % 1000) * 1000000; + nanosleep(&sleeper, NULL); +} diff --git a/drivers/RF24/RF24_RPi.cpp b/drivers/RF24/RF24_Linux.cpp similarity index 87% rename from drivers/RF24/RF24_RPi.cpp rename to drivers/RF24/RF24_Linux.cpp index f130df0..9b4c25a 100644 --- a/drivers/RF24/RF24_RPi.cpp +++ b/drivers/RF24/RF24_Linux.cpp @@ -16,35 +16,11 @@ * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * - * Based on Olivier Mauti work(RF24.h), copyright (C) 2016 Olivier Mauti + * This is a wrapper for maniacbug's RF24 library, copyright (C) 2011 J. Coliz */ -#include "RF24.h" - -// verify RF24 IRQ defs -#if defined(MY_RX_MESSAGE_BUFFER_FEATURE) - #if !defined(MY_RF24_IRQ_PIN) - #error Message buffering feature requires MY_RF24_IRQ_PIN to be defined! - #endif - #ifndef SPI_HAS_TRANSACTION - #error RF24 IRQ usage requires transactional SPI support - #endif -#else - #ifdef MY_RX_MESSAGE_BUFFER_SIZE - #error Receive message buffering requires RF24 IRQ usage - #endif -#endif - -// pipes -#define BROADCAST_PIPE 1 -#define NODE_PIPE 2 - -// debug -#if defined(MY_DEBUG_VERBOSE_RF24) - #define RF24_DEBUG(x,...) debug(x, ##__VA_ARGS__) -#else - #define RF24_DEBUG(x,...) -#endif +#include +#include "RF24_Linux.h" #ifdef MY_RX_MESSAGE_BUFFER_FEATURE typedef void (*RF24_receiveCallbackType)(void); @@ -57,7 +33,7 @@ static uint8_t MY_RF24_BASE_ADDR[MY_RF24_ADDR_WIDTH] = { MY_RF24_BASE_RADIO_ID }; static uint8_t MY_RF24_NODE_ADDRESS = AUTO; -RF24 _rf24(MY_RF24_CE_PIN, MY_RF24_CS_PIN); +RF24 _rf24(RF24_CE_PIN, RF24_CS_PIN, MY_RF24_SPI_MAX_SPEED); static void RF24_startListening(void) { RF24_DEBUG(PSTR("start listening\n")); diff --git a/drivers/RF24/RF24_Linux.h b/drivers/RF24/RF24_Linux.h new file mode 100644 index 0000000..d1e00ac --- /dev/null +++ b/drivers/RF24/RF24_Linux.h @@ -0,0 +1,302 @@ +/** + * The MySensors Arduino library handles the wireless radio link and protocol + * between your home built sensors/actuators and HA controller of choice. + * The sensors forms a self healing radio network with optional repeaters. Each + * repeater and gateway builds a routing tables in EEPROM which keeps track of the + * network topology allowing messages to be routed to nodes. + * + * Created by Marcelo Aquino + * Copyright (C) 2016 Marcelo Aquino + * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors + * + * Documentation: http://www.mysensors.org + * Support Forum: http://forum.mysensors.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + */ + +#ifndef __RF24_LINUX_H__ +#define __RF24_LINUX_H__ + +// SPI settings +#if !defined(MY_RF24_SPI_MAX_SPEED) + #define MY_RF24_SPI_MAX_SPEED BCM2835_SPI_SPEED_8MHZ +#endif + +// verify RF24 IRQ defs +#if defined(MY_RX_MESSAGE_BUFFER_FEATURE) + #if !defined(MY_RF24_IRQ_PIN) + #error Message buffering feature requires MY_RF24_IRQ_PIN to be defined! + #endif + #ifndef SPI_HAS_TRANSACTION + #error RF24 IRQ usage requires transactional SPI support + #endif +#else + #ifdef MY_RX_MESSAGE_BUFFER_SIZE + #error Receive message buffering requires RF24 IRQ usage + #endif +#endif + +// pipes +#define BROADCAST_PIPE 1 +#define NODE_PIPE 2 + +// debug +#if defined(MY_DEBUG_VERBOSE_RF24) + #define RF24_DEBUG(x,...) debug(x, ##__VA_ARGS__) +#else + #define RF24_DEBUG(x,...) +#endif + +#ifdef LINUX_ARCH_RASPBERRYPI + // CE pin + #ifdef MY_RF24_CE_PIN + #ifdef __RPI_BPLUS + #if MY_RF24_CE_PIN == 3 + #define RF24_CE_PIN RPI_BPLUS_GPIO_J8_03 + #elif MY_RF24_CE_PIN == 5 + #define RF24_CE_PIN RPI_BPLUS_GPIO_J8_05 + #elif MY_RF24_CE_PIN == 7 + #define RF24_CE_PIN RPI_BPLUS_GPIO_J8_07 + #elif MY_RF24_CE_PIN == 8 + #define RF24_CE_PIN RPI_BPLUS_GPIO_J8_08 + #elif MY_RF24_CE_PIN == 10 + #define RF24_CE_PIN RPI_BPLUS_GPIO_J8_10 + #elif MY_RF24_CE_PIN == 11 + #define RF24_CE_PIN RPI_BPLUS_GPIO_J8_11 + #elif MY_RF24_CE_PIN == 12 + #define RF24_CE_PIN RPI_BPLUS_GPIO_J8_12 + #elif MY_RF24_CE_PIN == 13 + #define RF24_CE_PIN RPI_BPLUS_GPIO_J8_13 + #elif MY_RF24_CE_PIN == 15 + #define RF24_CE_PIN RPI_BPLUS_GPIO_J8_15 + #elif MY_RF24_CE_PIN == 16 + #define RF24_CE_PIN RPI_BPLUS_GPIO_J8_16 + #elif MY_RF24_CE_PIN == 18 + #define RF24_CE_PIN RPI_BPLUS_GPIO_J8_18 + #elif MY_RF24_CE_PIN == 19 + #define RF24_CE_PIN RPI_BPLUS_GPIO_J8_19 + #elif MY_RF24_CE_PIN == 21 + #define RF24_CE_PIN RPI_BPLUS_GPIO_J8_21 + #elif MY_RF24_CE_PIN == 22 + #define RF24_CE_PIN RPI_BPLUS_GPIO_J8_22 + #elif MY_RF24_CE_PIN == 23 + #define RF24_CE_PIN RPI_BPLUS_GPIO_J8_23 + #elif MY_RF24_CE_PIN == 24 + #define RF24_CE_PIN RPI_BPLUS_GPIO_J8_24 + #elif MY_RF24_CE_PIN == 26 + #define RF24_CE_PIN RPI_BPLUS_GPIO_J8_26 + #elif MY_RF24_CE_PIN == 29 + #define RF24_CE_PIN RPI_BPLUS_GPIO_J8_29 + #elif MY_RF24_CE_PIN == 31 + #define RF24_CE_PIN RPI_BPLUS_GPIO_J8_31 + #elif MY_RF24_CE_PIN == 32 + #define RF24_CE_PIN RPI_BPLUS_GPIO_J8_32 + #elif MY_RF24_CE_PIN == 33 + #define RF24_CE_PIN RPI_BPLUS_GPIO_J8_33 + #elif MY_RF24_CE_PIN == 35 + #define RF24_CE_PIN RPI_BPLUS_GPIO_J8_35 + #elif MY_RF24_CE_PIN == 36 + #define RF24_CE_PIN RPI_BPLUS_GPIO_J8_36 + #elif MY_RF24_CE_PIN == 37 + #define RF24_CE_PIN RPI_BPLUS_GPIO_J8_37 + #elif MY_RF24_CE_PIN == 38 + #define RF24_CE_PIN RPI_BPLUS_GPIO_J8_38 + #elif MY_RF24_CE_PIN == 40 + #define RF24_CE_PIN RPI_BPLUS_GPIO_J8_40 + #else + #error invalid value for MY_RF24_CE_PIN + #endif + #else + #if MY_RF24_CE_PIN == 3 + #define RF24_CE_PIN RPI_V2_GPIO_P1_03 + #elif MY_RF24_CE_PIN == 5 + #define RF24_CE_PIN RPI_V2_GPIO_P1_05 + #elif MY_RF24_CE_PIN == 7 + #define RF24_CE_PIN RPI_V2_GPIO_P1_07 + #elif MY_RF24_CE_PIN == 8 + #define RF24_CE_PIN RPI_V2_GPIO_P1_08 + #elif MY_RF24_CE_PIN == 10 + #define RF24_CE_PIN RPI_V2_GPIO_P1_10 + #elif MY_RF24_CE_PIN == 11 + #define RF24_CE_PIN RPI_V2_GPIO_P1_11 + #elif MY_RF24_CE_PIN == 12 + #define RF24_CE_PIN RPI_V2_GPIO_P1_12 + #elif MY_RF24_CE_PIN == 13 + #define RF24_CE_PIN RPI_V2_GPIO_P1_13 + #elif MY_RF24_CE_PIN == 15 + #define RF24_CE_PIN RPI_V2_GPIO_P1_15 + #elif MY_RF24_CE_PIN == 16 + #define RF24_CE_PIN RPI_V2_GPIO_P1_16 + #elif MY_RF24_CE_PIN == 18 + #define RF24_CE_PIN RPI_V2_GPIO_P1_18 + #elif MY_RF24_CE_PIN == 19 + #define RF24_CE_PIN RPI_V2_GPIO_P1_19 + #elif MY_RF24_CE_PIN == 21 + #define RF24_CE_PIN RPI_V2_GPIO_P1_21 + #elif MY_RF24_CE_PIN == 22 + #define RF24_CE_PIN RPI_V2_GPIO_P1_22 + #elif MY_RF24_CE_PIN == 23 + #define RF24_CE_PIN RPI_V2_GPIO_P1_23 + #elif MY_RF24_CE_PIN == 24 + #define RF24_CE_PIN RPI_V2_GPIO_P1_24 + #elif MY_RF24_CE_PIN == 26 + #define RF24_CE_PIN RPI_V2_GPIO_P1_26 + #elif MY_RF24_CE_PIN == 29 + #define RF24_CE_PIN RPI_V2_GPIO_P1_29 + #elif MY_RF24_CE_PIN == 31 + #define RF24_CE_PIN RPI_V2_GPIO_P1_31 + #elif MY_RF24_CE_PIN == 32 + #define RF24_CE_PIN RPI_V2_GPIO_P1_32 + #elif MY_RF24_CE_PIN == 33 + #define RF24_CE_PIN RPI_V2_GPIO_P1_33 + #elif MY_RF24_CE_PIN == 35 + #define RF24_CE_PIN RPI_V2_GPIO_P1_35 + #elif MY_RF24_CE_PIN == 36 + #define RF24_CE_PIN RPI_V2_GPIO_P1_36 + #elif MY_RF24_CE_PIN == 37 + #define RF24_CE_PIN RPI_V2_GPIO_P1_37 + #elif MY_RF24_CE_PIN == 38 + #define RF24_CE_PIN RPI_V2_GPIO_P1_38 + #elif MY_RF24_CE_PIN == 40 + #define RF24_CE_PIN RPI_V2_GPIO_P1_40 + #else + #error invalid value for MY_RF24_CE_PIN + #endif + #endif + #else + #ifdef __RPI_BPLUS + #define RF24_CE_PIN RPI_BPLUS_GPIO_J8_22 + #else + #define RF24_CE_PIN RPI_V2_GPIO_P1_22 + #endif + #endif + + // CS pin + #ifdef MY_RF24_CS_PIN + #ifdef __RPI_BPLUS + #if MY_RF24_CS_PIN == 3 + #define RF24_CS_PIN RPI_BPLUS_GPIO_J8_03 + #elif MY_RF24_CS_PIN == 5 + #define RF24_CS_PIN RPI_BPLUS_GPIO_J8_05 + #elif MY_RF24_CS_PIN == 7 + #define RF24_CS_PIN RPI_BPLUS_GPIO_J8_07 + #elif MY_RF24_CS_PIN == 8 + #define RF24_CS_PIN RPI_BPLUS_GPIO_J8_08 + #elif MY_RF24_CS_PIN == 10 + #define RF24_CS_PIN RPI_BPLUS_GPIO_J8_10 + #elif MY_RF24_CS_PIN == 11 + #define RF24_CS_PIN RPI_BPLUS_GPIO_J8_11 + #elif MY_RF24_CS_PIN == 12 + #define RF24_CS_PIN RPI_BPLUS_GPIO_J8_12 + #elif MY_RF24_CS_PIN == 13 + #define RF24_CS_PIN RPI_BPLUS_GPIO_J8_13 + #elif MY_RF24_CS_PIN == 15 + #define RF24_CS_PIN RPI_BPLUS_GPIO_J8_15 + #elif MY_RF24_CS_PIN == 16 + #define RF24_CS_PIN RPI_BPLUS_GPIO_J8_16 + #elif MY_RF24_CS_PIN == 18 + #define RF24_CS_PIN RPI_BPLUS_GPIO_J8_18 + #elif MY_RF24_CS_PIN == 19 + #define RF24_CS_PIN RPI_BPLUS_GPIO_J8_19 + #elif MY_RF24_CS_PIN == 21 + #define RF24_CS_PIN RPI_BPLUS_GPIO_J8_21 + #elif MY_RF24_CS_PIN == 22 + #define RF24_CS_PIN RPI_BPLUS_GPIO_J8_22 + #elif MY_RF24_CS_PIN == 23 + #define RF24_CS_PIN RPI_BPLUS_GPIO_J8_23 + #elif MY_RF24_CS_PIN == 24 + #define RF24_CS_PIN RPI_BPLUS_GPIO_J8_24 + #elif MY_RF24_CS_PIN == 26 + #define RF24_CS_PIN RPI_BPLUS_GPIO_J8_26 + #elif MY_RF24_CS_PIN == 29 + #define RF24_CS_PIN RPI_BPLUS_GPIO_J8_29 + #elif MY_RF24_CS_PIN == 31 + #define RF24_CS_PIN RPI_BPLUS_GPIO_J8_31 + #elif MY_RF24_CS_PIN == 32 + #define RF24_CS_PIN RPI_BPLUS_GPIO_J8_32 + #elif MY_RF24_CS_PIN == 33 + #define RF24_CS_PIN RPI_BPLUS_GPIO_J8_33 + #elif MY_RF24_CS_PIN == 35 + #define RF24_CS_PIN RPI_BPLUS_GPIO_J8_35 + #elif MY_RF24_CS_PIN == 36 + #define RF24_CS_PIN RPI_BPLUS_GPIO_J8_36 + #elif MY_RF24_CS_PIN == 37 + #define RF24_CS_PIN RPI_BPLUS_GPIO_J8_37 + #elif MY_RF24_CS_PIN == 38 + #define RF24_CS_PIN RPI_BPLUS_GPIO_J8_38 + #elif MY_RF24_CS_PIN == 40 + #define RF24_CS_PIN RPI_BPLUS_GPIO_J8_40 + #else + #error invalid value for MY_RF24_CS_PIN + #endif + #else + #if MY_RF24_CS_PIN == 3 + #define RF24_CS_PIN RPI_V2_GPIO_P1_03 + #elif MY_RF24_CS_PIN == 5 + #define RF24_CS_PIN RPI_V2_GPIO_P1_05 + #elif MY_RF24_CS_PIN == 7 + #define RF24_CS_PIN RPI_V2_GPIO_P1_07 + #elif MY_RF24_CS_PIN == 8 + #define RF24_CS_PIN RPI_V2_GPIO_P1_08 + #elif MY_RF24_CS_PIN == 10 + #define RF24_CS_PIN RPI_V2_GPIO_P1_10 + #elif MY_RF24_CS_PIN == 11 + #define RF24_CS_PIN RPI_V2_GPIO_P1_11 + #elif MY_RF24_CS_PIN == 12 + #define RF24_CS_PIN RPI_V2_GPIO_P1_12 + #elif MY_RF24_CS_PIN == 13 + #define RF24_CS_PIN RPI_V2_GPIO_P1_13 + #elif MY_RF24_CS_PIN == 15 + #define RF24_CS_PIN RPI_V2_GPIO_P1_15 + #elif MY_RF24_CS_PIN == 16 + #define RF24_CS_PIN RPI_V2_GPIO_P1_16 + #elif MY_RF24_CS_PIN == 18 + #define RF24_CS_PIN RPI_V2_GPIO_P1_18 + #elif MY_RF24_CS_PIN == 19 + #define RF24_CS_PIN RPI_V2_GPIO_P1_19 + #elif MY_RF24_CS_PIN == 21 + #define RF24_CS_PIN RPI_V2_GPIO_P1_21 + #elif MY_RF24_CS_PIN == 22 + #define RF24_CS_PIN RPI_V2_GPIO_P1_22 + #elif MY_RF24_CS_PIN == 23 + #define RF24_CS_PIN RPI_V2_GPIO_P1_23 + #elif MY_RF24_CS_PIN == 24 + #define RF24_CS_PIN BCM2835_SPI_CS0 + #elif MY_RF24_CS_PIN == 26 + #define RF24_CS_PIN BCM2835_SPI_CS1 + #elif MY_RF24_CS_PIN == 29 + #define RF24_CS_PIN RPI_V2_GPIO_P1_29 + #elif MY_RF24_CS_PIN == 31 + #define RF24_CS_PIN RPI_V2_GPIO_P1_31 + #elif MY_RF24_CS_PIN == 32 + #define RF24_CS_PIN RPI_V2_GPIO_P1_32 + #elif MY_RF24_CS_PIN == 33 + #define RF24_CS_PIN RPI_V2_GPIO_P1_33 + #elif MY_RF24_CS_PIN == 35 + #define RF24_CS_PIN RPI_V2_GPIO_P1_35 + #elif MY_RF24_CS_PIN == 36 + #define RF24_CS_PIN RPI_V2_GPIO_P1_36 + #elif MY_RF24_CS_PIN == 37 + #define RF24_CS_PIN RPI_V2_GPIO_P1_37 + #elif MY_RF24_CS_PIN == 38 + #define RF24_CS_PIN RPI_V2_GPIO_P1_38 + #elif MY_RF24_CS_PIN == 40 + #define RF24_CS_PIN RPI_V2_GPIO_P1_40 + #else + #error invalid value for MY_RF24_CS_PIN + #endif + #endif + #else + #ifdef __RPI_BPLUS + #define RF24_CS_PIN RPI_BPLUS_GPIO_J8_24 + #else + #define RF24_CS_PIN BCM2835_SPI_CS0 + #endif + #endif +#endif + +#endif diff --git a/examples_RPi/MySGateway.cpp b/examples_RPi/MySGateway.cpp deleted file mode 100644 index 8a7e9dd..0000000 --- a/examples_RPi/MySGateway.cpp +++ /dev/null @@ -1,91 +0,0 @@ -/** - * The MySensors Arduino library handles the wireless radio link and protocol - * between your home built sensors/actuators and HA controller of choice. - * The sensors forms a self healing radio network with optional repeaters. Each - * repeater and gateway builds a routing tables in EEPROM which keeps track of the - * network topology allowing messages to be routed to nodes. - * - * Created by Marcelo Aquino - * Copyleft (c) 2016, Marcelo Aquino - * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors - * - * Documentation: http://www.mysensors.org - * Support Forum: http://forum.mysensors.org - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - */ - -#include -#include -#include - -// Enable debug prints to monitor -#ifndef MY_DEBUG - #define MY_DEBUG -#endif - -// Config file -#ifndef MY_LINUX_CONFIG_FILE - #define MY_LINUX_CONFIG_FILE "/etc/MySensorGateway.cfg" -#endif - -// Enables and select radio type (if attached) -#ifndef MY_RADIO_NRF24 - #define MY_RADIO_NRF24 -#endif - -#define MY_GATEWAY_RASPBERRYPI - -// How many clients should be able to connect to this gateway (default 1) -#define MY_GATEWAY_MAX_CLIENTS 10 - -#ifdef MY_GATEWAY_MQTT_CLIENT - // Set MQTT client id - #ifndef MY_MQTT_CLIENT_ID - #define MY_MQTT_CLIENT_ID "mysensors-1" - #endif - // MQTT broker ip address. - #ifndef MY_CONTROLLER_IP_ADDRESS - #define MY_CONTROLLER_IP_ADDRESS 192, 168, 178, 68 - #endif - // The MQTT broker port to to open - #ifndef MY_PORT - #define MY_PORT 1883 - #endif - - // Enable these if your MQTT broker requires usenrame/password - //#define MY_MQTT_USER "username" - //#define MY_MQTT_PASSWORD "password" - - // Set this node's subscribe and publish topic prefix - #ifndef MY_MQTT_PUBLISH_TOPIC_PREFIX - #define MY_MQTT_PUBLISH_TOPIC_PREFIX "mygateway1-out" - #endif - #ifndef MY_MQTT_SUBSCRIBE_TOPIC_PREFIX - #define MY_MQTT_SUBSCRIBE_TOPIC_PREFIX "mygateway1-in" - #endif -#else - // Enable MY_IP_ADDRESS here if you want to listen for incoming network - // connections on the specified IP address/hostname only - //#define MY_IP_ADDRESS 192,168,178,87 - - // The port to keep open on node server mode - #ifndef MY_PORT - #define MY_PORT 5003 - #endif -#endif - -#include - -void setup() { -} - -void presentation() { - // Present locally attached sensors here -} - -void loop() { - // Send locally attached sensors data here -} diff --git a/examples_linux/mysGateway.cpp b/examples_linux/mysGateway.cpp new file mode 100644 index 0000000..b53ffcc --- /dev/null +++ b/examples_linux/mysGateway.cpp @@ -0,0 +1,55 @@ +/** + * The MySensors Arduino library handles the wireless radio link and protocol + * between your home built sensors/actuators and HA controller of choice. + * The sensors forms a self healing radio network with optional repeaters. Each + * repeater and gateway builds a routing tables in EEPROM which keeps track of the + * network topology allowing messages to be routed to nodes. + * + * Created by Marcelo Aquino + * Copyleft (c) 2016, Marcelo Aquino + * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors + * + * Documentation: http://www.mysensors.org + * Support Forum: http://forum.mysensors.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + */ + +#include +#include +#include + +// For more options run ./configure --help + +// Config file +//#define MY_LINUX_CONFIG_FILE "/etc/MySensorGateway.cfg" + +// How many clients should be able to connect to this gateway (default 1) +#define MY_GATEWAY_MAX_CLIENTS 10 + +// 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" + + +#include + +void setup() { +} + +void presentation() { + // Present locally attached sensors here +} + +void loop() { + // Send locally attached sensors data here +} diff --git a/initscripts/mysgateway.systemd b/initscripts/mysgateway.systemd index 9763c66..5b707c6 100644 --- a/initscripts/mysgateway.systemd +++ b/initscripts/mysgateway.systemd @@ -3,7 +3,7 @@ Description=MySensors Gateway daemon Requires=network.target [Service] -ExecStart=%gateway_dir%/MySGateway +ExecStart=%gateway_dir%/mysGateway [Install] WantedBy=multi-user.target diff --git a/initscripts/mysgateway.sysvinit b/initscripts/mysgateway.sysvinit index df01c6c..34ee0c4 100644 --- a/initscripts/mysgateway.sysvinit +++ b/initscripts/mysgateway.sysvinit @@ -1,6 +1,6 @@ #! /bin/sh ### BEGIN INIT INFO -# Provides: MySGateway +# Provides: mysGateway # Required-Start: $remote_fs $syslog # Required-Stop: $remote_fs $syslog # Default-Start: 2 3 4 5 @@ -20,7 +20,7 @@ # 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 +NAME=mysGateway DAEMON=%gateway_dir%/$NAME DAEMON_ARGS="-d" PIDFILE=/var/run/$NAME.pid From cb8c98431eaed08822452efb318a50276ee49f6f Mon Sep 17 00:00:00 2001 From: Marcelo Aquino Date: Fri, 19 Aug 2016 13:45:51 -0300 Subject: [PATCH 21/33] Fix compilation errors for MQTT gateway --- MyConfig.h | 16 ++++------ configure | 8 ++--- core/MyHwLinuxGeneric.cpp | 56 ++++++++++----------------------- core/MyHwLinuxGeneric.h | 18 +++++------ drivers/Linux/Arduino.h | 1 + drivers/Linux/compatibility.cpp | 17 ++++++++++ 6 files changed, 53 insertions(+), 63 deletions(-) diff --git a/MyConfig.h b/MyConfig.h index 3834b5f..84a5b1c 100644 --- a/MyConfig.h +++ b/MyConfig.h @@ -616,7 +616,11 @@ * @brief The Ethernet TCP/UDP port to open on controller or gateway. */ #ifndef MY_PORT -#define MY_PORT 5003 + #ifdef MY_GATEWAY_MQTT_CLIENT + #define MY_PORT 1883 + #else + #define MY_PORT 5003 + #endif #endif // Static ip address of gateway (if this is disabled, DHCP will be used) @@ -718,15 +722,7 @@ * @brief Set the name of predictable tty */ #ifndef MY_LINUX_TTY_NAME -#define MY_LINUX_TTY_NAME "/dev/ttyMySensorsGateway" -#endif - -/** - * @def MY_LINUX_TTY_GROUPNAME - * @brief Set the group name for the raw tty - */ -#ifndef MY_LINUX_TTY_GROUPNAME -#define MY_LINUX_TTY_GROUPNAME tty +#define MY_LINUX_TTY_NAME "/dev/ttyAMA0" #endif /** diff --git a/configure b/configure index 7ad5629..dc3012c 100755 --- a/configure +++ b/configure @@ -267,13 +267,13 @@ for opt do CXXFLAGS="-DMY_PORT=${optarg} $CXXFLAGS" ;; --my-mqtt-client-id=*) - CXXFLAGS="-DMY_MQTT_CLIENT_ID=${optarg} $CXXFLAGS" + CXXFLAGS="-DMY_MQTT_CLIENT_ID=\\\"${optarg}\\\" $CXXFLAGS" ;; --my-mqtt-publish-topic-prefix=*) - CXXFLAGS="-DMY_MQTT_PUBLISH_TOPIC_PREFIX=${optarg} $CXXFLAGS" + CXXFLAGS="-DMY_MQTT_PUBLISH_TOPIC_PREFIX=\\\"${optarg}\\\" $CXXFLAGS" ;; --my-mqtt-subscribe-topic-prefix=*) - CXXFLAGS="-DMY_MQTT_SUBSCRIBE_TOPIC_PREFIX=${optarg} $CXXFLAGS" + CXXFLAGS="-DMY_MQTT_SUBSCRIBE_TOPIC_PREFIX=\\\"${optarg}\\\" $CXXFLAGS" ;; --my-rf24-irq-pin=*) CXXFLAGS="-DMY_RX_MESSAGE_BUFFER_FEATURE -DMY_RF24_IRQ_PIN=${optarg} $CXXFLAGS" @@ -319,7 +319,7 @@ if [[ ${gateway_type} == "ethernet" ]]; then CXXFLAGS="-DMY_GATEWAY_LINUX $CXXFLAGS" elif [[ ${gateway_type} == "mqtt" ]]; then LDFLAGS="-lmosquitto $LDFLAGS" - CXXFLAGS="-DMY_GATEWAY_MQTT_CLIENT $CXXFLAGS" + CXXFLAGS="-DMY_GATEWAY_LINUX -DMY_GATEWAY_MQTT_CLIENT $CXXFLAGS" fi if [[ ${radio_type} == "nrf24" ]]; then diff --git a/core/MyHwLinuxGeneric.cpp b/core/MyHwLinuxGeneric.cpp index e288bd6..ee74731 100644 --- a/core/MyHwLinuxGeneric.cpp +++ b/core/MyHwLinuxGeneric.cpp @@ -19,7 +19,6 @@ #include "MyHwLinuxGeneric.h" -#include #include #include #include @@ -28,60 +27,47 @@ static const char* CONFIG_FILE = MY_LINUX_CONFIG_FILE; static const size_t _length = 1024; // ATMega328 has 1024 bytes -static uint8_t _config[_length]; -static unsigned long millis_at_start; +static uint8_t _config[_length]; bool CheckConfigFile() { struct stat fileInfo; - - if(stat(CONFIG_FILE, &fileInfo) != 0) - { + + if (stat(CONFIG_FILE, &fileInfo) != 0) { //File does not exist. Create it. debug("Config file %s does not exist, creating new config file.\n", CONFIG_FILE); ofstream myFile(CONFIG_FILE, ios::out | ios::binary); - if(!myFile) - { + 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) - { + } 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 - { + } else { //Read config into local memory. ifstream myFile(CONFIG_FILE, ios::in | ios::binary); - if(!myFile) - { + if (!myFile) { debug("Unable to open config to file %s for reading.\n", CONFIG_FILE); return false; } myFile.read((char*)_config, _length); myFile.close(); } - + return true; } void hwInit() { - timeval curTime; - - for (size_t i = 0; i < _length; i++) + for (size_t i = 0; i < _length; i++) { _config[i] = 0xFF; + } - if (!CheckConfigFile()) - { + if (!CheckConfigFile()) { exit(1); } - - gettimeofday(&curTime, NULL); - millis_at_start = curTime.tv_sec; } void hwReadConfigBlock(void* buf, void* addr, size_t length) @@ -101,8 +87,7 @@ void hwWriteConfigBlock(void* buf, void* addr, size_t length) memcpy(_config+offs, buf, length); ofstream myFile(CONFIG_FILE, ios::out | ios::binary); - if(!myFile) - { + if (!myFile) { debug("Unable to write config to file %s.\n", CONFIG_FILE); return; } @@ -121,21 +106,12 @@ uint8_t hwReadConfig(int adr) void hwWriteConfig(int adr, uint8_t value) { uint8_t curr = hwReadConfig(adr); - if (curr != value) - { + if (curr != value) { hwWriteConfigBlock(&value, reinterpret_cast(adr), 1); } } -unsigned long hwMillis() -{ - timeval curTime; - - gettimeofday(&curTime, NULL); - return ((curTime.tv_sec - millis_at_start) * 1000) + (curTime.tv_usec / 1000); -} - -// TODO: Not supported! +// Not supported! int8_t hwSleep(unsigned long ms) { (void)ms; @@ -143,7 +119,7 @@ int8_t hwSleep(unsigned long ms) return -2; } -// TODO: Not supported! +// Not supported! int8_t hwSleep(uint8_t interrupt, uint8_t mode, unsigned long ms) { (void)interrupt; @@ -153,7 +129,7 @@ int8_t hwSleep(uint8_t interrupt, uint8_t mode, unsigned long ms) return -2; } -// TODO: Not supported! +// Not supported! int8_t hwSleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2, unsigned long ms) { (void)interrupt1; diff --git a/core/MyHwLinuxGeneric.h b/core/MyHwLinuxGeneric.h index c44d912..1d763ce 100644 --- a/core/MyHwLinuxGeneric.h +++ b/core/MyHwLinuxGeneric.h @@ -30,25 +30,25 @@ #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); void hwWriteConfig(int adr, uint8_t value); uint8_t hwReadConfig(int adr); -unsigned long hwMillis(); #ifdef MY_RF24_IRQ_PIN -static pthread_mutex_t hw_mutex = PTHREAD_MUTEX_INITIALIZER; + static 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 __hwUnlock(const uint8_t *__s) { + pthread_mutex_unlock(&hw_mutex); + (void)__s; + } -static __inline__ void __hwLock() { - pthread_mutex_lock(&hw_mutex); -} + static __inline__ void __hwLock() { + pthread_mutex_lock(&hw_mutex); + } #endif #if defined(DOXYGEN) diff --git a/drivers/Linux/Arduino.h b/drivers/Linux/Arduino.h index e928b04..08e9701 100644 --- a/drivers/Linux/Arduino.h +++ b/drivers/Linux/Arduino.h @@ -51,6 +51,7 @@ char *ltoa(long value, char* result, int base); char *ultoa(long num, char *str, int radix); char *utoa(int num, char *str, int radix); char *dtostrf(float f, int width, int decimals, char *result); +unsigned long millis(void); void delay(unsigned int millis); #ifdef __cplusplus diff --git a/drivers/Linux/compatibility.cpp b/drivers/Linux/compatibility.cpp index 5c1e5fe..ff76cc5 100644 --- a/drivers/Linux/compatibility.cpp +++ b/drivers/Linux/compatibility.cpp @@ -1,7 +1,11 @@ #include #include +#include #include "Arduino.h" +// For millis() +static unsigned long millis_at_start = 0; + /** * C++ version 0.4 char* style "itoa": * Written by Lukás Chmela @@ -177,6 +181,19 @@ char *dtostrf(float f, int width, int decimals, char *result) return result; } +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(unsigned int millis) { struct timespec sleeper; From 412e41967fa61e17ace383b74da98715100ebbe5 Mon Sep 17 00:00:00 2001 From: Marcelo Aquino Date: Sun, 21 Aug 2016 19:56:46 -0300 Subject: [PATCH 22/33] Add support for serial gateway to Rpi --- MyConfig.h | 10 +- MySensors.h | 8 +- configure | 26 +++- core/MyHwLinuxGeneric.cpp | 12 +- core/MyHwLinuxGeneric.h | 6 +- core/MySensorsCore.cpp | 4 +- core/MyTransportNRF24.cpp | 4 +- drivers/Linux/Arduino.h | 51 +++---- drivers/Linux/Client.h | 1 + drivers/Linux/EthernetClient.h | 2 +- drivers/Linux/Print.cpp | 238 +++++++++++++++++++++++++++++ drivers/Linux/Print.h | 76 ++++++++++ drivers/Linux/SerialPort.cpp | 159 ++++++++++++++++++++ drivers/Linux/SerialPort.h | 100 +++++++++++++ drivers/Linux/Server.h | 4 +- drivers/Linux/Stream.cpp | 258 ++++++++++++++++++++++++++++++++ drivers/Linux/Stream.h | 121 +++++++++++++-- drivers/Linux/compatibility.cpp | 201 ++++--------------------- drivers/Linux/noniso.cpp | 147 ++++++++++++++++++ drivers/Linux/stdlib_noniso.h | 52 +++++++ 20 files changed, 1241 insertions(+), 239 deletions(-) create mode 100644 drivers/Linux/Print.cpp create mode 100644 drivers/Linux/Print.h create mode 100644 drivers/Linux/SerialPort.cpp create mode 100644 drivers/Linux/SerialPort.h create mode 100644 drivers/Linux/Stream.cpp create mode 100644 drivers/Linux/noniso.cpp create mode 100644 drivers/Linux/stdlib_noniso.h diff --git a/MyConfig.h b/MyConfig.h index 84a5b1c..b8e0517 100644 --- a/MyConfig.h +++ b/MyConfig.h @@ -410,7 +410,7 @@ #define MY_RF24_CE_PIN 4 #elif defined(ARDUINO_ARCH_SAMD) #define MY_RF24_CE_PIN 27 - #elif defined(LINUX) + #elif defined(__linux__) #define MY_RF24_CE_PIN 22 #else #define MY_RF24_CE_PIN 9 @@ -426,7 +426,7 @@ #define MY_RF24_CS_PIN 15 #elif defined(ARDUINO_ARCH_SAMD) #define MY_RF24_CS_PIN 3 - #elif defined(LINUX) + #elif defined(__linux__) #define MY_RF24_CS_PIN 24 #else #define MY_RF24_CS_PIN 10 @@ -718,11 +718,11 @@ ***************************************/ /** - * @def MY_LINUX_TTY_NAME + * @def MY_LINUX_SERIAL_PORT * @brief Set the name of predictable tty */ -#ifndef MY_LINUX_TTY_NAME -#define MY_LINUX_TTY_NAME "/dev/ttyAMA0" +#ifndef MY_LINUX_SERIAL_PORT +#define MY_LINUX_SERIAL_PORT "/dev/ttyUSB0" #endif /** diff --git a/MySensors.h b/MySensors.h index d4c7a5a..77e36ac 100644 --- a/MySensors.h +++ b/MySensors.h @@ -69,7 +69,7 @@ #include "core/MyHwATMega328.cpp" #elif defined(ARDUINO_ARCH_SAMD) #include "core/MyHwSAMD.cpp" -#elif defined(LINUX) +#elif defined(__linux__) // Remove PSTR macros from debug prints #undef PSTR #define PSTR(x) (x) @@ -284,7 +284,9 @@ #if defined(MY_RF24_ENABLE_ENCRYPTION) #include "drivers/AES/AES.cpp" #endif - #if !defined(LINUX) + #if defined(__linux__) + #include "drivers/RF24/RF24_Linux.cpp" + #else #include "drivers/RF24/RF24.cpp" #endif #include "core/MyTransportNRF24.cpp" @@ -329,7 +331,7 @@ #if !defined(MY_CORE_ONLY) #if defined(ARDUINO_ARCH_ESP8266) #include "core/MyMainESP8266.cpp" - #elif defined(LINUX) + #elif defined(__linux__) #include "core/MyMainLinux.cpp" #else #include "core/MyMainDefault.cpp" diff --git a/configure b/configure index dc3012c..8df6e97 100755 --- a/configure +++ b/configure @@ -35,15 +35,10 @@ MySensors options: --my-debug=[enable|disable] Enables or disables debugging. Default to enable. - --my-gateway=[none|ethernet|mqtt] + --my-config-file= Config file path. [/etc/MySensorGateway.cfg] + --my-gateway=[none|ethernet|serial|mqtt] Gateway type, set to none to disable gateway feature. Default to ethernet. - --my-config-file= Config file path. [/etc/MySensorGateway.cfg] - --my-radio=[none|nrf24] Radio type, set to none to disable radio feature. - Default to rf24. - --my-rf24-channel=<0-125> RF channel for the sensor net, 0-125. - --my-rf24-pa-level=[RF24_PA_MAX|RF24_PA_LOW] - RF24 PA level. --my-controller-url-address= Controller or MQTT broker url. --my-controller-ip-address= @@ -51,11 +46,18 @@ MySensors options: Use commas instead of points. Example: 127,0,0,1 --my-port= The port to keep open on gateway mode. If gateway is set to mqtt, it sets the broker port. + --my-serial-port= Serial port. [/dev/ttyUSB0] + --my-serial-baudrate= Serial baud rate. [115200] --my-mqtt-client-id= MQTT client id. --my-mqtt-publish-topic-prefix= MQTT publish topic prefix. --my-mqtt-subscribe-topic-prefix= MQTT subscribe topic prefix. + --my-radio=[none|nrf24] Radio type, set to none to disable radio feature. + Default to rf24. + --my-rf24-channel=<0-125> RF channel for the sensor net, 0-125. + --my-rf24-pa-level=[RF24_PA_MAX|RF24_PA_LOW] + RF24 PA level. --my-rf24-irq-pin= Pin number connected to nRF24L01 IRQ pin. For RaspberryPi use BCM numbering. --my-rx-message-buffer-size= @@ -251,6 +253,12 @@ for opt do --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-rf24-channel=*) CXXFLAGS="-DMY_RF24_CHANNEL=${optarg} $CXXFLAGS" ;; @@ -305,7 +313,7 @@ if [ -z "${CPUFLAGS}" ]; then CPUFLAGS=$(gcc_cpu_flags $SOC) fi -CXXFLAGS="$CPUFLAGS -Ofast -g -Wall -Wextra -DLINUX $CXXFLAGS" +CXXFLAGS="$CPUFLAGS -Ofast -g -Wall -Wextra $CXXFLAGS" if [[ $TYPE == "RPi2" || $TYPE == "RPi3" || $REV == "0010" ]]; then CXXFLAGS+="-D__RPI_BPLUS" @@ -317,6 +325,8 @@ 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 LDFLAGS="-lmosquitto $LDFLAGS" CXXFLAGS="-DMY_GATEWAY_LINUX -DMY_GATEWAY_MQTT_CLIENT $CXXFLAGS" diff --git a/core/MyHwLinuxGeneric.cpp b/core/MyHwLinuxGeneric.cpp index ee74731..bf48457 100644 --- a/core/MyHwLinuxGeneric.cpp +++ b/core/MyHwLinuxGeneric.cpp @@ -30,12 +30,12 @@ static const size_t _length = 1024; // ATMega328 has 1024 bytes static uint8_t _config[_length]; bool CheckConfigFile() { - struct stat fileInfo; + struct stat fileInfo; if (stat(CONFIG_FILE, &fileInfo) != 0) { //File does not exist. Create it. debug("Config file %s does not exist, creating new config file.\n", CONFIG_FILE); - ofstream myFile(CONFIG_FILE, ios::out | ios::binary); + 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; @@ -47,7 +47,7 @@ bool CheckConfigFile() { return false; } else { //Read config into local memory. - ifstream myFile(CONFIG_FILE, ios::in | ios::binary); + 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; @@ -68,6 +68,10 @@ void hwInit() if (!CheckConfigFile()) { exit(1); } + + #ifdef MY_GATEWAY_SERIAL + MY_SERIALDEVICE.begin(MY_BAUD_RATE); + #endif } void hwReadConfigBlock(void* buf, void* addr, size_t length) @@ -86,7 +90,7 @@ void hwWriteConfigBlock(void* buf, void* addr, size_t length) if (length && offs + length <= _length) { memcpy(_config+offs, buf, length); - ofstream myFile(CONFIG_FILE, ios::out | ios::binary); + std::ofstream myFile(CONFIG_FILE, std::ios::out | std::ios::binary); if (!myFile) { debug("Unable to write config to file %s.\n", CONFIG_FILE); return; diff --git a/core/MyHwLinuxGeneric.h b/core/MyHwLinuxGeneric.h index 1d763ce..0d3e49b 100644 --- a/core/MyHwLinuxGeneric.h +++ b/core/MyHwLinuxGeneric.h @@ -20,10 +20,12 @@ #ifndef MyHwLinuxGeneric_h #define MyHwLinuxGeneric_h -#include "MyHw.h" #include #include +#include "MyHw.h" +#include "SerialPort.h" +SerialPort Serial = SerialPort(MY_LINUX_SERIAL_PORT); #define MY_SERIALDEVICE Serial // Define these as macros (do nothing) @@ -35,8 +37,8 @@ void hwInit(); void hwReadConfigBlock(void* buf, void* adr, size_t length); void hwWriteConfigBlock(void* buf, void* adr, size_t length); -void hwWriteConfig(int adr, uint8_t value); uint8_t hwReadConfig(int adr); +void hwWriteConfig(int adr, uint8_t value); #ifdef MY_RF24_IRQ_PIN static pthread_mutex_t hw_mutex = PTHREAD_MUTEX_INITIALIZER; diff --git a/core/MySensorsCore.cpp b/core/MySensorsCore.cpp index 2d0a9db..836f817 100644 --- a/core/MySensorsCore.cpp +++ b/core/MySensorsCore.cpp @@ -51,7 +51,7 @@ void _process() { transportProcess(); #endif - #if defined(LINUX) + #if defined(__linux__) // To avoid high cpu usage usleep(10000); // 10ms #endif @@ -65,7 +65,7 @@ void _infiniteLoop() { #if defined (MY_LEDS_BLINKING_FEATURE) ledsProcess(); #endif - #if defined(LINUX) + #if defined(__linux__) exit(1); #endif } diff --git a/core/MyTransportNRF24.cpp b/core/MyTransportNRF24.cpp index ce39008..cdd3f2b 100644 --- a/core/MyTransportNRF24.cpp +++ b/core/MyTransportNRF24.cpp @@ -20,8 +20,8 @@ #include "MyConfig.h" #include "MyTransport.h" -#ifdef LINUX_ARCH_RASPBERRYPI - #include "drivers/RF24/RF24_Linux.cpp" +#ifdef __linux__ + #include "drivers/RF24/RF24_Linux.h" #else #include "drivers/RF24/RF24.h" #endif diff --git a/drivers/Linux/Arduino.h b/drivers/Linux/Arduino.h index 08e9701..e1dc088 100644 --- a/drivers/Linux/Arduino.h +++ b/drivers/Linux/Arduino.h @@ -1,18 +1,17 @@ #ifndef Arduino_h #define Arduino_h -#include +#include #include - -#ifdef __cplusplus - #include - - using namespace std; - - extern "C" { -#endif - -void yield(void); +#include +#include +#include +#include +#include +#include +#include +#include +#include "stdlib_noniso.h" #define PI 3.1415926535897932384626433832795 #define HALF_PI 1.5707963267948966192313216916398 @@ -21,17 +20,11 @@ void yield(void); #define RAD_TO_DEG 57.295779513082320876798154814105 #define EULER 2.718281828459045235360287471352 -#ifndef __cplusplus - #define min(a,b) ((a)<(b)?(a):(b)) - #define max(a,b) ((a)>(b)?(a):(b)) - #define abs(x) ((x)>0?(x):-(x)) - #define round(x) ((x)>=0?(long)((x)+0.5):(long)((x)-0.5)) -#endif - #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)) @@ -43,19 +36,23 @@ void yield(void); #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__) + +using std::string; +using std::min; +using std::max; +using std::abs; + typedef bool boolean; typedef uint8_t byte; +typedef string String; -char *itoa(int value, char* result, int base); -char *ltoa(long value, char* result, int base); -char *ultoa(long num, char *str, int radix); -char *utoa(int num, char *str, int radix); -char *dtostrf(float f, int width, int decimals, char *result); +void yield(void); unsigned long millis(void); void delay(unsigned int millis); - -#ifdef __cplusplus - } -#endif +void randomSeed(unsigned long seed); +long randMax(long howbig); +long randMinMax(long howsmall, long howbig); #endif diff --git a/drivers/Linux/Client.h b/drivers/Linux/Client.h index 067b57d..4f38ac2 100644 --- a/drivers/Linux/Client.h +++ b/drivers/Linux/Client.h @@ -21,6 +21,7 @@ #ifndef client_h #define client_h + #include "Stream.h" #include "IPAddress.h" diff --git a/drivers/Linux/EthernetClient.h b/drivers/Linux/EthernetClient.h index f21f5d3..78dc143 100644 --- a/drivers/Linux/EthernetClient.h +++ b/drivers/Linux/EthernetClient.h @@ -50,7 +50,7 @@ class EthernetClient : public Client { private: - int _sock; + int _sock; //!< @brief Network socket. public: /** diff --git a/drivers/Linux/Print.cpp b/drivers/Linux/Print.cpp new file mode 100644 index 0000000..306b82c --- /dev/null +++ b/drivers/Linux/Print.cpp @@ -0,0 +1,238 @@ +/* + 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 for MySensors use + */ + +#include +#include +#include +#include +#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 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; +} diff --git a/drivers/Linux/Print.h b/drivers/Linux/Print.h new file mode 100644 index 0000000..b63e7a1 --- /dev/null +++ b/drivers/Linux/Print.h @@ -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 for MySensors use + */ + +#ifndef Print_h +#define Print_h + +#include +#include +#include + +#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 diff --git a/drivers/Linux/SerialPort.cpp b/drivers/Linux/SerialPort.cpp new file mode 100644 index 0000000..732d102 --- /dev/null +++ b/drivers/Linux/SerialPort.cpp @@ -0,0 +1,159 @@ +/* + * The MySensors Arduino library handles the wireless radio link and protocol + * between your home built sensors/actuators and HA controller of choice. + * The sensors forms a self healing radio network with optional repeaters. Each + * repeater and gateway builds a routing tables in EEPROM which keeps track of the + * network topology allowing messages to be routed to nodes. + * + * Created by Marcelo Aquino + * Copyright (C) 2016 Marcelo Aquino + * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors + * + * Documentation: http://www.mysensors.org + * Support Forum: http://forum.mysensors.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "SerialPort.h" + +#define SERIAL_PORT "/dev/ttyAMA0" + +SerialPort::SerialPort() +{ + SerialPort(SERIAL_PORT); +} + +SerialPort::SerialPort(const char *port) : serialPort(std::string(port)) +{} + +void SerialPort::begin(int bauds) +{ + speed_t speed; + struct termios options; + + if ((sd = open(serialPort.c_str(), O_RDWR | O_NOCTTY | O_NDELAY)) == -1) { + fprintf(stderr, "Unable to open the serial port %s - \n", serialPort.c_str()); + exit(-1); + } + + // 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) { + perror("Couldn't get term attributes"); + exit(1); + } + + // make raw + cfmakeraw(&options); + + // Set the baud rate + cfsetispeed(&options, speed); + cfsetospeed(&options, speed); + + // turn on READ & ignore ctrl lines + options.c_cflag |= (CLOCAL | CREAD); + // 8N1 + options.c_cflag &= ~CSTOPB; + + options.c_lflag &= ~ECHOE; + + // 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) { + perror("Couldn't set term attributes"); + exit(1); + } + + // flush + if (tcflush(sd, TCIOFLUSH) < 0) { + perror("Couldn't flush serial"); + exit(1); + } + + usleep(10000); +} + +int SerialPort::available() +{ + int nbytes = 0; + + if (ioctl(sd, FIONREAD, &nbytes) < 0) { + fprintf(stderr, "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); +} diff --git a/drivers/Linux/SerialPort.h b/drivers/Linux/SerialPort.h new file mode 100644 index 0000000..e344c6d --- /dev/null +++ b/drivers/Linux/SerialPort.h @@ -0,0 +1,100 @@ +/* + * The MySensors Arduino library handles the wireless radio link and protocol + * between your home built sensors/actuators and HA controller of choice. + * The sensors forms a self healing radio network with optional repeaters. Each + * repeater and gateway builds a routing tables in EEPROM which keeps track of the + * network topology allowing messages to be routed to nodes. + * + * Created by Marcelo Aquino + * Copyright (C) 2016 Marcelo Aquino + * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors + * + * Documentation: http://www.mysensors.org + * Support Forum: http://forum.mysensors.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + */ + +#ifndef SerialPort_h +#define SerialPort_h + +#include +#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. + +public: + /** + * @brief SerialPort constructor. + */ + SerialPort(); + /** + * @brief SerialPort constructor. + */ + SerialPort(const char *port); + /** + * @brief Sets the data rate in bits per second (baud) for serial data transmission. + * + * @param serialSpeed serial port speed. + */ + void begin(int serialSpeed); + /** + * @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 diff --git a/drivers/Linux/Server.h b/drivers/Linux/Server.h index 7910e83..c637f7a 100644 --- a/drivers/Linux/Server.h +++ b/drivers/Linux/Server.h @@ -20,8 +20,10 @@ #ifndef server_h #define server_h +#include "Print.h" + #if !DOXYGEN -class Server { +class Server : public Print { public: virtual void begin() =0; }; diff --git a/drivers/Linux/Stream.cpp b/drivers/Linux/Stream.cpp new file mode 100644 index 0000000..1817083 --- /dev/null +++ b/drivers/Linux/Stream.cpp @@ -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 for MySensors use + */ + +#include +#include + +#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; +} diff --git a/drivers/Linux/Stream.h b/drivers/Linux/Stream.h index 226543b..dff2074 100644 --- a/drivers/Linux/Stream.h +++ b/drivers/Linux/Stream.h @@ -1,16 +1,119 @@ +/* + 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 for MySensors use + */ + #ifndef Stream_h #define Stream_h -#if !DOXYGEN -class Stream { - public: - virtual size_t write(uint8_t) = 0; - virtual size_t write(const uint8_t *buffer, size_t size) = 0; +#include +#include +#include "Print.h" - virtual int available() = 0; - virtual int read() = 0; - virtual int peek() = 0; - virtual void flush() = 0; +// 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 diff --git a/drivers/Linux/compatibility.cpp b/drivers/Linux/compatibility.cpp index ff76cc5..a0a0eb3 100644 --- a/drivers/Linux/compatibility.cpp +++ b/drivers/Linux/compatibility.cpp @@ -1,185 +1,12 @@ -#include #include #include +#include #include "Arduino.h" // For millis() static unsigned long millis_at_start = 0; -/** -* C++ version 0.4 char* style "itoa": -* Written by Lukás Chmela -* Released under GPLv3. -*/ -char *itoa(int value, char* result, int base) { - // check that the base if valid - if (base < 2 || base > 36) { *result = '\0'; return result; } - - char* ptr = result, *ptr1 = result, tmp_char; - int tmp_value; - - do { - tmp_value = value; - value /= base; - *ptr++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz" [35 + (tmp_value - value * base)]; - } while ( value ); - - // Apply negative sign - // Apply negative sign - if (tmp_value < 0) *ptr++ = '-'; - *ptr-- = '\0'; - while(ptr1 < ptr) { - tmp_char = *ptr; - *ptr--= *ptr1; - *ptr1++ = tmp_char; - } - return result; -} - -/** - * C++ version 0.4 char* style "itoa": - * Written by Lukás Chmela - * Released under GPLv3. - */ -char *ltoa(long value, char* result, int base) { - // check that the base if valid - if (base < 2 || base > 36) { *result = '\0'; return result; } - - char* ptr = result, *ptr1 = result, tmp_char; - long tmp_value; - - do { - tmp_value = value; - value /= base; - *ptr++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz" [35 + (tmp_value - value * base)]; - } while ( value ); - - // Apply negative sign - if (tmp_value < 0) *ptr++ = '-'; - *ptr-- = '\0'; - while(ptr1 < ptr) { - tmp_char = *ptr; - *ptr--= *ptr1; - *ptr1++ = tmp_char; - } - return result; -} - -char *ultoa(long num, char *str, int radix) -{ - unsigned long value; - char *sp = str; - char *sp2; - - value = num; - - /* Store sign at start of buffer for negative base-10 values */ - if (10 == radix && 0 > num) { - *sp++ = '-'; - value = -num; - } - - sp2 = sp; - - do { - char rem = value % radix; - value /= radix; - if (10 > rem) { - *sp++ = '0' + rem; - } else { - *sp++ = 'A' + rem - 10; - } - } while (0 < value); - - /* Mark end of string */ - *sp-- = 0; - - /* Reverse string contents (excluding sign) in place */ - while (sp2 < sp) { - char tmp = *sp2; - *sp2++ = *sp; - *sp-- = tmp; - } - - return str; -} - -/** - * Copyright (c) 2012, Peter A. Bigot - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * * Neither the name of the software nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -char *utoa(int num, char *str, int radix) -{ - unsigned int value; - char *sp = str; - char *sp2; - - value = num; - - /* Store sign at start of buffer for negative base-10 values */ - if (10 == radix && 0 > num) { - *sp++ = '-'; - value = -num; - } - - sp2 = sp; - - do { - char rem = value % radix; - value /= radix; - if (10 > rem) { - *sp++ = '0' + rem; - } else { - *sp++ = 'A' + rem - 10; - } - } while (0 < value); - - /* Mark end of string */ - *sp-- = 0; - - /* Reverse string contents (excluding sign) in place */ - while (sp2 < sp) { - char tmp = *sp2; - *sp2++ = *sp; - *sp-- = tmp; - } - - return str; -} - -char *dtostrf(float f, int width, int decimals, char *result) -{ - sprintf(result,"%*.*f", width, decimals, f); - return result; -} +void yield(void) {} unsigned long millis(void) { @@ -202,3 +29,27 @@ void delay(unsigned int millis) 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; +} diff --git a/drivers/Linux/noniso.cpp b/drivers/Linux/noniso.cpp new file mode 100644 index 0000000..f628aa7 --- /dev/null +++ b/drivers/Linux/noniso.cpp @@ -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 for MySensors use +*/ + +#include +#include +#include +#include +#include +#include +#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; +} diff --git a/drivers/Linux/stdlib_noniso.h b/drivers/Linux/stdlib_noniso.h new file mode 100644 index 0000000..636cbf4 --- /dev/null +++ b/drivers/Linux/stdlib_noniso.h @@ -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 From f0c5a993d56ce9cfd679e10840b9051c94b6e4b0 Mon Sep 17 00:00:00 2001 From: Marcelo Aquino Date: Mon, 22 Aug 2016 21:26:44 -0300 Subject: [PATCH 23/33] Fix Rpi config file corruption * Add uninstall to Makefile --- Makefile | 24 +++++++++++++++++++++++- MyConfig.h | 2 +- configure | 4 ++-- core/MyHwLinuxGeneric.cpp | 5 +++-- examples_linux/mysGateway.cpp | 2 +- 5 files changed, 30 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index 05067d1..1596919 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,8 @@ CONFIG_FILE=Makefile.inc include $(CONFIG_FILE) -GATEWAY=examples_linux/mysGateway +GATEWAY_BIN=mysGateway +GATEWAY=examples_linux/$(GATEWAY_BIN) GATEWAY_SOURCES=$(wildcard drivers/Linux/*.cpp) examples_linux/mysGateway.cpp GATEWAY_OBJECTS=$(patsubst %.cpp,%.o,$(GATEWAY_SOURCES)) DEPS+=$(patsubst %.cpp,%.d,$(GATEWAY_SOURCES)) @@ -61,7 +62,28 @@ 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 diff --git a/MyConfig.h b/MyConfig.h index b8e0517..785beda 100644 --- a/MyConfig.h +++ b/MyConfig.h @@ -732,7 +732,7 @@ * For now the configuration file is only used to store the emulated eeprom state */ #ifndef MY_LINUX_CONFIG_FILE - #define MY_LINUX_CONFIG_FILE "/etc/MySensorGateway.cfg" + #define MY_LINUX_CONFIG_FILE "/etc/mysensors.dat" #endif // Doxygen specific constructs, not included when built normally diff --git a/configure b/configure index 8df6e97..242b236 100755 --- a/configure +++ b/configure @@ -35,7 +35,7 @@ MySensors options: --my-debug=[enable|disable] Enables or disables debugging. Default to enable. - --my-config-file= Config file path. [/etc/MySensorGateway.cfg] + --my-config-file= Config file path. [/etc/mysensors.dat] --my-gateway=[none|ethernet|serial|mqtt] Gateway type, set to none to disable gateway feature. Default to ethernet. @@ -248,7 +248,7 @@ for opt do gateway_type=${optarg} ;; --my-config-file=*) - CXXFLAGS="-DMY_CONFIG_FILE=${optarg} $CXXFLAGS" + CXXFLAGS="-DMY_LINUX_CONFIG_FILE=\\\"${optarg}\\\" $CXXFLAGS" ;; --my-radio=*) radio_type=${optarg} diff --git a/core/MyHwLinuxGeneric.cpp b/core/MyHwLinuxGeneric.cpp index bf48457..b116555 100644 --- a/core/MyHwLinuxGeneric.cpp +++ b/core/MyHwLinuxGeneric.cpp @@ -90,12 +90,13 @@ void hwWriteConfigBlock(void* buf, void* addr, size_t length) if (length && offs + length <= _length) { memcpy(_config+offs, buf, length); - std::ofstream myFile(CONFIG_FILE, std::ios::out | std::ios::binary); + 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.write((const char*)buf+offs, _length); + myFile.seekp(offs); + myFile.write((const char*)buf, length); myFile.close(); } } diff --git a/examples_linux/mysGateway.cpp b/examples_linux/mysGateway.cpp index b53ffcc..baec194 100644 --- a/examples_linux/mysGateway.cpp +++ b/examples_linux/mysGateway.cpp @@ -24,7 +24,7 @@ // For more options run ./configure --help // Config file -//#define MY_LINUX_CONFIG_FILE "/etc/MySensorGateway.cfg" +//#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 From ebdaedb5145781780fa2da4fc4c2d48bb2617226 Mon Sep 17 00:00:00 2001 From: Marcelo Aquino Date: Sun, 21 Aug 2016 23:25:11 -0300 Subject: [PATCH 24/33] Add ESP8266 Serial modes SERIAL_TX_ONLY allows to use RX (GPIO3) as a general purpose input/output SERIAL_RX_ONLY allows to use TX (GPIO1) as a general purpose input/output --- MyConfig.h | 22 ++++++++++++++++++++++ core/MyHwESP8266.h | 2 +- keywords.txt | 1 + 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/MyConfig.h b/MyConfig.h index 366a676..2e1d3df 100644 --- a/MyConfig.h +++ b/MyConfig.h @@ -643,6 +643,10 @@ // If MY_CONTROLLER_IP_ADDRESS is left un-defined, gateway acts as server allowing incoming connections. //#define MY_CONTROLLER_IP_ADDRESS 192, 168, 178, 254 +/************************************** +* Node Locking +***************************************/ + /** * @defgroup MyLockgrp MyNodeLock * @ingroup internals @@ -699,8 +703,26 @@ #endif /** @}*/ // Node lock group +/********************************** +* ESP8266 Defaults +***********************************/ + +/** + * @def MY_ESP8266_SERIAL_MODE + * @brief Serial modes: SERIAL_FULL, SERIAL_RX_ONLY, SERIAL_TX_ONLY + * + * SERIAL_FULL: Default mode. + * SERIAL_TX_ONLY: allows to use RX (GPIO3) as a general purpose input/output. + * SERIAL_RX_ONLY: allows to use TX (GPIO1) as a general purpose input/output. + */ +#ifndef MY_ESP8266_SERIAL_MODE +#define MY_ESP8266_SERIAL_MODE SERIAL_FULL #endif +/** @}*/ // ESP8266 Defaults + +#endif // MyConfig_h + // Doxygen specific constructs, not included when built normally // This is used to enable disabled macros/definitions to be included in the documentation as well. #if DOXYGEN diff --git a/core/MyHwESP8266.h b/core/MyHwESP8266.h index ffbcb69..3bd6c00 100644 --- a/core/MyHwESP8266.h +++ b/core/MyHwESP8266.h @@ -35,7 +35,7 @@ // Define these as macros to save valuable space #define hwDigitalWrite(__pin, __value) (digitalWrite(__pin, __value)) -#define hwInit() MY_SERIALDEVICE.begin(MY_BAUD_RATE); MY_SERIALDEVICE.setDebugOutput(true) +#define hwInit() MY_SERIALDEVICE.begin(MY_BAUD_RATE, SERIAL_8N1, MY_ESP8266_SERIAL_MODE, 1); MY_SERIALDEVICE.setDebugOutput(true) #define hwWatchdogReset() wdt_reset() #define hwReboot() ESP.restart(); #define hwMillis() millis() diff --git a/keywords.txt b/keywords.txt index b84d949..5d8da56 100644 --- a/keywords.txt +++ b/keywords.txt @@ -107,6 +107,7 @@ MY_GATEWAY_MAX_RECEIVE_LENGTH LITERAL1 MY_ESP8266_SSID LITERAL1 MY_ESP8266_PASSWORD LITERAL1 MY_ESP8266_HOSTNAME LITERAL1 +MY_ESP8266_SERIAL_MODE LITERAL1 MY_IP_GATEWAY_ADDRESS LITERAL1 MY_IP_SUBNET_ADDRESS LITERAL1 MY_W5100_SPI_EN LITERAL1 From b4f4e28545c46f345c45003d50d74a65a58738a2 Mon Sep 17 00:00:00 2001 From: Marcelo Aquino Date: Sat, 3 Sep 2016 00:49:36 -0300 Subject: [PATCH 25/33] Add support for PTY to RPi serial gateway --- MyConfig.h | 33 +++++++-- MySensors.h | 2 +- configure | 37 +++++----- core/MyHwLinuxGeneric.cpp | 6 ++ core/MyHwLinuxGeneric.h | 7 +- core/MyMainLinux.cpp | 4 ++ drivers/Linux/SerialPort.cpp | 126 +++++++++++++++++++++++++++------- drivers/Linux/SerialPort.h | 29 +++++--- examples_linux/mysGateway.cpp | 10 +++ 9 files changed, 198 insertions(+), 56 deletions(-) diff --git a/MyConfig.h b/MyConfig.h index 785beda..dff0d6e 100644 --- a/MyConfig.h +++ b/MyConfig.h @@ -719,21 +719,43 @@ /** * @def MY_LINUX_SERIAL_PORT - * @brief Set the name of predictable tty + * @brief Serial device port */ #ifndef MY_LINUX_SERIAL_PORT -#define MY_LINUX_SERIAL_PORT "/dev/ttyUSB0" +#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 +#ifndef MY_LINUX_CONFIG_FILE +#define MY_LINUX_CONFIG_FILE "/etc/mysensors.dat" +#endif // Doxygen specific constructs, not included when built normally // This is used to enable disabled macros/definitions to be included in the documentation as well. @@ -751,4 +773,5 @@ #define MY_RX_MESSAGE_BUFFER_FEATURE #define MY_RX_MESSAGE_BUFFER_SIZE #define MY_NODE_LOCK_FEATURE +#define MY_LINUX_SERIAL_GROUPNAME #endif diff --git a/MySensors.h b/MySensors.h index 77e36ac..2b7e820 100644 --- a/MySensors.h +++ b/MySensors.h @@ -245,7 +245,7 @@ // GATEWAY - ESP8266 #include "core/MyGatewayTransportEthernet.cpp" #elif defined(MY_GATEWAY_LINUX) - // GATEWAY - Generic Linux (RaspberryPi, BBB, ...) + // GATEWAY - Generic Linux #include "drivers/Linux/EthernetClient.h" #include "drivers/Linux/EthernetServer.h" #include "drivers/Linux/IPAddress.h" diff --git a/configure b/configure index 242b236..86b73c3 100755 --- a/configure +++ b/configure @@ -11,12 +11,7 @@ Options: Help: -h, --help print this message -Driver options: - --driver=[SPIDEV|MRAA|RPi|LittleWire] - Driver for RF24 library. [configure autodetected] - Building options: - --os=[LINUX|DARWIN] Operating system. [configure autodetected] --soc=[BCM2835|BCM2836|AM33XX|A10|A13|A20|H3] SoC type to be used. [configure autodetected] --cpu-flags= CPU defining/optimizing flags to be used. [configure autodetected] @@ -33,12 +28,10 @@ Installation options: MySensors options: --my-debug=[enable|disable] - Enables or disables debugging. - Default to enable. + Enables or disables debugging. [enable] --my-config-file= Config file path. [/etc/mysensors.dat] --my-gateway=[none|ethernet|serial|mqtt] - Gateway type, set to none to disable gateway feature. - Default to ethernet. + Gateway type, set to none to disable gateway feature. [ethernet] --my-controller-url-address= Controller or MQTT broker url. --my-controller-ip-address= @@ -48,21 +41,25 @@ MySensors options: If gateway is set to mqtt, it sets the broker port. --my-serial-port= Serial port. [/dev/ttyUSB0] --my-serial-baudrate= 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= Symlink name for the PTY device. [/dev/ttyMySensorsGateway] + --my-serial-groupname= + Grant access to the specified system group for the serial device. --my-mqtt-client-id= MQTT client id. --my-mqtt-publish-topic-prefix= MQTT publish topic prefix. --my-mqtt-subscribe-topic-prefix= MQTT subscribe topic prefix. - --my-radio=[none|nrf24] Radio type, set to none to disable radio feature. - Default to rf24. + --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. --my-rf24-pa-level=[RF24_PA_MAX|RF24_PA_LOW] RF24 PA level. --my-rf24-irq-pin= Pin number connected to nRF24L01 IRQ pin. For RaspberryPi use BCM numbering. --my-rx-message-buffer-size= - Buffer size for incoming messages when using rf24 interrupts. - Default is 20. + Buffer size for incoming messages when using rf24 interrupts. [20] + EOF } @@ -202,7 +199,7 @@ debug=enable gateway_type=ethernet radio_type=nrf24 -params="OS SOC CXXFLAGS LDFLAGS PREFIX CXX RF24H_LIB_DIR GATEWAY_DIR INIT_SYSTEM" +params="SOC CXXFLAGS LDFLAGS PREFIX CXX RF24H_LIB_DIR GATEWAY_DIR INIT_SYSTEM" for opt do if [ "$opt" = "-h" ] || [ "$opt" = "--help" ]; then @@ -211,9 +208,6 @@ for opt do fi optarg="${opt#*=}" case "$opt" in - --os=*) - OS="$optarg" - ;; --soc=*) SOC="$optarg" ;; @@ -259,6 +253,15 @@ for opt do --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" ;; diff --git a/core/MyHwLinuxGeneric.cpp b/core/MyHwLinuxGeneric.cpp index b116555..2db3102 100644 --- a/core/MyHwLinuxGeneric.cpp +++ b/core/MyHwLinuxGeneric.cpp @@ -71,6 +71,12 @@ void hwInit() #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 } diff --git a/core/MyHwLinuxGeneric.h b/core/MyHwLinuxGeneric.h index 0d3e49b..eb5fee3 100644 --- a/core/MyHwLinuxGeneric.h +++ b/core/MyHwLinuxGeneric.h @@ -25,7 +25,12 @@ #include "MyHw.h" #include "SerialPort.h" -SerialPort Serial = SerialPort(MY_LINUX_SERIAL_PORT); +#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) diff --git a/core/MyMainLinux.cpp b/core/MyMainLinux.cpp index 5798cec..fd044e4 100644 --- a/core/MyMainLinux.cpp +++ b/core/MyMainLinux.cpp @@ -20,6 +20,10 @@ void handle_sigint(int sig) detachInterrupt(MY_RF24_IRQ_PIN); #endif + #if defined(MY_GATEWAY_SERIAL) + MY_SERIALDEVICE.end(); + #endif + exit(0); } diff --git a/drivers/Linux/SerialPort.cpp b/drivers/Linux/SerialPort.cpp index 732d102..048db46 100644 --- a/drivers/Linux/SerialPort.cpp +++ b/drivers/Linux/SerialPort.cpp @@ -25,30 +25,67 @@ #include #include #include +#include +#include +#include #include "SerialPort.h" -#define SERIAL_PORT "/dev/ttyAMA0" - -SerialPort::SerialPort() +SerialPort::SerialPort(const char *port, bool isPty) : serialPort(std::string(port)), isPty(isPty) { - SerialPort(SERIAL_PORT); + sd = -1; } -SerialPort::SerialPort(const char *port) : serialPort(std::string(port)) -{} - void SerialPort::begin(int bauds) +{ + if (!open(bauds)) { + fprintf(stderr, "Failed to open serial port.\n"); + exit(1); + } +} + +bool SerialPort::open(int bauds) { speed_t speed; struct termios options; - if ((sd = open(serialPort.c_str(), O_RDWR | O_NOCTTY | O_NDELAY)) == -1) { - fprintf(stderr, "Unable to open the serial port %s - \n", serialPort.c_str()); - exit(-1); - } + if (isPty) { + sd = posix_openpt(O_RDWR | O_NOCTTY | O_NDELAY); + if (sd < 0) { + perror("Couldn't open a PTY"); + return false; + } - // nonblocking mode - fcntl(sd, F_SETFL, FNDELAY); + if (grantpt(sd) != 0) { + perror("Couldn't grant permission to the PTY"); + return false; + } + + if (unlockpt(sd) != 0) { + perror("Couldn't unlock the PTY"); + return false; + } + + // Get the current options of the port + if (tcgetattr(sd, &options) < 0) { + perror("Couldn't get term attributes"); + 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) { + fprintf(stderr, "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) { + fprintf(stderr, "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 ; @@ -73,22 +110,20 @@ void SerialPort::begin(int bauds) // Get the current options of the port if (tcgetattr(sd, &options) < 0) { perror("Couldn't get term attributes"); - exit(1); + return false; } - // make raw - cfmakeraw(&options); + // Clear all the options + bzero(&options, sizeof(options)); // Set the baud rate cfsetispeed(&options, speed); cfsetospeed(&options, speed); - // turn on READ & ignore ctrl lines - options.c_cflag |= (CLOCAL | CREAD); - // 8N1 - options.c_cflag &= ~CSTOPB; - - options.c_lflag &= ~ECHOE; + // 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; @@ -98,16 +133,55 @@ void SerialPort::begin(int bauds) // Set parameters if (tcsetattr(sd, TCSANOW, &options) < 0) { perror("Couldn't set term attributes"); - exit(1); + return false; } // flush if (tcflush(sd, TCIOFLUSH) < 0) { perror("Couldn't flush serial"); - exit(1); + 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) { + fprintf(stderr, "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) { + fprintf(stderr, "Could not change PTY owner! (%d) %s\n", errno, strerror(errno)); + return false; + } + + ret = chmod(dev, ttyPermissions); + if (ret != 0) { + fprintf(stderr, "Could not change PTY permissions! (%d) %s\n", errno, strerror(errno)); + return false; + } + + return true; + } + return false; } int SerialPort::available() @@ -156,4 +230,8 @@ void SerialPort::flush() void SerialPort::end() { close(sd); + + if (isPty) { + unlink(serialPort.c_str()); // remove the symlink + } } diff --git a/drivers/Linux/SerialPort.h b/drivers/Linux/SerialPort.h index e344c6d..e0f77bb 100644 --- a/drivers/Linux/SerialPort.h +++ b/drivers/Linux/SerialPort.h @@ -21,6 +21,7 @@ #define SerialPort_h #include +#include #include "Stream.h" /** @@ -32,22 +33,34 @@ 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(); + SerialPort(const char *port, bool isPty = false); /** - * @brief SerialPort constructor. - */ - SerialPort(const char *port); - /** - * @brief Sets the data rate in bits per second (baud) for serial data transmission. + * @brief Open the serial port and set the data rate in bits per second (baud). * - * @param serialSpeed serial port speed. + * This function will terminate the program on an error. + * + * @param bauds bits per second. */ - void begin(int serialSpeed); + 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. * diff --git a/examples_linux/mysGateway.cpp b/examples_linux/mysGateway.cpp index baec194..1d7c745 100644 --- a/examples_linux/mysGateway.cpp +++ b/examples_linux/mysGateway.cpp @@ -29,6 +29,16 @@ // 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 From 09103e9c042eb4c0c90b7ecba8227baaf9f78b63 Mon Sep 17 00:00:00 2001 From: Marcelo Aquino Date: Wed, 7 Sep 2016 14:03:40 -0300 Subject: [PATCH 26/33] Add run time options to RPi gateway --- configure | 2 +- core/MyGatewayTransportSerial.cpp | 3 - core/MyHwLinuxGeneric.cpp | 24 +++++--- core/MyMainLinux.cpp | 98 ++++++++++++++++++++++++++++--- drivers/Linux/EthernetClient.cpp | 13 ++-- drivers/Linux/EthernetClient.h | 7 --- drivers/Linux/EthernetServer.cpp | 23 ++++---- drivers/Linux/EthernetServer.h | 7 --- drivers/Linux/Print.cpp | 6 +- drivers/Linux/SerialPort.cpp | 33 +++++------ drivers/Linux/log.cpp | 22 +++++++ drivers/Linux/log.h | 10 ++++ initscripts/mysgateway.sysvinit | 7 +-- 13 files changed, 178 insertions(+), 77 deletions(-) create mode 100644 drivers/Linux/log.cpp create mode 100644 drivers/Linux/log.h diff --git a/configure b/configure index 86b73c3..13d69fd 100755 --- a/configure +++ b/configure @@ -28,7 +28,7 @@ Installation options: MySensors options: --my-debug=[enable|disable] - Enables or disables debugging. [enable] + Enables or disables MySensors core debugging. [enable] --my-config-file= Config file path. [/etc/mysensors.dat] --my-gateway=[none|ethernet|serial|mqtt] Gateway type, set to none to disable gateway feature. [ethernet] diff --git a/core/MyGatewayTransportSerial.cpp b/core/MyGatewayTransportSerial.cpp index bbad4b3..8b5120d 100644 --- a/core/MyGatewayTransportSerial.cpp +++ b/core/MyGatewayTransportSerial.cpp @@ -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: diff --git a/core/MyHwLinuxGeneric.cpp b/core/MyHwLinuxGeneric.cpp index 2db3102..cc66f05 100644 --- a/core/MyHwLinuxGeneric.cpp +++ b/core/MyHwLinuxGeneric.cpp @@ -24,12 +24,14 @@ #include #include #include +#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() { +bool CheckConfigFile() +{ struct stat fileInfo; if (stat(CONFIG_FILE, &fileInfo) != 0) { @@ -152,27 +154,31 @@ int8_t hwSleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mo return -2; } -uint16_t hwCPUVoltage() { +uint16_t hwCPUVoltage() +{ // TODO: Not supported! return 0; } -uint16_t hwCPUFrequency() { +uint16_t hwCPUFrequency() +{ // TODO: Not supported! return 0; } -uint16_t hwFreeMem() { +uint16_t hwFreeMem() +{ // TODO: Not supported! return 0; } #ifdef MY_DEBUG -void hwDebugPrint(const char *fmt, ... ) +void hwDebugPrint(const char *fmt, ...) { - va_list arglist; - va_start(arglist, fmt); - vprintf(fmt, arglist); - va_end(arglist); + va_list args; + + va_start(args, fmt); + mys_log_v(LOG_DEBUG, fmt, args); + va_end(args); } #endif diff --git a/core/MyMainLinux.cpp b/core/MyMainLinux.cpp index fd044e4..a461184 100644 --- a/core/MyMainLinux.cpp +++ b/core/MyMainLinux.cpp @@ -1,19 +1,23 @@ // Initialize library and handle sketch functions like we want to -#include +#include #include #include +#include +#include +#include +#include +#include "log.h" #include "MySensorsCore.h" -/* - * handler for SIGINT signal - */ void handle_sigint(int sig) { if (sig == SIGINT) { - std::cout << "Received SIGINT\n" << std::endl; + mys_log(LOG_NOTICE, "Received SIGINT\n\n"); } else if (sig == SIGTERM) { - std::cout << "Received SIGTERM\n" << std::endl; + mys_log(LOG_NOTICE, "Received SIGTERM\n\n"); + } else { + return; } #ifdef MY_RF24_IRQ_PIN @@ -27,11 +31,91 @@ void handle_sigint(int sig) exit(0); } -int main(void) { +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; +} + +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': + 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"); + exit(0); + case 'd': + debug = 1; + break; + case 'b': + foreground = 0; + break; + } + } + + log_opts = LOG_CONS; + if (foreground && isatty(STDIN_FILENO)) { + 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 (;;) { diff --git a/drivers/Linux/EthernetClient.cpp b/drivers/Linux/EthernetClient.cpp index 387fcaa..9a9e65b 100644 --- a/drivers/Linux/EthernetClient.cpp +++ b/drivers/Linux/EthernetClient.cpp @@ -29,6 +29,7 @@ #include #include #include +#include "log.h" #include "EthernetClient.h" EthernetClient::EthernetClient() : _sock(-1) { @@ -50,7 +51,7 @@ int EthernetClient::connect(const char* host, uint16_t port) { sprintf(port_str, "%hu", port); if ((rv = getaddrinfo(host, port_str, &hints, &servinfo)) != 0) { - fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv)); + mys_log(LOG_ERR, "getaddrinfo: %s\n", gai_strerror(rv)); return -1; } @@ -58,13 +59,13 @@ int EthernetClient::connect(const char* host, uint16_t port) { for (p = servinfo; p != NULL; p = p->ai_next) { if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) { - perror("socket"); + mys_log(LOG_ERR, "socket: %s\n", strerror(errno)); continue; } if (::connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) { close(sockfd); - perror("connect"); + mys_log(LOG_ERR, "connect: %s\n", strerror(errno)); continue; } @@ -72,7 +73,7 @@ int EthernetClient::connect(const char* host, uint16_t port) { } if (p == NULL) { - fprintf(stderr, "failed to connect\n"); + mys_log(LOG_ERR, "failed to connect\n"); return -1; } @@ -80,7 +81,7 @@ int EthernetClient::connect(const char* host, uint16_t port) { void *addr = &(((struct sockaddr_in*)p->ai_addr)->sin_addr); inet_ntop(p->ai_family, addr, s, sizeof s); - ETHERNETCLIENT_DEBUG("connected to %s\n", s); + mys_log(LOG_DEBUG, "connected to %s\n", s); freeaddrinfo(servinfo); // all done with this structure @@ -106,7 +107,7 @@ size_t EthernetClient::write(const uint8_t *buf, size_t size) { while (size > 0) { rc = send(_sock, buf + bytes, size, MSG_NOSIGNAL | MSG_DONTWAIT); if (rc == -1) { - perror("send"); + mys_log(LOG_ERR, "send: %s\n", strerror(errno)); close(_sock); _sock = -1; break; diff --git a/drivers/Linux/EthernetClient.h b/drivers/Linux/EthernetClient.h index 78dc143..2cbaca9 100644 --- a/drivers/Linux/EthernetClient.h +++ b/drivers/Linux/EthernetClient.h @@ -37,13 +37,6 @@ #define ETHERNETCLIENT_W5100_CLOSE_WAIT 0x1C #define ETHERNETCLIENT_W5100_LAST_ACK 0x1D -// debug -#if defined(ETHERNETCLIENT_VERBOSE) - #define ETHERNETCLIENT_DEBUG(x,...) debug(x, ##__VA_ARGS__) //!< debug -#else - #define ETHERNETCLIENT_DEBUG(x,...) //!< debug NULL -#endif - /** * EthernetClient class */ diff --git a/drivers/Linux/EthernetServer.cpp b/drivers/Linux/EthernetServer.cpp index 0537bb6..7ac89f4 100644 --- a/drivers/Linux/EthernetServer.cpp +++ b/drivers/Linux/EthernetServer.cpp @@ -28,6 +28,7 @@ #include #include #include +#include "log.h" #include "EthernetClient.h" #include "EthernetServer.h" @@ -56,26 +57,26 @@ void EthernetServer::begin(IPAddress address) sprintf(portstr, "%d", port); if ((rv = getaddrinfo(address.toString().c_str(), portstr, &hints, &servinfo)) != 0) { - fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv)); + 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) { - perror("socket"); + mys_log(LOG_ERR, "socket: %s\n", strerror(errno)); continue; } if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) { - perror("setsockopt"); + mys_log(LOG_ERR, "setsockopt: %s\n", strerror(errno)); freeaddrinfo(servinfo); return; } if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) { close(sockfd); - perror("bind"); + mys_log(LOG_ERR, "bind: %s\n", strerror(errno)); continue; } @@ -83,13 +84,13 @@ void EthernetServer::begin(IPAddress address) } if (p == NULL) { - fprintf(stderr, "failed to bind\n"); + mys_log(LOG_ERR, "failed to bind\n"); freeaddrinfo(servinfo); return; } if (listen(sockfd, ETHERNETSERVER_BACKLOG) == -1) { - perror("listen"); + mys_log(LOG_ERR, "listen: %s\n", strerror(errno)); freeaddrinfo(servinfo); return; } @@ -101,7 +102,7 @@ void EthernetServer::begin(IPAddress address) struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr; void *addr = &(ipv4->sin_addr); inet_ntop(p->ai_family, addr, ipstr, sizeof ipstr); - ETHERNETSERVER_DEBUG("Listening for connections on %s:%s\n", ipstr, portstr); + mys_log(LOG_DEBUG, "Listening for connections on %s:%s\n", ipstr, portstr); } bool EthernetServer::hasClient() @@ -141,7 +142,7 @@ size_t EthernetServer::write(const uint8_t *buffer, size_t size) client.stop(); clients[i] = clients.back(); clients.pop_back(); - ETHERNETSERVER_DEBUG("Client disconnected."); + mys_log(LOG_DEBUG, "Client disconnected."); } } @@ -191,7 +192,7 @@ void EthernetServer::_accept() } } if (no_free_slots) { - ETHERNETSERVER_DEBUG("Max number of ethernet clients reached."); + mys_log(LOG_DEBUG, "Max number of ethernet clients reached."); return; } } @@ -200,7 +201,7 @@ void EthernetServer::_accept() new_fd = accept(sockfd, (struct sockaddr *)&client_addr, &sin_size); if (new_fd == -1) { if (errno != EAGAIN && errno != EWOULDBLOCK) { - perror("accept"); + mys_log(LOG_ERR, "accept: %s\n", strerror(errno)); } return; } @@ -210,5 +211,5 @@ void EthernetServer::_accept() void *addr = &(((struct sockaddr_in*)&client_addr)->sin_addr); inet_ntop(client_addr.ss_family, addr, ipstr, sizeof ipstr); - ETHERNETSERVER_DEBUG("New connection from %s\n", ipstr); + mys_log(LOG_DEBUG, "New connection from %s\n", ipstr); } diff --git a/drivers/Linux/EthernetServer.h b/drivers/Linux/EthernetServer.h index 8bb4c6d..4240e96 100644 --- a/drivers/Linux/EthernetServer.h +++ b/drivers/Linux/EthernetServer.h @@ -34,13 +34,6 @@ #define ETHERNETSERVER_BACKLOG 10 //!< Maximum length to which the queue of pending connections may grow. #endif -// debug -#if defined(ETHERNETSERVER_VERBOSE) - #define ETHERNETSERVER_DEBUG(x,...) printf(x, ##__VA_ARGS__) //!< debug -#else - #define ETHERNETSERVER_DEBUG(x,...) //!< debug NULL -#endif - class EthernetClient; /** diff --git a/drivers/Linux/Print.cpp b/drivers/Linux/Print.cpp index 306b82c..94eb814 100644 --- a/drivers/Linux/Print.cpp +++ b/drivers/Linux/Print.cpp @@ -38,7 +38,11 @@ size_t Print::write(const uint8_t *buffer, size_t size) { return n; } -size_t Print::printf(const char *format, ...) { +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]; diff --git a/drivers/Linux/SerialPort.cpp b/drivers/Linux/SerialPort.cpp index 048db46..79b8484 100644 --- a/drivers/Linux/SerialPort.cpp +++ b/drivers/Linux/SerialPort.cpp @@ -28,6 +28,7 @@ #include #include #include +#include "log.h" #include "SerialPort.h" SerialPort::SerialPort(const char *port, bool isPty) : serialPort(std::string(port)), isPty(isPty) @@ -38,7 +39,7 @@ SerialPort::SerialPort(const char *port, bool isPty) : serialPort(std::string(po void SerialPort::begin(int bauds) { if (!open(bauds)) { - fprintf(stderr, "Failed to open serial port.\n"); + mys_log(LOG_ERR, "Failed to open serial port.\n"); exit(1); } } @@ -51,35 +52,29 @@ bool SerialPort::open(int bauds) if (isPty) { sd = posix_openpt(O_RDWR | O_NOCTTY | O_NDELAY); if (sd < 0) { - perror("Couldn't open a PTY"); + mys_log(LOG_ERR, "Couldn't open a PTY: %s\n", strerror(errno)); return false; } if (grantpt(sd) != 0) { - perror("Couldn't grant permission to the PTY"); + mys_log(LOG_ERR, "Couldn't grant permission to the PTY: %s\n", strerror(errno)); return false; } if (unlockpt(sd) != 0) { - perror("Couldn't unlock the PTY"); - return false; - } - - // Get the current options of the port - if (tcgetattr(sd, &options) < 0) { - perror("Couldn't get term attributes"); + 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) { - fprintf(stderr, "Couldn't create a symlink '%s' to PTY! (%d) %s\n", serialPort.c_str(), errno, strerror(errno)); + 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) { - fprintf(stderr, "Unable to open the serial port %s\n", serialPort.c_str()); + mys_log(LOG_ERR, "Unable to open the serial port %s\n", serialPort.c_str()); return false; } @@ -109,7 +104,7 @@ bool SerialPort::open(int bauds) // Get the current options of the port if (tcgetattr(sd, &options) < 0) { - perror("Couldn't get term attributes"); + mys_log(LOG_ERR, "Couldn't get term attributes: %s\n", strerror(errno)); return false; } @@ -132,13 +127,13 @@ bool SerialPort::open(int bauds) // Set parameters if (tcsetattr(sd, TCSANOW, &options) < 0) { - perror("Couldn't set term attributes"); + mys_log(LOG_ERR, "Couldn't set term attributes: %s\n", strerror(errno)); return false; } // flush if (tcflush(sd, TCIOFLUSH) < 0) { - perror("Couldn't flush serial"); + mys_log(LOG_ERR, "Couldn't flush serial: %s\n", strerror(errno)); return false; } @@ -157,7 +152,7 @@ bool SerialPort::setGroupPerm(const char *groupName) if (sd != -1 && groupName != NULL) { devGrp = getgrnam(groupName); if (devGrp == NULL) { - fprintf(stderr, "getgrnam: %s failed. (%d) %s\n", groupName, errno, strerror(errno)); + mys_log(LOG_ERR, "getgrnam: %s failed. (%d) %s\n", groupName, errno, strerror(errno)); return false; } @@ -169,13 +164,13 @@ bool SerialPort::setGroupPerm(const char *groupName) ret = chown(dev, -1, devGrp->gr_gid); if (ret == -1) { - fprintf(stderr, "Could not change PTY owner! (%d) %s\n", errno, strerror(errno)); + mys_log(LOG_ERR, "Could not change PTY owner! (%d) %s\n", errno, strerror(errno)); return false; } ret = chmod(dev, ttyPermissions); if (ret != 0) { - fprintf(stderr, "Could not change PTY permissions! (%d) %s\n", errno, strerror(errno)); + mys_log(LOG_ERR, "Could not change PTY permissions! (%d) %s\n", errno, strerror(errno)); return false; } @@ -189,7 +184,7 @@ int SerialPort::available() int nbytes = 0; if (ioctl(sd, FIONREAD, &nbytes) < 0) { - fprintf(stderr, "Failed to get byte count on serial.\n"); + mys_log(LOG_ERR, "Failed to get byte count on serial.\n"); exit(-1); } return nbytes; diff --git a/drivers/Linux/log.cpp b/drivers/Linux/log.cpp new file mode 100644 index 0000000..b45df4e --- /dev/null +++ b/drivers/Linux/log.cpp @@ -0,0 +1,22 @@ +#include +#include +#include +#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); +} diff --git a/drivers/Linux/log.h b/drivers/Linux/log.h new file mode 100644 index 0000000..791e4b9 --- /dev/null +++ b/drivers/Linux/log.h @@ -0,0 +1,10 @@ +#ifndef LOG_H +#define LOG_H + +#include +#include + +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))); + +#endif diff --git a/initscripts/mysgateway.sysvinit b/initscripts/mysgateway.sysvinit index 34ee0c4..5ca7e12 100644 --- a/initscripts/mysgateway.sysvinit +++ b/initscripts/mysgateway.sysvinit @@ -10,11 +10,6 @@ # placed in /etc/init.d. ### END INIT INFO -# Author: Foo Bar -# -# Please remove the "Author" lines above and replace them -# with your own name if you copy and modify this script. - # Do NOT "set -e" # PATH should only include /usr/* if it runs after the mountnfs.sh script @@ -22,7 +17,7 @@ PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/sbin:/usr/local/bin DESC="MySensors Gateway" NAME=mysGateway DAEMON=%gateway_dir%/$NAME -DAEMON_ARGS="-d" +DAEMON_ARGS="-b" PIDFILE=/var/run/$NAME.pid SCRIPTNAME=/etc/init.d/$NAME From 4175a5f42321771becd73ab9c57a9b5950bd6353 Mon Sep 17 00:00:00 2001 From: Marcelo Aquino Date: Fri, 9 Sep 2016 14:02:24 -0300 Subject: [PATCH 27/33] Cleanup configure script --- configure | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/configure b/configure index 13d69fd..b837590 100755 --- a/configure +++ b/configure @@ -17,10 +17,8 @@ Building options: --cpu-flags= CPU defining/optimizing flags to be used. [configure autodetected] --extra-cxxflags= Extra C flags passed to C/C++ compilation. [] --extra-ldflags= Extra C flags passed to linking. [] - --libname= Library name. [rf24] --cxx_compiler= C++ compiler [arm-linux-gnueabihf-g++][g++] --no-clean Don't clean previous build artifacts. - --rf24h-lib-dir RF24 library files directory. Installation options: --prefix= Installation prefix path. [/usr/local] @@ -39,7 +37,7 @@ MySensors options: Use commas instead of points. Example: 127,0,0,1 --my-port= The port to keep open on gateway mode. If gateway is set to mqtt, it sets the broker port. - --my-serial-port= Serial port. [/dev/ttyUSB0] + --my-serial-port= Serial port. [/dev/ttyACM0] --my-serial-baudrate= 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. @@ -52,9 +50,9 @@ MySensors options: --my-mqtt-subscribe-topic-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. + --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 level. [RF24_PA_MAX] --my-rf24-irq-pin= Pin number connected to nRF24L01 IRQ pin. For RaspberryPi use BCM numbering. --my-rx-message-buffer-size= @@ -229,9 +227,6 @@ for opt do --prefix=*) PREFIX="$optarg" ;; - --rf24h-lib-dir=*) - RF24H_LIB_DIR="$optarg" - ;; --gateway-dir=*) GATEWAY_DIR="$optarg" ;; @@ -331,7 +326,6 @@ if [[ ${gateway_type} == "ethernet" ]]; then elif [[ ${gateway_type} == "serial" ]]; then CXXFLAGS="-DMY_GATEWAY_SERIAL $CXXFLAGS" elif [[ ${gateway_type} == "mqtt" ]]; then - LDFLAGS="-lmosquitto $LDFLAGS" CXXFLAGS="-DMY_GATEWAY_LINUX -DMY_GATEWAY_MQTT_CLIENT $CXXFLAGS" fi From fab2112f9bf7846b53b6dedc5137303329b5dbb7 Mon Sep 17 00:00:00 2001 From: Marcelo Aquino Date: Fri, 9 Sep 2016 19:21:19 -0300 Subject: [PATCH 28/33] Use mysensors built-in nrf24 library for RPi --- Makefile | 29 +- MyConfig.h | 4 +- MySensors.h | 10 +- configure | 6 +- core/MyHwLinuxGeneric.cpp | 6 + core/MyHwLinuxGeneric.h | 1 + core/MyMainLinux.cpp | 19 +- core/MyTransportNRF24.cpp | 6 +- drivers/Linux/Arduino.h | 11 +- drivers/Linux/compatibility.cpp | 2 +- drivers/Linux/{log.cpp => log.c} | 0 drivers/Linux/log.h | 8 + drivers/RF24/RF24.cpp | 53 +- drivers/RF24/RF24_Linux.cpp | 200 ---- drivers/RF24/RF24_Linux.h | 302 ------ drivers/RPi/SPI.cpp | 106 ++ drivers/RPi/SPI.h | 231 +++++ drivers/RPi/bcm2835.c | 1504 +++++++++++++++++++++++++++ drivers/RPi/bcm2835.h | 1648 ++++++++++++++++++++++++++++++ drivers/RPi/piHiPri.c | 48 + drivers/RPi/rpi_util.cpp | 330 ++++++ drivers/RPi/rpi_util.h | 71 ++ 22 files changed, 4051 insertions(+), 544 deletions(-) rename drivers/Linux/{log.cpp => log.c} (100%) delete mode 100644 drivers/RF24/RF24_Linux.cpp delete mode 100644 drivers/RF24/RF24_Linux.h create mode 100644 drivers/RPi/SPI.cpp create mode 100644 drivers/RPi/SPI.h create mode 100644 drivers/RPi/bcm2835.c create mode 100644 drivers/RPi/bcm2835.h create mode 100644 drivers/RPi/piHiPri.c create mode 100644 drivers/RPi/rpi_util.cpp create mode 100644 drivers/RPi/rpi_util.h diff --git a/Makefile b/Makefile index 1596919..3ee190c 100644 --- a/Makefile +++ b/Makefile @@ -13,14 +13,22 @@ include $(CONFIG_FILE) GATEWAY_BIN=mysGateway GATEWAY=examples_linux/$(GATEWAY_BIN) -GATEWAY_SOURCES=$(wildcard drivers/Linux/*.cpp) examples_linux/mysGateway.cpp -GATEWAY_OBJECTS=$(patsubst %.cpp,%.o,$(GATEWAY_SOURCES)) -DEPS+=$(patsubst %.cpp,%.d,$(GATEWAY_SOURCES)) +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 -ifneq ($(RF24H_LIB_DIR),) -CINCLUDE+=-I$(RF24H_LIB_DIR) +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 @@ -28,15 +36,18 @@ endif all: $(GATEWAY) # Gateway Build -$(GATEWAY): $(GATEWAY_OBJECTS) - $(CXX) $(LDFLAGS) -o $@ $(GATEWAY_OBJECTS) +$(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]" @@ -45,7 +56,7 @@ cleanconfig: # clear build files clean: @echo "[Cleaning]" - rm -rf build $(GATEWAY_OBJECTS) $(GATEWAY) $(DEPS) + rm -rf build $(OBJECTS) $(GATEWAY) $(DEPS) $(CONFIG_FILE): @echo "[Running configure]" diff --git a/MyConfig.h b/MyConfig.h index dff0d6e..10c3240 100644 --- a/MyConfig.h +++ b/MyConfig.h @@ -410,7 +410,7 @@ #define MY_RF24_CE_PIN 4 #elif defined(ARDUINO_ARCH_SAMD) #define MY_RF24_CE_PIN 27 - #elif defined(__linux__) + #elif defined(LINUX_ARCH_RASPBERRYPI) #define MY_RF24_CE_PIN 22 #else #define MY_RF24_CE_PIN 9 @@ -426,7 +426,7 @@ #define MY_RF24_CS_PIN 15 #elif defined(ARDUINO_ARCH_SAMD) #define MY_RF24_CS_PIN 3 - #elif defined(__linux__) + #elif defined(LINUX_ARCH_RASPBERRYPI) #define MY_RF24_CS_PIN 24 #else #define MY_RF24_CS_PIN 10 diff --git a/MySensors.h b/MySensors.h index 2b7e820..ad59f3f 100644 --- a/MySensors.h +++ b/MySensors.h @@ -276,6 +276,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 @@ -284,11 +288,7 @@ #if defined(MY_RF24_ENABLE_ENCRYPTION) #include "drivers/AES/AES.cpp" #endif - #if defined(__linux__) - #include "drivers/RF24/RF24_Linux.cpp" - #else - #include "drivers/RF24/RF24.cpp" - #endif + #include "drivers/RF24/RF24.cpp" #include "core/MyTransportNRF24.cpp" #elif defined(MY_RS485) #include "drivers/AltSoftSerial/AltSoftSerial.cpp" diff --git a/configure b/configure index b837590..b2b2dc8 100755 --- a/configure +++ b/configure @@ -54,7 +54,6 @@ MySensors options: --my-rf24-pa-level=[RF24_PA_MAX|RF24_PA_LOW] RF24 PA level. [RF24_PA_MAX] --my-rf24-irq-pin= Pin number connected to nRF24L01 IRQ pin. - For RaspberryPi use BCM numbering. --my-rx-message-buffer-size= Buffer size for incoming messages when using rf24 interrupts. [20] @@ -197,7 +196,7 @@ debug=enable gateway_type=ethernet radio_type=nrf24 -params="SOC CXXFLAGS LDFLAGS PREFIX CXX RF24H_LIB_DIR GATEWAY_DIR INIT_SYSTEM" +params="SOC CXXFLAGS LDFLAGS PREFIX CXX GATEWAY_DIR INIT_SYSTEM" for opt do if [ "$opt" = "-h" ] || [ "$opt" = "--help" ]; then @@ -312,6 +311,7 @@ if [ -z "${CPUFLAGS}" ]; then 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" @@ -331,8 +331,6 @@ fi if [[ ${radio_type} == "nrf24" ]]; then CXXFLAGS="-DMY_RADIO_NRF24 $CXXFLAGS" - LDFLAGS="-lrf24-bcm $LDFLAGS" - RF24H_LIB_DIR=${PREFIX}/include/RF24 fi LDFLAGS="-pthread $LDFLAGS" diff --git a/core/MyHwLinuxGeneric.cpp b/core/MyHwLinuxGeneric.cpp index cc66f05..ea5ddb8 100644 --- a/core/MyHwLinuxGeneric.cpp +++ b/core/MyHwLinuxGeneric.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include "log.h" static const char* CONFIG_FILE = MY_LINUX_CONFIG_FILE; @@ -124,6 +125,11 @@ void hwWriteConfig(int adr, uint8_t value) } } +void hwRandomNumberInit() +{ + randomSeed(time(NULL)); +} + // Not supported! int8_t hwSleep(unsigned long ms) { diff --git a/core/MyHwLinuxGeneric.h b/core/MyHwLinuxGeneric.h index eb5fee3..067b93a 100644 --- a/core/MyHwLinuxGeneric.h +++ b/core/MyHwLinuxGeneric.h @@ -44,6 +44,7 @@ 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; diff --git a/core/MyMainLinux.cpp b/core/MyMainLinux.cpp index a461184..56a2d47 100644 --- a/core/MyMainLinux.cpp +++ b/core/MyMainLinux.cpp @@ -72,6 +72,15 @@ static int daemonize(void) 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; @@ -83,11 +92,7 @@ int main(int argc, char *argv[]) while ((opt = getopt(argc, argv, "hdb")) != -1) { switch (opt) { case 'h': - 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"); + print_usage(); exit(0); case 'd': debug = 1; @@ -95,11 +100,15 @@ int main(int argc, char *argv[]) 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) { diff --git a/core/MyTransportNRF24.cpp b/core/MyTransportNRF24.cpp index cdd3f2b..e458eee 100644 --- a/core/MyTransportNRF24.cpp +++ b/core/MyTransportNRF24.cpp @@ -20,11 +20,7 @@ #include "MyConfig.h" #include "MyTransport.h" -#ifdef __linux__ - #include "drivers/RF24/RF24_Linux.h" -#else - #include "drivers/RF24/RF24.h" -#endif +#include "drivers/RF24/RF24.h" #include "drivers/CircularBuffer/CircularBuffer.h" diff --git a/drivers/Linux/Arduino.h b/drivers/Linux/Arduino.h index e1dc088..41081d5 100644 --- a/drivers/Linux/Arduino.h +++ b/drivers/Linux/Arduino.h @@ -13,6 +13,11 @@ #include #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 @@ -39,6 +44,10 @@ #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; @@ -50,7 +59,7 @@ typedef string String; void yield(void); unsigned long millis(void); -void delay(unsigned int millis); +void _delay_ms(unsigned int millis); void randomSeed(unsigned long seed); long randMax(long howbig); long randMinMax(long howsmall, long howbig); diff --git a/drivers/Linux/compatibility.cpp b/drivers/Linux/compatibility.cpp index a0a0eb3..b0ceee1 100644 --- a/drivers/Linux/compatibility.cpp +++ b/drivers/Linux/compatibility.cpp @@ -21,7 +21,7 @@ unsigned long millis(void) return ((curTime.tv_sec - millis_at_start) * 1000) + (curTime.tv_usec / 1000); } -void delay(unsigned int millis) +void _delay_ms(unsigned int millis) { struct timespec sleeper; diff --git a/drivers/Linux/log.cpp b/drivers/Linux/log.c similarity index 100% rename from drivers/Linux/log.cpp rename to drivers/Linux/log.c diff --git a/drivers/Linux/log.h b/drivers/Linux/log.h index 791e4b9..5c93dcc 100644 --- a/drivers/Linux/log.h +++ b/drivers/Linux/log.h @@ -4,7 +4,15 @@ #include #include +#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 diff --git a/drivers/RF24/RF24.cpp b/drivers/RF24/RF24.cpp index bd84c56..aea2bd7 100644 --- a/drivers/RF24/RF24.cpp +++ b/drivers/RF24/RF24.cpp @@ -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 diff --git a/drivers/RF24/RF24_Linux.cpp b/drivers/RF24/RF24_Linux.cpp deleted file mode 100644 index 9b4c25a..0000000 --- a/drivers/RF24/RF24_Linux.cpp +++ /dev/null @@ -1,200 +0,0 @@ -/** - * The MySensors Arduino library handles the wireless radio link and protocol - * between your home built sensors/actuators and HA controller of choice. - * The sensors forms a self healing radio network with optional repeaters. Each - * repeater and gateway builds a routing tables in EEPROM which keeps track of the - * network topology allowing messages to be routed to nodes. - * - * Created by Marcelo Aquino - * Copyright (C) 2016 Marcelo Aquino - * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors - * - * Documentation: http://www.mysensors.org - * Support Forum: http://forum.mysensors.org - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This is a wrapper for maniacbug's RF24 library, copyright (C) 2011 J. Coliz - */ - -#include -#include "RF24_Linux.h" - -#ifdef MY_RX_MESSAGE_BUFFER_FEATURE - typedef void (*RF24_receiveCallbackType)(void); - - void (* RF24_receiveCallback)(void) = NULL; - - static pthread_mutex_t rf24_mutex = PTHREAD_MUTEX_INITIALIZER; -#endif - -static uint8_t MY_RF24_BASE_ADDR[MY_RF24_ADDR_WIDTH] = { MY_RF24_BASE_RADIO_ID }; -static uint8_t MY_RF24_NODE_ADDRESS = AUTO; - -RF24 _rf24(RF24_CE_PIN, RF24_CS_PIN, MY_RF24_SPI_MAX_SPEED); - -static void RF24_startListening(void) { - RF24_DEBUG(PSTR("start listening\n")); - - _rf24.startListening(); -} - -static void RF24_powerDown(void) { -#ifdef MY_RX_MESSAGE_BUFFER_FEATURE - pthread_mutex_lock(&rf24_mutex); -#endif - _rf24.powerDown(); -#ifdef MY_RX_MESSAGE_BUFFER_FEATURE - pthread_mutex_unlock(&rf24_mutex); -#endif -} - -static bool RF24_sendMessage( uint8_t recipient, const void* buf, uint8_t len ) { -#ifdef MY_RX_MESSAGE_BUFFER_FEATURE - pthread_mutex_lock(&rf24_mutex); -#endif - // Make sure radio has powered up - _rf24.powerUp(); - _rf24.stopListening(); - - RF24_DEBUG(PSTR("send message to %d, len=%d\n"), recipient,len); - - MY_RF24_BASE_ADDR[0] = recipient; - _rf24.openWritingPipe(MY_RF24_BASE_ADDR); - bool ok = _rf24.write(buf, len, recipient == BROADCAST_ADDRESS); - _rf24.startListening(); -#ifdef MY_RX_MESSAGE_BUFFER_FEATURE - pthread_mutex_unlock(&rf24_mutex); -#endif - return ok; -} - -static bool RF24_isDataAvailable() { -#ifdef MY_RX_MESSAGE_BUFFER_FEATURE - pthread_mutex_lock(&rf24_mutex); -#endif - uint8_t pipe_num = 255; - _rf24.available(&pipe_num); -#ifdef MY_RX_MESSAGE_BUFFER_FEATURE - pthread_mutex_unlock(&rf24_mutex); -#endif - return (pipe_num <= 5); -} - -static uint8_t RF24_readMessage( void* buf) { -#ifdef MY_RX_MESSAGE_BUFFER_FEATURE - pthread_mutex_lock(&rf24_mutex); -#endif - uint8_t len = _rf24.getDynamicPayloadSize(); - _rf24.read(buf, len); -#ifdef MY_RX_MESSAGE_BUFFER_FEATURE - pthread_mutex_unlock(&rf24_mutex); -#endif - return len; -} - -static void RF24_setNodeAddress(uint8_t address) { -#ifdef MY_RX_MESSAGE_BUFFER_FEATURE - pthread_mutex_lock(&rf24_mutex); -#endif - if (address != AUTO){ - MY_RF24_NODE_ADDRESS = address; - // enable node pipe - MY_RF24_BASE_ADDR[0] = MY_RF24_NODE_ADDRESS; - _rf24.openReadingPipe(NODE_PIPE, MY_RF24_BASE_ADDR); - // enable autoACK on node pipe - _rf24.setAutoAck(NODE_PIPE, true); - } -#ifdef MY_RX_MESSAGE_BUFFER_FEATURE - pthread_mutex_unlock(&rf24_mutex); -#endif -} - -static uint8_t RF24_getNodeID(void) { - return MY_RF24_NODE_ADDRESS; -} - -bool RF24_sanityCheck(void) { - bool ok = true; -#ifdef MY_RX_MESSAGE_BUFFER_FEATURE - pthread_mutex_lock(&rf24_mutex); -#endif - // detect HW defect ot interrupted SPI line, CE disconnect cannot be detected - if (_rf24.getPALevel() != MY_RF24_PA_LEVEL || _rf24.getDataRate() != MY_RF24_DATARATE || - _rf24.getChannel() != MY_RF24_CHANNEL) { - ok = false; - } -#ifdef MY_RX_MESSAGE_BUFFER_FEATURE - pthread_mutex_unlock(&rf24_mutex); -#endif - return ok; -} - -#ifdef MY_RX_MESSAGE_BUFFER_FEATURE -void RF24_irqHandler(void) { - if (RF24_receiveCallback) { - while (RF24_isDataAvailable()) { - RF24_receiveCallback(); - } - } else { - // clear RX interrupt - bool tx_ok,tx_fail,rx; - _rf24.whatHappened(tx_ok,tx_fail,rx); - } -} - -void RF24_registerReceiveCallback(RF24_receiveCallbackType cb) { - RF24_receiveCallback = cb; -} -#endif - -static bool RF24_initialize(void) { - // start up the radio library - _rf24.begin(); - // determine whether the hardware is an nRF24L01+ or not - if (!_rf24.isPVariant()) { - RF24_DEBUG(PSTR("radio hardware not compatible")); - return false; - } - // set CRC - _rf24.setCRCLength(RF24_CRC_16); - // set address width - _rf24.setAddressWidth(MY_RF24_ADDR_WIDTH); - // auto retransmit delay 1500us, auto retransmit count 15 - _rf24.setRetries(5, 15); - // channel - _rf24.setChannel(MY_RF24_CHANNEL); - // PA level - _rf24.setPALevel(MY_RF24_PA_LEVEL); - // data rate - _rf24.setDataRate(MY_RF24_DATARATE); - // sanity check - #if defined(MY_RF24_SANITY_CHECK) - if (!RF24_sanityCheck()) { - RF24_DEBUG(PSTR("RF24:Sanity check failed: configuration mismatch! Check wiring, replace module or non-P version\n")); - return false; - } - #endif - // enable Dynamic payload - _rf24.enableDynamicPayloads(); - // enable ACK payload - _rf24.enableAckPayload(); - // disable AA on all pipes, activate when node pipe set - _rf24.setAutoAck(false); - // all nodes listen to broadcast pipe (for FIND_PARENT_RESPONSE messages) - MY_RF24_BASE_ADDR[0] = BROADCAST_ADDRESS; - _rf24.openReadingPipe(BROADCAST_PIPE, MY_RF24_BASE_ADDR); - - //_rf24.printDetails(); - - #ifdef MY_RX_MESSAGE_BUFFER_FEATURE - // Mask all interrupts except the receive interrupt - _rf24.maskIRQ(1,1,0); - - attachInterrupt(MY_RF24_IRQ_PIN, INT_EDGE_FALLING, RF24_irqHandler); - #endif - - return true; -} diff --git a/drivers/RF24/RF24_Linux.h b/drivers/RF24/RF24_Linux.h deleted file mode 100644 index d1e00ac..0000000 --- a/drivers/RF24/RF24_Linux.h +++ /dev/null @@ -1,302 +0,0 @@ -/** - * The MySensors Arduino library handles the wireless radio link and protocol - * between your home built sensors/actuators and HA controller of choice. - * The sensors forms a self healing radio network with optional repeaters. Each - * repeater and gateway builds a routing tables in EEPROM which keeps track of the - * network topology allowing messages to be routed to nodes. - * - * Created by Marcelo Aquino - * Copyright (C) 2016 Marcelo Aquino - * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors - * - * Documentation: http://www.mysensors.org - * Support Forum: http://forum.mysensors.org - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - */ - -#ifndef __RF24_LINUX_H__ -#define __RF24_LINUX_H__ - -// SPI settings -#if !defined(MY_RF24_SPI_MAX_SPEED) - #define MY_RF24_SPI_MAX_SPEED BCM2835_SPI_SPEED_8MHZ -#endif - -// verify RF24 IRQ defs -#if defined(MY_RX_MESSAGE_BUFFER_FEATURE) - #if !defined(MY_RF24_IRQ_PIN) - #error Message buffering feature requires MY_RF24_IRQ_PIN to be defined! - #endif - #ifndef SPI_HAS_TRANSACTION - #error RF24 IRQ usage requires transactional SPI support - #endif -#else - #ifdef MY_RX_MESSAGE_BUFFER_SIZE - #error Receive message buffering requires RF24 IRQ usage - #endif -#endif - -// pipes -#define BROADCAST_PIPE 1 -#define NODE_PIPE 2 - -// debug -#if defined(MY_DEBUG_VERBOSE_RF24) - #define RF24_DEBUG(x,...) debug(x, ##__VA_ARGS__) -#else - #define RF24_DEBUG(x,...) -#endif - -#ifdef LINUX_ARCH_RASPBERRYPI - // CE pin - #ifdef MY_RF24_CE_PIN - #ifdef __RPI_BPLUS - #if MY_RF24_CE_PIN == 3 - #define RF24_CE_PIN RPI_BPLUS_GPIO_J8_03 - #elif MY_RF24_CE_PIN == 5 - #define RF24_CE_PIN RPI_BPLUS_GPIO_J8_05 - #elif MY_RF24_CE_PIN == 7 - #define RF24_CE_PIN RPI_BPLUS_GPIO_J8_07 - #elif MY_RF24_CE_PIN == 8 - #define RF24_CE_PIN RPI_BPLUS_GPIO_J8_08 - #elif MY_RF24_CE_PIN == 10 - #define RF24_CE_PIN RPI_BPLUS_GPIO_J8_10 - #elif MY_RF24_CE_PIN == 11 - #define RF24_CE_PIN RPI_BPLUS_GPIO_J8_11 - #elif MY_RF24_CE_PIN == 12 - #define RF24_CE_PIN RPI_BPLUS_GPIO_J8_12 - #elif MY_RF24_CE_PIN == 13 - #define RF24_CE_PIN RPI_BPLUS_GPIO_J8_13 - #elif MY_RF24_CE_PIN == 15 - #define RF24_CE_PIN RPI_BPLUS_GPIO_J8_15 - #elif MY_RF24_CE_PIN == 16 - #define RF24_CE_PIN RPI_BPLUS_GPIO_J8_16 - #elif MY_RF24_CE_PIN == 18 - #define RF24_CE_PIN RPI_BPLUS_GPIO_J8_18 - #elif MY_RF24_CE_PIN == 19 - #define RF24_CE_PIN RPI_BPLUS_GPIO_J8_19 - #elif MY_RF24_CE_PIN == 21 - #define RF24_CE_PIN RPI_BPLUS_GPIO_J8_21 - #elif MY_RF24_CE_PIN == 22 - #define RF24_CE_PIN RPI_BPLUS_GPIO_J8_22 - #elif MY_RF24_CE_PIN == 23 - #define RF24_CE_PIN RPI_BPLUS_GPIO_J8_23 - #elif MY_RF24_CE_PIN == 24 - #define RF24_CE_PIN RPI_BPLUS_GPIO_J8_24 - #elif MY_RF24_CE_PIN == 26 - #define RF24_CE_PIN RPI_BPLUS_GPIO_J8_26 - #elif MY_RF24_CE_PIN == 29 - #define RF24_CE_PIN RPI_BPLUS_GPIO_J8_29 - #elif MY_RF24_CE_PIN == 31 - #define RF24_CE_PIN RPI_BPLUS_GPIO_J8_31 - #elif MY_RF24_CE_PIN == 32 - #define RF24_CE_PIN RPI_BPLUS_GPIO_J8_32 - #elif MY_RF24_CE_PIN == 33 - #define RF24_CE_PIN RPI_BPLUS_GPIO_J8_33 - #elif MY_RF24_CE_PIN == 35 - #define RF24_CE_PIN RPI_BPLUS_GPIO_J8_35 - #elif MY_RF24_CE_PIN == 36 - #define RF24_CE_PIN RPI_BPLUS_GPIO_J8_36 - #elif MY_RF24_CE_PIN == 37 - #define RF24_CE_PIN RPI_BPLUS_GPIO_J8_37 - #elif MY_RF24_CE_PIN == 38 - #define RF24_CE_PIN RPI_BPLUS_GPIO_J8_38 - #elif MY_RF24_CE_PIN == 40 - #define RF24_CE_PIN RPI_BPLUS_GPIO_J8_40 - #else - #error invalid value for MY_RF24_CE_PIN - #endif - #else - #if MY_RF24_CE_PIN == 3 - #define RF24_CE_PIN RPI_V2_GPIO_P1_03 - #elif MY_RF24_CE_PIN == 5 - #define RF24_CE_PIN RPI_V2_GPIO_P1_05 - #elif MY_RF24_CE_PIN == 7 - #define RF24_CE_PIN RPI_V2_GPIO_P1_07 - #elif MY_RF24_CE_PIN == 8 - #define RF24_CE_PIN RPI_V2_GPIO_P1_08 - #elif MY_RF24_CE_PIN == 10 - #define RF24_CE_PIN RPI_V2_GPIO_P1_10 - #elif MY_RF24_CE_PIN == 11 - #define RF24_CE_PIN RPI_V2_GPIO_P1_11 - #elif MY_RF24_CE_PIN == 12 - #define RF24_CE_PIN RPI_V2_GPIO_P1_12 - #elif MY_RF24_CE_PIN == 13 - #define RF24_CE_PIN RPI_V2_GPIO_P1_13 - #elif MY_RF24_CE_PIN == 15 - #define RF24_CE_PIN RPI_V2_GPIO_P1_15 - #elif MY_RF24_CE_PIN == 16 - #define RF24_CE_PIN RPI_V2_GPIO_P1_16 - #elif MY_RF24_CE_PIN == 18 - #define RF24_CE_PIN RPI_V2_GPIO_P1_18 - #elif MY_RF24_CE_PIN == 19 - #define RF24_CE_PIN RPI_V2_GPIO_P1_19 - #elif MY_RF24_CE_PIN == 21 - #define RF24_CE_PIN RPI_V2_GPIO_P1_21 - #elif MY_RF24_CE_PIN == 22 - #define RF24_CE_PIN RPI_V2_GPIO_P1_22 - #elif MY_RF24_CE_PIN == 23 - #define RF24_CE_PIN RPI_V2_GPIO_P1_23 - #elif MY_RF24_CE_PIN == 24 - #define RF24_CE_PIN RPI_V2_GPIO_P1_24 - #elif MY_RF24_CE_PIN == 26 - #define RF24_CE_PIN RPI_V2_GPIO_P1_26 - #elif MY_RF24_CE_PIN == 29 - #define RF24_CE_PIN RPI_V2_GPIO_P1_29 - #elif MY_RF24_CE_PIN == 31 - #define RF24_CE_PIN RPI_V2_GPIO_P1_31 - #elif MY_RF24_CE_PIN == 32 - #define RF24_CE_PIN RPI_V2_GPIO_P1_32 - #elif MY_RF24_CE_PIN == 33 - #define RF24_CE_PIN RPI_V2_GPIO_P1_33 - #elif MY_RF24_CE_PIN == 35 - #define RF24_CE_PIN RPI_V2_GPIO_P1_35 - #elif MY_RF24_CE_PIN == 36 - #define RF24_CE_PIN RPI_V2_GPIO_P1_36 - #elif MY_RF24_CE_PIN == 37 - #define RF24_CE_PIN RPI_V2_GPIO_P1_37 - #elif MY_RF24_CE_PIN == 38 - #define RF24_CE_PIN RPI_V2_GPIO_P1_38 - #elif MY_RF24_CE_PIN == 40 - #define RF24_CE_PIN RPI_V2_GPIO_P1_40 - #else - #error invalid value for MY_RF24_CE_PIN - #endif - #endif - #else - #ifdef __RPI_BPLUS - #define RF24_CE_PIN RPI_BPLUS_GPIO_J8_22 - #else - #define RF24_CE_PIN RPI_V2_GPIO_P1_22 - #endif - #endif - - // CS pin - #ifdef MY_RF24_CS_PIN - #ifdef __RPI_BPLUS - #if MY_RF24_CS_PIN == 3 - #define RF24_CS_PIN RPI_BPLUS_GPIO_J8_03 - #elif MY_RF24_CS_PIN == 5 - #define RF24_CS_PIN RPI_BPLUS_GPIO_J8_05 - #elif MY_RF24_CS_PIN == 7 - #define RF24_CS_PIN RPI_BPLUS_GPIO_J8_07 - #elif MY_RF24_CS_PIN == 8 - #define RF24_CS_PIN RPI_BPLUS_GPIO_J8_08 - #elif MY_RF24_CS_PIN == 10 - #define RF24_CS_PIN RPI_BPLUS_GPIO_J8_10 - #elif MY_RF24_CS_PIN == 11 - #define RF24_CS_PIN RPI_BPLUS_GPIO_J8_11 - #elif MY_RF24_CS_PIN == 12 - #define RF24_CS_PIN RPI_BPLUS_GPIO_J8_12 - #elif MY_RF24_CS_PIN == 13 - #define RF24_CS_PIN RPI_BPLUS_GPIO_J8_13 - #elif MY_RF24_CS_PIN == 15 - #define RF24_CS_PIN RPI_BPLUS_GPIO_J8_15 - #elif MY_RF24_CS_PIN == 16 - #define RF24_CS_PIN RPI_BPLUS_GPIO_J8_16 - #elif MY_RF24_CS_PIN == 18 - #define RF24_CS_PIN RPI_BPLUS_GPIO_J8_18 - #elif MY_RF24_CS_PIN == 19 - #define RF24_CS_PIN RPI_BPLUS_GPIO_J8_19 - #elif MY_RF24_CS_PIN == 21 - #define RF24_CS_PIN RPI_BPLUS_GPIO_J8_21 - #elif MY_RF24_CS_PIN == 22 - #define RF24_CS_PIN RPI_BPLUS_GPIO_J8_22 - #elif MY_RF24_CS_PIN == 23 - #define RF24_CS_PIN RPI_BPLUS_GPIO_J8_23 - #elif MY_RF24_CS_PIN == 24 - #define RF24_CS_PIN RPI_BPLUS_GPIO_J8_24 - #elif MY_RF24_CS_PIN == 26 - #define RF24_CS_PIN RPI_BPLUS_GPIO_J8_26 - #elif MY_RF24_CS_PIN == 29 - #define RF24_CS_PIN RPI_BPLUS_GPIO_J8_29 - #elif MY_RF24_CS_PIN == 31 - #define RF24_CS_PIN RPI_BPLUS_GPIO_J8_31 - #elif MY_RF24_CS_PIN == 32 - #define RF24_CS_PIN RPI_BPLUS_GPIO_J8_32 - #elif MY_RF24_CS_PIN == 33 - #define RF24_CS_PIN RPI_BPLUS_GPIO_J8_33 - #elif MY_RF24_CS_PIN == 35 - #define RF24_CS_PIN RPI_BPLUS_GPIO_J8_35 - #elif MY_RF24_CS_PIN == 36 - #define RF24_CS_PIN RPI_BPLUS_GPIO_J8_36 - #elif MY_RF24_CS_PIN == 37 - #define RF24_CS_PIN RPI_BPLUS_GPIO_J8_37 - #elif MY_RF24_CS_PIN == 38 - #define RF24_CS_PIN RPI_BPLUS_GPIO_J8_38 - #elif MY_RF24_CS_PIN == 40 - #define RF24_CS_PIN RPI_BPLUS_GPIO_J8_40 - #else - #error invalid value for MY_RF24_CS_PIN - #endif - #else - #if MY_RF24_CS_PIN == 3 - #define RF24_CS_PIN RPI_V2_GPIO_P1_03 - #elif MY_RF24_CS_PIN == 5 - #define RF24_CS_PIN RPI_V2_GPIO_P1_05 - #elif MY_RF24_CS_PIN == 7 - #define RF24_CS_PIN RPI_V2_GPIO_P1_07 - #elif MY_RF24_CS_PIN == 8 - #define RF24_CS_PIN RPI_V2_GPIO_P1_08 - #elif MY_RF24_CS_PIN == 10 - #define RF24_CS_PIN RPI_V2_GPIO_P1_10 - #elif MY_RF24_CS_PIN == 11 - #define RF24_CS_PIN RPI_V2_GPIO_P1_11 - #elif MY_RF24_CS_PIN == 12 - #define RF24_CS_PIN RPI_V2_GPIO_P1_12 - #elif MY_RF24_CS_PIN == 13 - #define RF24_CS_PIN RPI_V2_GPIO_P1_13 - #elif MY_RF24_CS_PIN == 15 - #define RF24_CS_PIN RPI_V2_GPIO_P1_15 - #elif MY_RF24_CS_PIN == 16 - #define RF24_CS_PIN RPI_V2_GPIO_P1_16 - #elif MY_RF24_CS_PIN == 18 - #define RF24_CS_PIN RPI_V2_GPIO_P1_18 - #elif MY_RF24_CS_PIN == 19 - #define RF24_CS_PIN RPI_V2_GPIO_P1_19 - #elif MY_RF24_CS_PIN == 21 - #define RF24_CS_PIN RPI_V2_GPIO_P1_21 - #elif MY_RF24_CS_PIN == 22 - #define RF24_CS_PIN RPI_V2_GPIO_P1_22 - #elif MY_RF24_CS_PIN == 23 - #define RF24_CS_PIN RPI_V2_GPIO_P1_23 - #elif MY_RF24_CS_PIN == 24 - #define RF24_CS_PIN BCM2835_SPI_CS0 - #elif MY_RF24_CS_PIN == 26 - #define RF24_CS_PIN BCM2835_SPI_CS1 - #elif MY_RF24_CS_PIN == 29 - #define RF24_CS_PIN RPI_V2_GPIO_P1_29 - #elif MY_RF24_CS_PIN == 31 - #define RF24_CS_PIN RPI_V2_GPIO_P1_31 - #elif MY_RF24_CS_PIN == 32 - #define RF24_CS_PIN RPI_V2_GPIO_P1_32 - #elif MY_RF24_CS_PIN == 33 - #define RF24_CS_PIN RPI_V2_GPIO_P1_33 - #elif MY_RF24_CS_PIN == 35 - #define RF24_CS_PIN RPI_V2_GPIO_P1_35 - #elif MY_RF24_CS_PIN == 36 - #define RF24_CS_PIN RPI_V2_GPIO_P1_36 - #elif MY_RF24_CS_PIN == 37 - #define RF24_CS_PIN RPI_V2_GPIO_P1_37 - #elif MY_RF24_CS_PIN == 38 - #define RF24_CS_PIN RPI_V2_GPIO_P1_38 - #elif MY_RF24_CS_PIN == 40 - #define RF24_CS_PIN RPI_V2_GPIO_P1_40 - #else - #error invalid value for MY_RF24_CS_PIN - #endif - #endif - #else - #ifdef __RPI_BPLUS - #define RF24_CS_PIN RPI_BPLUS_GPIO_J8_24 - #else - #define RF24_CS_PIN BCM2835_SPI_CS0 - #endif - #endif -#endif - -#endif diff --git a/drivers/RPi/SPI.cpp b/drivers/RPi/SPI.cpp new file mode 100644 index 0000000..8431daf --- /dev/null +++ b/drivers/RPi/SPI.cpp @@ -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 + * 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 + */ + +#include "SPI.h" +#include +#include +#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; +} diff --git a/drivers/RPi/SPI.h b/drivers/RPi/SPI.h new file mode 100644 index 0000000..84a4117 --- /dev/null +++ b/drivers/RPi/SPI.h @@ -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 + * 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 + */ +#ifndef _SPI_H_ +#define _SPI_H_ + +#include +#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 diff --git a/drivers/RPi/bcm2835.c b/drivers/RPi/bcm2835.c new file mode 100644 index 0000000..84a0f0d --- /dev/null +++ b/drivers/RPi/bcm2835.c @@ -0,0 +1,1504 @@ +/* bcm2835.c +// C and C++ support for Broadcom BCM 2835 as used in Raspberry Pi +// http://elinux.org/RPi_Low-level_peripherals +// http://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf +// +// Author: Mike McCauley +// Copyright (C) 2011-2013 Mike McCauley +// $Id: bcm2835.c,v 1.23 2015/03/31 04:55:41 mikem Exp mikem $ +// +// Modified September 2016 by Marcelo Aquino +*/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "log.h" + +#define BCK2835_LIBRARY_BUILD +#include "bcm2835.h" + +/* This define enables a little test program (by default a blinking output on pin RPI_GPIO_PIN_11) +// You can do some safe, non-destructive testing on any platform with: +// gcc bcm2835.c -D BCM2835_TEST +// ./a.out +*/ +/*#define BCM2835_TEST*/ + +/* Uncommenting this define compiles alternative I2C code for the version 1 RPi +// The P1 header I2C pins are connected to SDA0 and SCL0 on V1. +// By default I2C code is generated for the V2 RPi which has SDA1 and SCL1 connected. +*/ +/* #define I2C_V1*/ + +/* Physical address and size of the peripherals block +// May be overridden on RPi2 +*/ +uint32_t *bcm2835_peripherals_base = (uint32_t *)BCM2835_PERI_BASE; +uint32_t bcm2835_peripherals_size = BCM2835_PERI_SIZE; + +/* Virtual memory address of the mapped peripherals block + */ +uint32_t *bcm2835_peripherals = (uint32_t *)MAP_FAILED; + +/* And the register bases within the peripherals block + */ +volatile uint32_t *bcm2835_gpio = (uint32_t *)MAP_FAILED; +volatile uint32_t *bcm2835_pwm = (uint32_t *)MAP_FAILED; +volatile uint32_t *bcm2835_clk = (uint32_t *)MAP_FAILED; +volatile uint32_t *bcm2835_pads = (uint32_t *)MAP_FAILED; +volatile uint32_t *bcm2835_spi0 = (uint32_t *)MAP_FAILED; +volatile uint32_t *bcm2835_bsc0 = (uint32_t *)MAP_FAILED; +volatile uint32_t *bcm2835_bsc1 = (uint32_t *)MAP_FAILED; +volatile uint32_t *bcm2835_st = (uint32_t *)MAP_FAILED; + + +/* This variable allows us to test on hardware other than RPi. +// It prevents access to the kernel memory, and does not do any peripheral access +// Instead it prints out what it _would_ do if debug were 0 +*/ +static uint8_t debug = 0; + +/* I2C The time needed to transmit one byte. In microseconds. + */ +static int i2c_byte_wait_us = 0; + +/* +// Low level register access functions +*/ + +/* Function to return the pointers to the hardware register bases */ +uint32_t* bcm2835_regbase(uint8_t regbase) +{ + switch (regbase) + { + case BCM2835_REGBASE_ST: + return (uint32_t *)bcm2835_st; + case BCM2835_REGBASE_GPIO: + return (uint32_t *)bcm2835_gpio; + case BCM2835_REGBASE_PWM: + return (uint32_t *)bcm2835_pwm; + case BCM2835_REGBASE_CLK: + return (uint32_t *)bcm2835_clk; + case BCM2835_REGBASE_PADS: + return (uint32_t *)bcm2835_pads; + case BCM2835_REGBASE_SPI0: + return (uint32_t *)bcm2835_spi0; + case BCM2835_REGBASE_BSC0: + return (uint32_t *)bcm2835_bsc0; + case BCM2835_REGBASE_BSC1: + return (uint32_t *)bcm2835_st; + } + return (uint32_t *)MAP_FAILED; +} + +void bcm2835_set_debug(uint8_t d) +{ + debug = d; +} + +unsigned int bcm2835_version(void) +{ + return BCM2835_VERSION; +} + +/* Read with memory barriers from peripheral + * + */ +uint32_t bcm2835_peri_read(volatile uint32_t* paddr) +{ + uint32_t ret; + if (debug) + { + printf("bcm2835_peri_read paddr %08X\n", (unsigned) paddr); + return 0; + } + else + { + __sync_synchronize(); + ret = *paddr; + __sync_synchronize(); + return ret; + } +} + +/* read from peripheral without the read barrier + * This can only be used if more reads to THE SAME peripheral + * will follow. The sequence must terminate with memory barrier + * before any read or write to another peripheral can occur. + * The MB can be explicit, or one of the barrier read/write calls. + */ +uint32_t bcm2835_peri_read_nb(volatile uint32_t* paddr) +{ + if (debug) + { + printf("bcm2835_peri_read_nb paddr %08X\n", (unsigned) paddr); + return 0; + } + else + { + return *paddr; + } +} + +/* Write with memory barriers to peripheral + */ + +void bcm2835_peri_write(volatile uint32_t* paddr, uint32_t value) +{ + if (debug) + { + printf("bcm2835_peri_write paddr %08X, value %08X\n", (unsigned) paddr, value); + } + else + { + __sync_synchronize(); + *paddr = value; + __sync_synchronize(); + } +} + +/* write to peripheral without the write barrier */ +void bcm2835_peri_write_nb(volatile uint32_t* paddr, uint32_t value) +{ + if (debug) + { + printf("bcm2835_peri_write_nb paddr %08X, value %08X\n", + (unsigned) paddr, value); + } + else + { + *paddr = value; + } +} + +/* Set/clear only the bits in value covered by the mask + * This is not atomic - can be interrupted. + */ +void bcm2835_peri_set_bits(volatile uint32_t* paddr, uint32_t value, uint32_t mask) +{ + uint32_t v = bcm2835_peri_read(paddr); + v = (v & ~mask) | (value & mask); + bcm2835_peri_write(paddr, v); +} + +/* +// Low level convenience functions +*/ + +/* Function select +// pin is a BCM2835 GPIO pin number NOT RPi pin number +// There are 6 control registers, each control the functions of a block +// of 10 pins. +// Each control register has 10 sets of 3 bits per GPIO pin: +// +// 000 = GPIO Pin X is an input +// 001 = GPIO Pin X is an output +// 100 = GPIO Pin X takes alternate function 0 +// 101 = GPIO Pin X takes alternate function 1 +// 110 = GPIO Pin X takes alternate function 2 +// 111 = GPIO Pin X takes alternate function 3 +// 011 = GPIO Pin X takes alternate function 4 +// 010 = GPIO Pin X takes alternate function 5 +// +// So the 3 bits for port X are: +// X / 10 + ((X % 10) * 3) +*/ +void bcm2835_gpio_fsel(uint8_t pin, uint8_t mode) +{ + /* Function selects are 10 pins per 32 bit word, 3 bits per pin */ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPFSEL0/4 + (pin/10); + uint8_t shift = (pin % 10) * 3; + uint32_t mask = BCM2835_GPIO_FSEL_MASK << shift; + uint32_t value = mode << shift; + bcm2835_peri_set_bits(paddr, value, mask); +} + +/* Set output pin */ +void bcm2835_gpio_set(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPSET0/4 + pin/32; + uint8_t shift = pin % 32; + bcm2835_peri_write(paddr, 1 << shift); +} + +/* Clear output pin */ +void bcm2835_gpio_clr(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPCLR0/4 + pin/32; + uint8_t shift = pin % 32; + bcm2835_peri_write(paddr, 1 << shift); +} + +/* Set all output pins in the mask */ +void bcm2835_gpio_set_multi(uint32_t mask) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPSET0/4; + bcm2835_peri_write(paddr, mask); +} + +/* Clear all output pins in the mask */ +void bcm2835_gpio_clr_multi(uint32_t mask) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPCLR0/4; + bcm2835_peri_write(paddr, mask); +} + +/* Read input pin */ +uint8_t bcm2835_gpio_lev(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPLEV0/4 + pin/32; + uint8_t shift = pin % 32; + uint32_t value = bcm2835_peri_read(paddr); + return (value & (1 << shift)) ? HIGH : LOW; +} + +/* See if an event detection bit is set +// Sigh cant support interrupts yet +*/ +uint8_t bcm2835_gpio_eds(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPEDS0/4 + pin/32; + uint8_t shift = pin % 32; + uint32_t value = bcm2835_peri_read(paddr); + return (value & (1 << shift)) ? HIGH : LOW; +} + +uint32_t bcm2835_gpio_eds_multi(uint32_t mask) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPEDS0/4; + uint32_t value = bcm2835_peri_read(paddr); + return (value & mask); +} + +/* Write a 1 to clear the bit in EDS */ +void bcm2835_gpio_set_eds(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPEDS0/4 + pin/32; + uint8_t shift = pin % 32; + uint32_t value = 1 << shift; + bcm2835_peri_write(paddr, value); +} + +void bcm2835_gpio_set_eds_multi(uint32_t mask) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPEDS0/4; + bcm2835_peri_write(paddr, mask); +} + +/* Rising edge detect enable */ +void bcm2835_gpio_ren(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPREN0/4 + pin/32; + uint8_t shift = pin % 32; + uint32_t value = 1 << shift; + bcm2835_peri_set_bits(paddr, value, value); +} +void bcm2835_gpio_clr_ren(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPREN0/4 + pin/32; + uint8_t shift = pin % 32; + uint32_t value = 1 << shift; + bcm2835_peri_set_bits(paddr, 0, value); +} + +/* Falling edge detect enable */ +void bcm2835_gpio_fen(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPFEN0/4 + pin/32; + uint8_t shift = pin % 32; + uint32_t value = 1 << shift; + bcm2835_peri_set_bits(paddr, value, value); +} +void bcm2835_gpio_clr_fen(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPFEN0/4 + pin/32; + uint8_t shift = pin % 32; + uint32_t value = 1 << shift; + bcm2835_peri_set_bits(paddr, 0, value); +} + +/* High detect enable */ +void bcm2835_gpio_hen(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPHEN0/4 + pin/32; + uint8_t shift = pin % 32; + uint32_t value = 1 << shift; + bcm2835_peri_set_bits(paddr, value, value); +} +void bcm2835_gpio_clr_hen(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPHEN0/4 + pin/32; + uint8_t shift = pin % 32; + uint32_t value = 1 << shift; + bcm2835_peri_set_bits(paddr, 0, value); +} + +/* Low detect enable */ +void bcm2835_gpio_len(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPLEN0/4 + pin/32; + uint8_t shift = pin % 32; + uint32_t value = 1 << shift; + bcm2835_peri_set_bits(paddr, value, value); +} +void bcm2835_gpio_clr_len(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPLEN0/4 + pin/32; + uint8_t shift = pin % 32; + uint32_t value = 1 << shift; + bcm2835_peri_set_bits(paddr, 0, value); +} + +/* Async rising edge detect enable */ +void bcm2835_gpio_aren(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPAREN0/4 + pin/32; + uint8_t shift = pin % 32; + uint32_t value = 1 << shift; + bcm2835_peri_set_bits(paddr, value, value); +} +void bcm2835_gpio_clr_aren(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPAREN0/4 + pin/32; + uint8_t shift = pin % 32; + uint32_t value = 1 << shift; + bcm2835_peri_set_bits(paddr, 0, value); +} + +/* Async falling edge detect enable */ +void bcm2835_gpio_afen(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPAFEN0/4 + pin/32; + uint8_t shift = pin % 32; + uint32_t value = 1 << shift; + bcm2835_peri_set_bits(paddr, value, value); +} +void bcm2835_gpio_clr_afen(uint8_t pin) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPAFEN0/4 + pin/32; + uint8_t shift = pin % 32; + uint32_t value = 1 << shift; + bcm2835_peri_set_bits(paddr, 0, value); +} + +/* Set pullup/down */ +void bcm2835_gpio_pud(uint8_t pud) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPPUD/4; + bcm2835_peri_write(paddr, pud); +} + +/* Pullup/down clock +// Clocks the value of pud into the GPIO pin +*/ +void bcm2835_gpio_pudclk(uint8_t pin, uint8_t on) +{ + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPPUDCLK0/4 + pin/32; + uint8_t shift = pin % 32; + bcm2835_peri_write(paddr, (on ? 1 : 0) << shift); +} + +/* Read GPIO pad behaviour for groups of GPIOs */ +uint32_t bcm2835_gpio_pad(uint8_t group) +{ + if (bcm2835_pads == MAP_FAILED) { + return 0; + } + + volatile uint32_t* paddr = bcm2835_pads + BCM2835_PADS_GPIO_0_27/4 + group; + return bcm2835_peri_read(paddr); +} + +/* Set GPIO pad behaviour for groups of GPIOs +// powerup value for all pads is +// BCM2835_PAD_SLEW_RATE_UNLIMITED | BCM2835_PAD_HYSTERESIS_ENABLED | BCM2835_PAD_DRIVE_8mA +*/ +void bcm2835_gpio_set_pad(uint8_t group, uint32_t control) +{ + if (bcm2835_pads == MAP_FAILED) { + return; + } + + volatile uint32_t* paddr = bcm2835_pads + BCM2835_PADS_GPIO_0_27/4 + group; + bcm2835_peri_write(paddr, control | BCM2835_PAD_PASSWRD); +} + +/* Some convenient arduino-like functions +// milliseconds +*/ +void bcm2835_delay(unsigned int millis) +{ + struct timespec sleeper; + + sleeper.tv_sec = (time_t)(millis / 1000); + sleeper.tv_nsec = (long)(millis % 1000) * 1000000; + nanosleep(&sleeper, NULL); +} + +/* microseconds */ +void bcm2835_delayMicroseconds(uint64_t micros) +{ + struct timespec t1; + uint64_t start; + + if (debug) + { + /* Cant access sytem timers in debug mode */ + printf("bcm2835_delayMicroseconds %lld\n", micros); + return; + } + + /* Calling nanosleep() takes at least 100-200 us, so use it for + // long waits and use a busy wait on the System Timer for the rest. + */ + start = bcm2835_st_read(); + + if (micros > 450) + { + t1.tv_sec = 0; + t1.tv_nsec = 1000 * (long)(micros - 200); + nanosleep(&t1, NULL); + } + + bcm2835_st_delay(start, micros); +} + +/* +// Higher level convenience functions +*/ + +/* Set the state of an output */ +void bcm2835_gpio_write(uint8_t pin, uint8_t on) +{ + if (on) + bcm2835_gpio_set(pin); + else + bcm2835_gpio_clr(pin); +} + +/* Set the state of a all 32 outputs in the mask to on or off */ +void bcm2835_gpio_write_multi(uint32_t mask, uint8_t on) +{ + if (on) + bcm2835_gpio_set_multi(mask); + else + bcm2835_gpio_clr_multi(mask); +} + +/* Set the state of a all 32 outputs in the mask to the values in value */ +void bcm2835_gpio_write_mask(uint32_t value, uint32_t mask) +{ + bcm2835_gpio_set_multi(value & mask); + bcm2835_gpio_clr_multi((~value) & mask); +} + +/* Set the pullup/down resistor for a pin +// +// The GPIO Pull-up/down Clock Registers control the actuation of internal pull-downs on +// the respective GPIO pins. These registers must be used in conjunction with the GPPUD +// register to effect GPIO Pull-up/down changes. The following sequence of events is +// required: +// 1. Write to GPPUD to set the required control signal (i.e. Pull-up or Pull-Down or neither +// to remove the current Pull-up/down) +// 2. Wait 150 cycles ? this provides the required set-up time for the control signal +// 3. Write to GPPUDCLK0/1 to clock the control signal into the GPIO pads you wish to +// modify ? NOTE only the pads which receive a clock will be modified, all others will +// retain their previous state. +// 4. Wait 150 cycles ? this provides the required hold time for the control signal +// 5. Write to GPPUD to remove the control signal +// 6. Write to GPPUDCLK0/1 to remove the clock +// +// RPi has P1-03 and P1-05 with 1k8 pullup resistor +*/ +void bcm2835_gpio_set_pud(uint8_t pin, uint8_t pud) +{ + bcm2835_gpio_pud(pud); + delayMicroseconds(10); + bcm2835_gpio_pudclk(pin, 1); + delayMicroseconds(10); + bcm2835_gpio_pud(BCM2835_GPIO_PUD_OFF); + bcm2835_gpio_pudclk(pin, 0); +} + +int bcm2835_spi_begin(void) +{ + volatile uint32_t* paddr; + + if (bcm2835_spi0 == MAP_FAILED) + return 0; /* bcm2835_init() failed, or not root */ + + /* Set the SPI0 pins to the Alt 0 function to enable SPI0 access on them */ + bcm2835_gpio_fsel(RPI_GPIO_P1_26, BCM2835_GPIO_FSEL_ALT0); /* CE1 */ + bcm2835_gpio_fsel(RPI_GPIO_P1_24, BCM2835_GPIO_FSEL_ALT0); /* CE0 */ + bcm2835_gpio_fsel(RPI_GPIO_P1_21, BCM2835_GPIO_FSEL_ALT0); /* MISO */ + bcm2835_gpio_fsel(RPI_GPIO_P1_19, BCM2835_GPIO_FSEL_ALT0); /* MOSI */ + bcm2835_gpio_fsel(RPI_GPIO_P1_23, BCM2835_GPIO_FSEL_ALT0); /* CLK */ + + /* Set the SPI CS register to the some sensible defaults */ + paddr = bcm2835_spi0 + BCM2835_SPI0_CS/4; + bcm2835_peri_write(paddr, 0); /* All 0s */ + + /* Clear TX and RX fifos */ + bcm2835_peri_write_nb(paddr, BCM2835_SPI0_CS_CLEAR); + + return 1; // OK +} + +void bcm2835_spi_end(void) +{ + /* Set all the SPI0 pins back to input */ + bcm2835_gpio_fsel(RPI_GPIO_P1_26, BCM2835_GPIO_FSEL_INPT); /* CE1 */ + bcm2835_gpio_fsel(RPI_GPIO_P1_24, BCM2835_GPIO_FSEL_INPT); /* CE0 */ + bcm2835_gpio_fsel(RPI_GPIO_P1_21, BCM2835_GPIO_FSEL_INPT); /* MISO */ + bcm2835_gpio_fsel(RPI_GPIO_P1_19, BCM2835_GPIO_FSEL_INPT); /* MOSI */ + bcm2835_gpio_fsel(RPI_GPIO_P1_23, BCM2835_GPIO_FSEL_INPT); /* CLK */ +} + +void bcm2835_spi_setBitOrder(uint8_t __attribute__((unused)) order) +{ + /* BCM2835_SPI_BIT_ORDER_MSBFIRST is the only one supported by SPI0 */ +} + +/* defaults to 0, which means a divider of 65536. +// The divisor must be a power of 2. Odd numbers +// rounded down. The maximum SPI clock rate is +// of the APB clock +*/ +void bcm2835_spi_setClockDivider(uint16_t divider) +{ + volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CLK/4; + bcm2835_peri_write(paddr, divider); +} + +void bcm2835_spi_setDataMode(uint8_t mode) +{ + volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CS/4; + /* Mask in the CPO and CPHA bits of CS */ + bcm2835_peri_set_bits(paddr, mode << 2, BCM2835_SPI0_CS_CPOL | BCM2835_SPI0_CS_CPHA); +} + +/* Writes (and reads) a single byte to SPI */ +uint8_t bcm2835_spi_transfer(uint8_t value) +{ + volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CS/4; + volatile uint32_t* fifo = bcm2835_spi0 + BCM2835_SPI0_FIFO/4; + uint32_t ret; + + /* This is Polled transfer as per section 10.6.1 + // BUG ALERT: what happens if we get interupted in this section, and someone else + // accesses a different peripheral? + // Clear TX and RX fifos + */ + bcm2835_peri_set_bits(paddr, BCM2835_SPI0_CS_CLEAR, BCM2835_SPI0_CS_CLEAR); + + /* Set TA = 1 */ + bcm2835_peri_set_bits(paddr, BCM2835_SPI0_CS_TA, BCM2835_SPI0_CS_TA); + + /* Maybe wait for TXD */ + while (!(bcm2835_peri_read(paddr) & BCM2835_SPI0_CS_TXD)) + ; + + /* Write to FIFO, no barrier */ + bcm2835_peri_write_nb(fifo, value); + + /* Wait for DONE to be set */ + while (!(bcm2835_peri_read_nb(paddr) & BCM2835_SPI0_CS_DONE)) + ; + + /* Read any byte that was sent back by the slave while we sere sending to it */ + ret = bcm2835_peri_read_nb(fifo); + + /* Set TA = 0, and also set the barrier */ + bcm2835_peri_set_bits(paddr, 0, BCM2835_SPI0_CS_TA); + + return ret; +} + +/* Writes (and reads) an number of bytes to SPI */ +void bcm2835_spi_transfernb(char* tbuf, char* rbuf, uint32_t len) +{ + volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CS/4; + volatile uint32_t* fifo = bcm2835_spi0 + BCM2835_SPI0_FIFO/4; + uint32_t TXCnt=0; + uint32_t RXCnt=0; + + /* This is Polled transfer as per section 10.6.1 + // BUG ALERT: what happens if we get interupted in this section, and someone else + // accesses a different peripheral? + */ + + /* Clear TX and RX fifos */ + bcm2835_peri_set_bits(paddr, BCM2835_SPI0_CS_CLEAR, BCM2835_SPI0_CS_CLEAR); + + /* Set TA = 1 */ + bcm2835_peri_set_bits(paddr, BCM2835_SPI0_CS_TA, BCM2835_SPI0_CS_TA); + + /* Use the FIFO's to reduce the interbyte times */ + while((TXCnt < len)||(RXCnt < len)) + { + /* TX fifo not full, so add some more bytes */ + while(((bcm2835_peri_read(paddr) & BCM2835_SPI0_CS_TXD))&&(TXCnt < len )) + { + bcm2835_peri_write_nb(fifo, tbuf[TXCnt]); + TXCnt++; + } + /* Rx fifo not empty, so get the next received bytes */ + while(((bcm2835_peri_read(paddr) & BCM2835_SPI0_CS_RXD))&&( RXCnt < len )) + { + rbuf[RXCnt] = bcm2835_peri_read_nb(fifo); + RXCnt++; + } + } + /* Wait for DONE to be set */ + while (!(bcm2835_peri_read_nb(paddr) & BCM2835_SPI0_CS_DONE)) + ; + + /* Set TA = 0, and also set the barrier */ + bcm2835_peri_set_bits(paddr, 0, BCM2835_SPI0_CS_TA); +} + +/* Writes an number of bytes to SPI */ +void bcm2835_spi_writenb(char* tbuf, uint32_t len) +{ + volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CS/4; + volatile uint32_t* fifo = bcm2835_spi0 + BCM2835_SPI0_FIFO/4; + uint32_t i; + + /* This is Polled transfer as per section 10.6.1 + // BUG ALERT: what happens if we get interupted in this section, and someone else + // accesses a different peripheral? + // Answer: an ISR is required to issue the required memory barriers. + */ + + /* Clear TX and RX fifos */ + bcm2835_peri_set_bits(paddr, BCM2835_SPI0_CS_CLEAR, BCM2835_SPI0_CS_CLEAR); + + /* Set TA = 1 */ + bcm2835_peri_set_bits(paddr, BCM2835_SPI0_CS_TA, BCM2835_SPI0_CS_TA); + + for (i = 0; i < len; i++) + { + /* Maybe wait for TXD */ + while (!(bcm2835_peri_read(paddr) & BCM2835_SPI0_CS_TXD)) + ; + + /* Write to FIFO, no barrier */ + bcm2835_peri_write_nb(fifo, tbuf[i]); + + /* Read from FIFO to prevent stalling */ + while (bcm2835_peri_read(paddr) & BCM2835_SPI0_CS_RXD) + (void) bcm2835_peri_read_nb(fifo); + } + + /* Wait for DONE to be set */ + while (!(bcm2835_peri_read_nb(paddr) & BCM2835_SPI0_CS_DONE)) { + while (bcm2835_peri_read(paddr) & BCM2835_SPI0_CS_RXD) + (void) bcm2835_peri_read_nb(fifo); + }; + + /* Set TA = 0, and also set the barrier */ + bcm2835_peri_set_bits(paddr, 0, BCM2835_SPI0_CS_TA); +} + +/* Writes (and reads) an number of bytes to SPI +// Read bytes are copied over onto the transmit buffer +*/ +void bcm2835_spi_transfern(char* buf, uint32_t len) +{ + bcm2835_spi_transfernb(buf, buf, len); +} + +void bcm2835_spi_chipSelect(uint8_t cs) +{ + volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CS/4; + /* Mask in the CS bits of CS */ + bcm2835_peri_set_bits(paddr, cs, BCM2835_SPI0_CS_CS); +} + +void bcm2835_spi_setChipSelectPolarity(uint8_t cs, uint8_t active) +{ + volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CS/4; + uint8_t shift = 21 + cs; + /* Mask in the appropriate CSPOLn bit */ + bcm2835_peri_set_bits(paddr, active << shift, 1 << shift); +} + +int bcm2835_i2c_begin(void) +{ + uint16_t cdiv; + + if ( bcm2835_bsc0 == MAP_FAILED + || bcm2835_bsc1 == MAP_FAILED) + return 0; /* bcm2835_init() failed, or not root */ + +#ifdef I2C_V1 + volatile uint32_t* paddr = bcm2835_bsc0 + BCM2835_BSC_DIV/4; + /* Set the I2C/BSC0 pins to the Alt 0 function to enable I2C access on them */ + bcm2835_gpio_fsel(RPI_GPIO_P1_03, BCM2835_GPIO_FSEL_ALT0); /* SDA */ + bcm2835_gpio_fsel(RPI_GPIO_P1_05, BCM2835_GPIO_FSEL_ALT0); /* SCL */ +#else + volatile uint32_t* paddr = bcm2835_bsc1 + BCM2835_BSC_DIV/4; + /* Set the I2C/BSC1 pins to the Alt 0 function to enable I2C access on them */ + bcm2835_gpio_fsel(RPI_V2_GPIO_P1_03, BCM2835_GPIO_FSEL_ALT0); /* SDA */ + bcm2835_gpio_fsel(RPI_V2_GPIO_P1_05, BCM2835_GPIO_FSEL_ALT0); /* SCL */ +#endif + + /* Read the clock divider register */ + cdiv = bcm2835_peri_read(paddr); + /* Calculate time for transmitting one byte + // 1000000 = micros seconds in a second + // 9 = Clocks per byte : 8 bits + ACK + */ + i2c_byte_wait_us = ((float)cdiv / BCM2835_CORE_CLK_HZ) * 1000000 * 9; + + return 1; +} + +void bcm2835_i2c_end(void) +{ +#ifdef I2C_V1 + /* Set all the I2C/BSC0 pins back to input */ + bcm2835_gpio_fsel(RPI_GPIO_P1_03, BCM2835_GPIO_FSEL_INPT); /* SDA */ + bcm2835_gpio_fsel(RPI_GPIO_P1_05, BCM2835_GPIO_FSEL_INPT); /* SCL */ +#else + /* Set all the I2C/BSC1 pins back to input */ + bcm2835_gpio_fsel(RPI_V2_GPIO_P1_03, BCM2835_GPIO_FSEL_INPT); /* SDA */ + bcm2835_gpio_fsel(RPI_V2_GPIO_P1_05, BCM2835_GPIO_FSEL_INPT); /* SCL */ +#endif +} + +void bcm2835_i2c_setSlaveAddress(uint8_t addr) +{ + /* Set I2C Device Address */ +#ifdef I2C_V1 + volatile uint32_t* paddr = bcm2835_bsc0 + BCM2835_BSC_A/4; +#else + volatile uint32_t* paddr = bcm2835_bsc1 + BCM2835_BSC_A/4; +#endif + bcm2835_peri_write(paddr, addr); +} + +/* defaults to 0x5dc, should result in a 166.666 kHz I2C clock frequency. +// The divisor must be a power of 2. Odd numbers +// rounded down. +*/ +void bcm2835_i2c_setClockDivider(uint16_t divider) +{ +#ifdef I2C_V1 + volatile uint32_t* paddr = bcm2835_bsc0 + BCM2835_BSC_DIV/4; +#else + volatile uint32_t* paddr = bcm2835_bsc1 + BCM2835_BSC_DIV/4; +#endif + bcm2835_peri_write(paddr, divider); + /* Calculate time for transmitting one byte + // 1000000 = micros seconds in a second + // 9 = Clocks per byte : 8 bits + ACK + */ + i2c_byte_wait_us = ((float)divider / BCM2835_CORE_CLK_HZ) * 1000000 * 9; +} + +/* set I2C clock divider by means of a baudrate number */ +void bcm2835_i2c_set_baudrate(uint32_t baudrate) +{ + uint32_t divider; + /* use 0xFFFE mask to limit a max value and round down any odd number */ + divider = (BCM2835_CORE_CLK_HZ / baudrate) & 0xFFFE; + bcm2835_i2c_setClockDivider( (uint16_t)divider ); +} + +/* Writes an number of bytes to I2C */ +uint8_t bcm2835_i2c_write(const char * buf, uint32_t len) +{ +#ifdef I2C_V1 + volatile uint32_t* dlen = bcm2835_bsc0 + BCM2835_BSC_DLEN/4; + volatile uint32_t* fifo = bcm2835_bsc0 + BCM2835_BSC_FIFO/4; + volatile uint32_t* status = bcm2835_bsc0 + BCM2835_BSC_S/4; + volatile uint32_t* control = bcm2835_bsc0 + BCM2835_BSC_C/4; +#else + volatile uint32_t* dlen = bcm2835_bsc1 + BCM2835_BSC_DLEN/4; + volatile uint32_t* fifo = bcm2835_bsc1 + BCM2835_BSC_FIFO/4; + volatile uint32_t* status = bcm2835_bsc1 + BCM2835_BSC_S/4; + volatile uint32_t* control = bcm2835_bsc1 + BCM2835_BSC_C/4; +#endif + + uint32_t remaining = len; + uint32_t i = 0; + uint8_t reason = BCM2835_I2C_REASON_OK; + + /* Clear FIFO */ + bcm2835_peri_set_bits(control, BCM2835_BSC_C_CLEAR_1 , BCM2835_BSC_C_CLEAR_1 ); + /* Clear Status */ + bcm2835_peri_write(status, BCM2835_BSC_S_CLKT | BCM2835_BSC_S_ERR | BCM2835_BSC_S_DONE); + /* Set Data Length */ + bcm2835_peri_write(dlen, len); + /* pre populate FIFO with max buffer */ + while( remaining && ( i < BCM2835_BSC_FIFO_SIZE ) ) + { + bcm2835_peri_write_nb(fifo, buf[i]); + i++; + remaining--; + } + + /* Enable device and start transfer */ + bcm2835_peri_write(control, BCM2835_BSC_C_I2CEN | BCM2835_BSC_C_ST); + + /* Transfer is over when BCM2835_BSC_S_DONE */ + while(!(bcm2835_peri_read(status) & BCM2835_BSC_S_DONE )) + { + while ( remaining && (bcm2835_peri_read(status) & BCM2835_BSC_S_TXD )) + { + /* Write to FIFO */ + bcm2835_peri_write(fifo, buf[i]); + i++; + remaining--; + } + } + + /* Received a NACK */ + if (bcm2835_peri_read(status) & BCM2835_BSC_S_ERR) + { + reason = BCM2835_I2C_REASON_ERROR_NACK; + } + + /* Received Clock Stretch Timeout */ + else if (bcm2835_peri_read(status) & BCM2835_BSC_S_CLKT) + { + reason = BCM2835_I2C_REASON_ERROR_CLKT; + } + + /* Not all data is sent */ + else if (remaining) + { + reason = BCM2835_I2C_REASON_ERROR_DATA; + } + + bcm2835_peri_set_bits(control, BCM2835_BSC_S_DONE , BCM2835_BSC_S_DONE); + + return reason; +} + +/* Read an number of bytes from I2C */ +uint8_t bcm2835_i2c_read(char* buf, uint32_t len) +{ +#ifdef I2C_V1 + volatile uint32_t* dlen = bcm2835_bsc0 + BCM2835_BSC_DLEN/4; + volatile uint32_t* fifo = bcm2835_bsc0 + BCM2835_BSC_FIFO/4; + volatile uint32_t* status = bcm2835_bsc0 + BCM2835_BSC_S/4; + volatile uint32_t* control = bcm2835_bsc0 + BCM2835_BSC_C/4; +#else + volatile uint32_t* dlen = bcm2835_bsc1 + BCM2835_BSC_DLEN/4; + volatile uint32_t* fifo = bcm2835_bsc1 + BCM2835_BSC_FIFO/4; + volatile uint32_t* status = bcm2835_bsc1 + BCM2835_BSC_S/4; + volatile uint32_t* control = bcm2835_bsc1 + BCM2835_BSC_C/4; +#endif + + uint32_t remaining = len; + uint32_t i = 0; + uint8_t reason = BCM2835_I2C_REASON_OK; + + /* Clear FIFO */ + bcm2835_peri_set_bits(control, BCM2835_BSC_C_CLEAR_1 , BCM2835_BSC_C_CLEAR_1 ); + /* Clear Status */ + bcm2835_peri_write_nb(status, BCM2835_BSC_S_CLKT | BCM2835_BSC_S_ERR | BCM2835_BSC_S_DONE); + /* Set Data Length */ + bcm2835_peri_write_nb(dlen, len); + /* Start read */ + bcm2835_peri_write_nb(control, BCM2835_BSC_C_I2CEN | BCM2835_BSC_C_ST | BCM2835_BSC_C_READ); + + /* wait for transfer to complete */ + while (!(bcm2835_peri_read_nb(status) & BCM2835_BSC_S_DONE)) + { + /* we must empty the FIFO as it is populated and not use any delay */ + while (bcm2835_peri_read_nb(status) & BCM2835_BSC_S_RXD) + { + /* Read from FIFO, no barrier */ + buf[i] = bcm2835_peri_read_nb(fifo); + i++; + remaining--; + } + } + + /* transfer has finished - grab any remaining stuff in FIFO */ + while (remaining && (bcm2835_peri_read_nb(status) & BCM2835_BSC_S_RXD)) + { + /* Read from FIFO, no barrier */ + buf[i] = bcm2835_peri_read_nb(fifo); + i++; + remaining--; + } + + /* Received a NACK */ + if (bcm2835_peri_read(status) & BCM2835_BSC_S_ERR) + { + reason = BCM2835_I2C_REASON_ERROR_NACK; + } + + /* Received Clock Stretch Timeout */ + else if (bcm2835_peri_read(status) & BCM2835_BSC_S_CLKT) + { + reason = BCM2835_I2C_REASON_ERROR_CLKT; + } + + /* Not all data is received */ + else if (remaining) + { + reason = BCM2835_I2C_REASON_ERROR_DATA; + } + + bcm2835_peri_set_bits(control, BCM2835_BSC_S_DONE , BCM2835_BSC_S_DONE); + + return reason; +} + +/* Read an number of bytes from I2C sending a repeated start after writing +// the required register. Only works if your device supports this mode +*/ +uint8_t bcm2835_i2c_read_register_rs(char* regaddr, char* buf, uint32_t len) +{ +#ifdef I2C_V1 + volatile uint32_t* dlen = bcm2835_bsc0 + BCM2835_BSC_DLEN/4; + volatile uint32_t* fifo = bcm2835_bsc0 + BCM2835_BSC_FIFO/4; + volatile uint32_t* status = bcm2835_bsc0 + BCM2835_BSC_S/4; + volatile uint32_t* control = bcm2835_bsc0 + BCM2835_BSC_C/4; +#else + volatile uint32_t* dlen = bcm2835_bsc1 + BCM2835_BSC_DLEN/4; + volatile uint32_t* fifo = bcm2835_bsc1 + BCM2835_BSC_FIFO/4; + volatile uint32_t* status = bcm2835_bsc1 + BCM2835_BSC_S/4; + volatile uint32_t* control = bcm2835_bsc1 + BCM2835_BSC_C/4; +#endif + uint32_t remaining = len; + uint32_t i = 0; + uint8_t reason = BCM2835_I2C_REASON_OK; + + /* Clear FIFO */ + bcm2835_peri_set_bits(control, BCM2835_BSC_C_CLEAR_1 , BCM2835_BSC_C_CLEAR_1 ); + /* Clear Status */ + bcm2835_peri_write(status, BCM2835_BSC_S_CLKT | BCM2835_BSC_S_ERR | BCM2835_BSC_S_DONE); + /* Set Data Length */ + bcm2835_peri_write(dlen, 1); + /* Enable device and start transfer */ + bcm2835_peri_write(control, BCM2835_BSC_C_I2CEN); + bcm2835_peri_write(fifo, regaddr[0]); + bcm2835_peri_write(control, BCM2835_BSC_C_I2CEN | BCM2835_BSC_C_ST); + + /* poll for transfer has started */ + while ( !( bcm2835_peri_read(status) & BCM2835_BSC_S_TA ) ) + { + /* Linux may cause us to miss entire transfer stage */ + if(bcm2835_peri_read(status) & BCM2835_BSC_S_DONE) + break; + } + + /* Send a repeated start with read bit set in address */ + bcm2835_peri_write(dlen, len); + bcm2835_peri_write(control, BCM2835_BSC_C_I2CEN | BCM2835_BSC_C_ST | BCM2835_BSC_C_READ ); + + /* Wait for write to complete and first byte back. */ + bcm2835_delayMicroseconds(i2c_byte_wait_us * 3); + + /* wait for transfer to complete */ + while (!(bcm2835_peri_read(status) & BCM2835_BSC_S_DONE)) + { + /* we must empty the FIFO as it is populated and not use any delay */ + while (remaining && bcm2835_peri_read(status) & BCM2835_BSC_S_RXD) + { + /* Read from FIFO */ + buf[i] = bcm2835_peri_read(fifo); + i++; + remaining--; + } + } + + /* transfer has finished - grab any remaining stuff in FIFO */ + while (remaining && (bcm2835_peri_read(status) & BCM2835_BSC_S_RXD)) + { + /* Read from FIFO */ + buf[i] = bcm2835_peri_read(fifo); + i++; + remaining--; + } + + /* Received a NACK */ + if (bcm2835_peri_read(status) & BCM2835_BSC_S_ERR) + { + reason = BCM2835_I2C_REASON_ERROR_NACK; + } + + /* Received Clock Stretch Timeout */ + else if (bcm2835_peri_read(status) & BCM2835_BSC_S_CLKT) + { + reason = BCM2835_I2C_REASON_ERROR_CLKT; + } + + /* Not all data is sent */ + else if (remaining) + { + reason = BCM2835_I2C_REASON_ERROR_DATA; + } + + bcm2835_peri_set_bits(control, BCM2835_BSC_S_DONE , BCM2835_BSC_S_DONE); + + return reason; +} + +/* Sending an arbitrary number of bytes before issuing a repeated start +// (with no prior stop) and reading a response. Some devices require this behavior. +*/ +uint8_t bcm2835_i2c_write_read_rs(char* cmds, uint32_t cmds_len, char* buf, uint32_t buf_len) +{ +#ifdef I2C_V1 + volatile uint32_t* dlen = bcm2835_bsc0 + BCM2835_BSC_DLEN/4; + volatile uint32_t* fifo = bcm2835_bsc0 + BCM2835_BSC_FIFO/4; + volatile uint32_t* status = bcm2835_bsc0 + BCM2835_BSC_S/4; + volatile uint32_t* control = bcm2835_bsc0 + BCM2835_BSC_C/4; +#else + volatile uint32_t* dlen = bcm2835_bsc1 + BCM2835_BSC_DLEN/4; + volatile uint32_t* fifo = bcm2835_bsc1 + BCM2835_BSC_FIFO/4; + volatile uint32_t* status = bcm2835_bsc1 + BCM2835_BSC_S/4; + volatile uint32_t* control = bcm2835_bsc1 + BCM2835_BSC_C/4; +#endif + + uint32_t remaining = cmds_len; + uint32_t i = 0; + uint8_t reason = BCM2835_I2C_REASON_OK; + + /* Clear FIFO */ + bcm2835_peri_set_bits(control, BCM2835_BSC_C_CLEAR_1 , BCM2835_BSC_C_CLEAR_1 ); + + /* Clear Status */ + bcm2835_peri_write(status, BCM2835_BSC_S_CLKT | BCM2835_BSC_S_ERR | BCM2835_BSC_S_DONE); + + /* Set Data Length */ + bcm2835_peri_write(dlen, cmds_len); + + /* pre populate FIFO with max buffer */ + while( remaining && ( i < BCM2835_BSC_FIFO_SIZE ) ) + { + bcm2835_peri_write_nb(fifo, cmds[i]); + i++; + remaining--; + } + + /* Enable device and start transfer */ + bcm2835_peri_write(control, BCM2835_BSC_C_I2CEN | BCM2835_BSC_C_ST); + + /* poll for transfer has started (way to do repeated start, from BCM2835 datasheet) */ + while ( !( bcm2835_peri_read(status) & BCM2835_BSC_S_TA ) ) + { + /* Linux may cause us to miss entire transfer stage */ + if(bcm2835_peri_read_nb(status) & BCM2835_BSC_S_DONE) + break; + } + + remaining = buf_len; + i = 0; + + /* Send a repeated start with read bit set in address */ + bcm2835_peri_write(dlen, buf_len); + bcm2835_peri_write(control, BCM2835_BSC_C_I2CEN | BCM2835_BSC_C_ST | BCM2835_BSC_C_READ ); + + /* Wait for write to complete and first byte back. */ + bcm2835_delayMicroseconds(i2c_byte_wait_us * (cmds_len + 1)); + + /* wait for transfer to complete */ + while (!(bcm2835_peri_read_nb(status) & BCM2835_BSC_S_DONE)) + { + /* we must empty the FIFO as it is populated and not use any delay */ + while (remaining && bcm2835_peri_read(status) & BCM2835_BSC_S_RXD) + { + /* Read from FIFO, no barrier */ + buf[i] = bcm2835_peri_read_nb(fifo); + i++; + remaining--; + } + } + + /* transfer has finished - grab any remaining stuff in FIFO */ + while (remaining && (bcm2835_peri_read(status) & BCM2835_BSC_S_RXD)) + { + /* Read from FIFO */ + buf[i] = bcm2835_peri_read(fifo); + i++; + remaining--; + } + + /* Received a NACK */ + if (bcm2835_peri_read(status) & BCM2835_BSC_S_ERR) + { + reason = BCM2835_I2C_REASON_ERROR_NACK; + } + + /* Received Clock Stretch Timeout */ + else if (bcm2835_peri_read(status) & BCM2835_BSC_S_CLKT) + { + reason = BCM2835_I2C_REASON_ERROR_CLKT; + } + + /* Not all data is sent */ + else if (remaining) + { + reason = BCM2835_I2C_REASON_ERROR_DATA; + } + + bcm2835_peri_set_bits(control, BCM2835_BSC_S_DONE , BCM2835_BSC_S_DONE); + + return reason; +} + +/* Read the System Timer Counter (64-bits) */ +uint64_t bcm2835_st_read(void) +{ + volatile uint32_t* paddr; + uint32_t hi, lo; + uint64_t st; + paddr = bcm2835_st + BCM2835_ST_CHI/4; + hi = bcm2835_peri_read(paddr); + + paddr = bcm2835_st + BCM2835_ST_CLO/4; + lo = bcm2835_peri_read(paddr); + + paddr = bcm2835_st + BCM2835_ST_CHI/4; + st = bcm2835_peri_read(paddr); + + /* Test for overflow */ + if (st == hi) + { + st <<= 32; + st += lo; + } + else + { + st <<= 32; + paddr = bcm2835_st + BCM2835_ST_CLO/4; + st += bcm2835_peri_read(paddr); + } + return st; +} + +/* Delays for the specified number of microseconds with offset */ +void bcm2835_st_delay(uint64_t offset_micros, uint64_t micros) +{ + uint64_t compare = offset_micros + micros; + + while(bcm2835_st_read() < compare) + ; +} + +/* PWM */ + +void bcm2835_pwm_set_clock(uint32_t divisor) +{ + if ( bcm2835_clk == MAP_FAILED + || bcm2835_pwm == MAP_FAILED) + return; /* bcm2835_init() failed or not root */ + + /* From Gerts code */ + divisor &= 0xfff; + /* Stop PWM clock */ + bcm2835_peri_write(bcm2835_clk + BCM2835_PWMCLK_CNTL, BCM2835_PWM_PASSWRD | 0x01); + bcm2835_delay(110); /* Prevents clock going slow */ + /* Wait for the clock to be not busy */ + while ((bcm2835_peri_read(bcm2835_clk + BCM2835_PWMCLK_CNTL) & 0x80) != 0) + bcm2835_delay(1); + /* set the clock divider and enable PWM clock */ + bcm2835_peri_write(bcm2835_clk + BCM2835_PWMCLK_DIV, BCM2835_PWM_PASSWRD | (divisor << 12)); + bcm2835_peri_write(bcm2835_clk + BCM2835_PWMCLK_CNTL, BCM2835_PWM_PASSWRD | 0x11); /* Source=osc and enable */ +} + +void bcm2835_pwm_set_mode(uint8_t channel, uint8_t markspace, uint8_t enabled) +{ + if ( bcm2835_clk == MAP_FAILED + || bcm2835_pwm == MAP_FAILED) + return; /* bcm2835_init() failed or not root */ + + uint32_t control = bcm2835_peri_read(bcm2835_pwm + BCM2835_PWM_CONTROL); + + if (channel == 0) + { + if (markspace) + control |= BCM2835_PWM0_MS_MODE; + else + control &= ~BCM2835_PWM0_MS_MODE; + if (enabled) + control |= BCM2835_PWM0_ENABLE; + else + control &= ~BCM2835_PWM0_ENABLE; + } + else if (channel == 1) + { + if (markspace) + control |= BCM2835_PWM1_MS_MODE; + else + control &= ~BCM2835_PWM1_MS_MODE; + if (enabled) + control |= BCM2835_PWM1_ENABLE; + else + control &= ~BCM2835_PWM1_ENABLE; + } + + /* If you use the barrier here, wierd things happen, and the commands dont work */ + bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM_CONTROL, control); + /* bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM_CONTROL, BCM2835_PWM0_ENABLE | BCM2835_PWM1_ENABLE | BCM2835_PWM0_MS_MODE | BCM2835_PWM1_MS_MODE); */ + +} + +void bcm2835_pwm_set_range(uint8_t channel, uint32_t range) +{ + if ( bcm2835_clk == MAP_FAILED + || bcm2835_pwm == MAP_FAILED) + return; /* bcm2835_init() failed or not root */ + + if (channel == 0) + bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM0_RANGE, range); + else if (channel == 1) + bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM1_RANGE, range); +} + +void bcm2835_pwm_set_data(uint8_t channel, uint32_t data) +{ + if ( bcm2835_clk == MAP_FAILED + || bcm2835_pwm == MAP_FAILED) + return; /* bcm2835_init() failed or not root */ + + if (channel == 0) + bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM0_DATA, data); + else if (channel == 1) + bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM1_DATA, data); +} + +/* Allocate page-aligned memory. */ +void *malloc_aligned(size_t size) +{ + void *mem; + errno = posix_memalign(&mem, BCM2835_PAGE_SIZE, size); + return (errno ? NULL : mem); +} + +/* Map 'size' bytes starting at 'off' in file 'fd' to memory. +// Return mapped address on success, MAP_FAILED otherwise. +// On error print message. +*/ +static void *mapmem(const char *msg, size_t size, int fd, off_t off) +{ + void *map = mmap(NULL, size, (PROT_READ | PROT_WRITE), MAP_SHARED, fd, off); + if (map == MAP_FAILED) + mys_log(LOG_ERR, "bcm2835_init: %s mmap failed: %s\n", msg, strerror(errno)); + return map; +} + +static void unmapmem(void **pmem, size_t size) +{ + if (*pmem == MAP_FAILED) return; + munmap(*pmem, size); + *pmem = MAP_FAILED; +} + +/* Initialise this library. */ +int bcm2835_init(void) +{ + int memfd; + int ok; + FILE *fp; + + if (debug) + { + bcm2835_peripherals = (uint32_t*)BCM2835_PERI_BASE; + + bcm2835_pads = bcm2835_peripherals + BCM2835_GPIO_PADS/4; + bcm2835_clk = bcm2835_peripherals + BCM2835_CLOCK_BASE/4; + bcm2835_gpio = bcm2835_peripherals + BCM2835_GPIO_BASE/4; + bcm2835_pwm = bcm2835_peripherals + BCM2835_GPIO_PWM/4; + bcm2835_spi0 = bcm2835_peripherals + BCM2835_SPI0_BASE/4; + bcm2835_bsc0 = bcm2835_peripherals + BCM2835_BSC0_BASE/4; + bcm2835_bsc1 = bcm2835_peripherals + BCM2835_BSC1_BASE/4; + bcm2835_st = bcm2835_peripherals + BCM2835_ST_BASE/4; + return 1; /* Success */ + } + + /* Figure out the base and size of the peripheral address block + // using the device-tree. Required for RPi2, optional for RPi 1 + */ + if ((fp = fopen(BMC2835_RPI2_DT_FILENAME , "rb"))) + { + unsigned char buf[4]; + fseek(fp, BMC2835_RPI2_DT_PERI_BASE_ADDRESS_OFFSET, SEEK_SET); + if (fread(buf, 1, sizeof(buf), fp) == sizeof(buf)) + bcm2835_peripherals_base = (uint32_t *)(buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3] << 0); + fseek(fp, BMC2835_RPI2_DT_PERI_SIZE_OFFSET, SEEK_SET); + if (fread(buf, 1, sizeof(buf), fp) == sizeof(buf)) + bcm2835_peripherals_size = (buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3] << 0); + fclose(fp); + } + /* else we are prob on RPi 1 with BCM2835, and use the hardwired defaults */ + + /* Now get ready to map the peripherals block + * If we are not root, try for the new /dev/gpiomem interface and accept + * the fact that we can only access GPIO + * else try for the /dev/mem interface and get access to everything + */ + memfd = -1; + ok = 0; + if (geteuid() == 0) + { + /* Open the master /dev/mem device */ + if ((memfd = open("/dev/mem", O_RDWR | O_SYNC) ) < 0) + { + mys_log(LOG_ERR, "bcm2835_init: Unable to open /dev/mem: %s\n", + strerror(errno)) ; + goto exit; + } + + /* Base of the peripherals block is mapped to VM */ + bcm2835_peripherals = mapmem("gpio", bcm2835_peripherals_size, memfd, (uint32_t)bcm2835_peripherals_base); + if (bcm2835_peripherals == MAP_FAILED) goto exit; + + /* Now compute the base addresses of various peripherals, + // which are at fixed offsets within the mapped peripherals block + // Caution: bcm2835_peripherals is uint32_t*, so divide offsets by 4 + */ + bcm2835_gpio = bcm2835_peripherals + BCM2835_GPIO_BASE/4; + bcm2835_pwm = bcm2835_peripherals + BCM2835_GPIO_PWM/4; + bcm2835_clk = bcm2835_peripherals + BCM2835_CLOCK_BASE/4; + bcm2835_pads = bcm2835_peripherals + BCM2835_GPIO_PADS/4; + bcm2835_spi0 = bcm2835_peripherals + BCM2835_SPI0_BASE/4; + bcm2835_bsc0 = bcm2835_peripherals + BCM2835_BSC0_BASE/4; /* I2C */ + bcm2835_bsc1 = bcm2835_peripherals + BCM2835_BSC1_BASE/4; /* I2C */ + bcm2835_st = bcm2835_peripherals + BCM2835_ST_BASE/4; + + ok = 1; + } + else + { + /* Not root, try /dev/gpiomem */ + /* Open the master /dev/mem device */ + if ((memfd = open("/dev/gpiomem", O_RDWR | O_SYNC) ) < 0) + { + mys_log(LOG_ERR, "bcm2835_init: Unable to open /dev/gpiomem: %s\n", + strerror(errno)) ; + goto exit; + } + + /* Base of the peripherals block is mapped to VM */ + bcm2835_peripherals_base = 0; + bcm2835_peripherals = mapmem("gpio", bcm2835_peripherals_size, memfd, (uint32_t)bcm2835_peripherals_base); + if (bcm2835_peripherals == MAP_FAILED) goto exit; + bcm2835_gpio = bcm2835_peripherals; + ok = 1; + } + +exit: + if (memfd >= 0) + close(memfd); + + if (!ok) + bcm2835_close(); + + return ok; +} + +/* Close this library and deallocate everything */ +int bcm2835_close(void) +{ + if (debug) return 1; /* Success */ + + unmapmem((void**) &bcm2835_peripherals, bcm2835_peripherals_size); + bcm2835_peripherals = MAP_FAILED; + bcm2835_gpio = MAP_FAILED; + bcm2835_pwm = MAP_FAILED; + bcm2835_clk = MAP_FAILED; + bcm2835_pads = MAP_FAILED; + bcm2835_spi0 = MAP_FAILED; + bcm2835_bsc0 = MAP_FAILED; + bcm2835_bsc1 = MAP_FAILED; + bcm2835_st = MAP_FAILED; + return 1; /* Success */ +} + +#ifdef BCM2835_TEST +/* this is a simple test program that prints out what it will do rather than +// actually doing it +*/ +int main(int argc, char **argv) +{ + /* Be non-destructive */ + bcm2835_set_debug(1); + + if (!bcm2835_init()) + return 1; + + /* Configure some GPIO pins fo some testing + // Set RPI pin P1-11 to be an output + */ + bcm2835_gpio_fsel(RPI_GPIO_P1_11, BCM2835_GPIO_FSEL_OUTP); + /* Set RPI pin P1-15 to be an input */ + bcm2835_gpio_fsel(RPI_GPIO_P1_15, BCM2835_GPIO_FSEL_INPT); + /* with a pullup */ + bcm2835_gpio_set_pud(RPI_GPIO_P1_15, BCM2835_GPIO_PUD_UP); + /* And a low detect enable */ + bcm2835_gpio_len(RPI_GPIO_P1_15); + /* and input hysteresis disabled on GPIOs 0 to 27 */ + bcm2835_gpio_set_pad(BCM2835_PAD_GROUP_GPIO_0_27, BCM2835_PAD_SLEW_RATE_UNLIMITED|BCM2835_PAD_DRIVE_8mA); + +#if 1 + /* Blink */ + while (1) + { + /* Turn it on */ + bcm2835_gpio_write(RPI_GPIO_P1_11, HIGH); + + /* wait a bit */ + bcm2835_delay(500); + + /* turn it off */ + bcm2835_gpio_write(RPI_GPIO_P1_11, LOW); + + /* wait a bit */ + bcm2835_delay(500); + } +#endif + +#if 0 + /* Read input */ + while (1) + { + /* Read some data */ + uint8_t value = bcm2835_gpio_lev(RPI_GPIO_P1_15); + printf("read from pin 15: %d\n", value); + + /* wait a bit */ + bcm2835_delay(500); + } +#endif + +#if 0 + /* Look for a low event detection + // eds will be set whenever pin 15 goes low + */ + while (1) + { + if (bcm2835_gpio_eds(RPI_GPIO_P1_15)) + { + /* Now clear the eds flag by setting it to 1 */ + bcm2835_gpio_set_eds(RPI_GPIO_P1_15); + printf("low event detect for pin 15\n"); + } + + /* wait a bit */ + bcm2835_delay(500); + } +#endif + + if (!bcm2835_close()) + return 1; + + return 0; +} +#endif diff --git a/drivers/RPi/bcm2835.h b/drivers/RPi/bcm2835.h new file mode 100644 index 0000000..26acee2 --- /dev/null +++ b/drivers/RPi/bcm2835.h @@ -0,0 +1,1648 @@ +/* bcm2835.h + + C and C++ support for Broadcom BCM 2835 as used in Raspberry Pi + + Author: Mike McCauley + Copyright (C) 2011-2013 Mike McCauley + $Id: bcm2835.h,v 1.20 2015/03/31 04:55:41 mikem Exp mikem $ +*/ + +/*! \defgroup BCM2835grp C library for Broadcom BCM 2835 as used in Raspberry Pi + \ingroup internals + + This is a C library for Raspberry Pi (RPi). It provides access to + GPIO and other IO functions on the Broadcom BCM 2835 chip, + allowing access to the GPIO pins on the + 26 pin IDE plug on the RPi board so you can control and interface with various external devices. + + It provides functions for reading digital inputs and setting digital outputs, using SPI and I2C, + and for accessing the system timers. + Pin event detection is supported by polling (interrupts are not supported). + + It is C++ compatible, and installs as a header file and non-shared library on + any Linux-based distro (but clearly is no use except on Raspberry Pi or another board with + BCM 2835). + + The version of the package that this documentation refers to can be downloaded + from http://www.airspayce.com/mikem/bcm2835/bcm2835-1.50.tar.gz + You can find the latest version at http://www.airspayce.com/mikem/bcm2835 + + Several example programs are provided. + + Based on data in http://elinux.org/RPi_Low-level_peripherals and + http://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf + and http://www.scribd.com/doc/101830961/GPIO-Pads-Control2 + + You can also find online help and discussion at http://groups.google.com/group/bcm2835 + Please use that group for all questions and discussions on this topic. + Do not contact the author directly, unless it is to discuss commercial licensing. + Before asking a question or reporting a bug, please read http://www.catb.org/esr/faqs/smart-questions.html + + Tested on debian6-19-04-2012, 2012-07-15-wheezy-raspbian, 2013-07-26-wheezy-raspbian + and Occidentalisv01, 2016-02-09 Raspbian Jessie. + CAUTION: it has been observed that when detect enables such as bcm2835_gpio_len() + are used and the pin is pulled LOW + it can cause temporary hangs on 2012-07-15-wheezy-raspbian, 2013-07-26-wheezy-raspbian + and Occidentalisv01. + Reason for this is not yet determined, but we suspect that an interrupt handler is + hitting a hard loop on those OSs. + If you must use bcm2835_gpio_len() and friends, make sure you disable the pins with + bcm2835_gpio_clr_len() and friends after use. + + \par Running as root + + Prior to the release of Raspbian Jessie in Feb 2016, access to any + peripheral device via /dev/mem on the RPi required the process to + run as root. Raspbian Jessie permits non-root users to access the + GPIO peripheral (only) via /dev/gpiomem, and this library supports + that limited mode of operation. + + If the library runs with effective UID of 0 (ie root), then + bcm2835_init() will attempt to open /dev/mem, and, if successful, it + will permit use of all peripherals and library functions. + + If the library runs with any other effective UID (ie not root), then + bcm2835_init() will attempt to open /dev/gpiomem, and, if + successful, will only permit GPIO operations. In particular, + bcm2835_spi_begin() and bcm2835_i2c_begin() will return false and all + other non-gpio operations may fail silently or crash. + + \par Installation + + This library consists of a single non-shared library and header file, which will be + installed in the usual places by make install + + \code + # download the latest version of the library, say bcm2835-1.xx.tar.gz, then: + tar zxvf bcm2835-1.xx.tar.gz + cd bcm2835-1.xx + ./configure + make + sudo make check + sudo make install + \endcode + + \par Physical Addresses + + The functions bcm2835_peri_read(), bcm2835_peri_write() and bcm2835_peri_set_bits() + are low level peripheral register access functions. They are designed to use + physical addresses as described in section 1.2.3 ARM physical addresses + of the BCM2835 ARM Peripherals manual. + Physical addresses range from 0x20000000 to 0x20FFFFFF for peripherals. The bus + addresses for peripherals are set up to map onto the peripheral bus address range starting at + 0x7E000000. Thus a peripheral advertised in the manual at bus address 0x7Ennnnnn is available at + physical address 0x20nnnnnn. + + On RPI 2, the peripheral addresses are different and the bcm2835 library gets them + from reading /proc/device-tree/soc/ranges. This is only availble with recent versions of the kernel on RPI 2. + + After initialisation, the base address of the various peripheral + registers are available with the following + externals: + bcm2835_gpio + bcm2835_pwm + bcm2835_clk + bcm2835_pads + bcm2835_spio0 + bcm2835_st + bcm2835_bsc0 + bcm2835_bsc1 + + \par Raspberry Pi 2 (RPI2) + + For this library to work correctly on RPI2, you MUST have the device tree support enabled in the kernel. + You should also ensure you are using the latest version of Linux. The library has been tested on RPI2 + with 2015-02-16-raspbian-wheezy and ArchLinuxARM-rpi-2 as of 2015-03-29. + + When device tree suport is enabled, the file /proc/device-tree/soc/ranges will appear in the file system, + and the bcm2835 module relies on its presence to correctly run on RPI2 (it is optional for RPI1). + Without device tree support enabled and the presence of this file, it will not work on RPI2. + + To enable device tree support: + + \code + sudo raspi-config + under Advanced Options - enable Device Tree + Reboot. + \endcode + + \par Pin Numbering + + The GPIO pin numbering as used by RPi is different to and inconsistent with the underlying + BCM 2835 chip pin numbering. http://elinux.org/RPi_BCM2835_GPIOs + + RPi has a 26 pin IDE header that provides access to some of the GPIO pins on the BCM 2835, + as well as power and ground pins. Not all GPIO pins on the BCM 2835 are available on the + IDE header. + + RPi Version 2 also has a P5 connector with 4 GPIO pins, 5V, 3.3V and Gnd. + + The functions in this library are designed to be passed the BCM 2835 GPIO pin number and _not_ + the RPi pin number. There are symbolic definitions for each of the available pins + that you should use for convenience. See \ref RPiGPIOPin. + + \par SPI Pins + + The bcm2835_spi_* functions allow you to control the BCM 2835 SPI0 interface, + allowing you to send and received data by SPI (Serial Peripheral Interface). + For more information about SPI, see http://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus + + When bcm2835_spi_begin() is called it changes the bahaviour of the SPI interface pins from their + default GPIO behaviour in order to support SPI. While SPI is in use, you will not be able + to control the state of the SPI pins through the usual bcm2835_spi_gpio_write(). + When bcm2835_spi_end() is called, the SPI pins will all revert to inputs, and can then be + configured and controled with the usual bcm2835_gpio_* calls. + + The Raspberry Pi GPIO pins used for SPI are: + + - P1-19 (MOSI) + - P1-21 (MISO) + - P1-23 (CLK) + - P1-24 (CE0) + - P1-26 (CE1) + + \par I2C Pins + + The bcm2835_i2c_* functions allow you to control the BCM 2835 BSC interface, + allowing you to send and received data by I2C ("eye-squared cee"; generically referred to as "two-wire interface") . + For more information about I?C, see http://en.wikipedia.org/wiki/I%C2%B2C + + The Raspberry Pi V2 GPIO pins used for I2C are: + + - P1-03 (SDA) + - P1-05 (SLC) + + \par PWM + + The BCM2835 supports hardware PWM on a limited subset of GPIO pins. This bcm2835 library provides + functions for configuring and controlling PWM output on these pins. + + The BCM2835 contains 2 independent PWM channels (0 and 1), each of which be connnected to a limited subset of + GPIO pins. The following GPIO pins may be connected to the following PWM channels (from section 9.5): + \code + GPIO PIN RPi pin PWM Channel ALT FUN + 12 0 0 + 13 1 0 + 18 1-12 0 5 + 19 1 5 + 40 0 0 + 41 1 0 + 45 1 0 + 52 0 1 + 53 1 1 + \endcode + In order for a GPIO pin to emit output from its PWM channel, it must be set to the Alt Function given above. + Note carefully that current versions of the Raspberry Pi only expose one of these pins (GPIO 18 = RPi Pin 1-12) + on the IO headers, and therefore this is the only IO pin on the RPi that can be used for PWM. + Further it must be set to ALT FUN 5 to get PWM output. + + Both PWM channels are driven by the same PWM clock, whose clock dvider can be varied using + bcm2835_pwm_set_clock(). Each channel can be separately enabled with bcm2835_pwm_set_mode(). + The average output of the PWM channel is determined by the ratio of DATA/RANGE for that channel. + Use bcm2835_pwm_set_range() to set the range and bcm2835_pwm_set_data() to set the data in that ratio + + Each PWM channel can run in either Balanced or Mark-Space mode. In Balanced mode, the hardware + sends a combination of clock pulses that results in an overall DATA pulses per RANGE pulses. + In Mark-Space mode, the hardware sets the output HIGH for DATA clock pulses wide, followed by + LOW for RANGE-DATA clock pulses. + + The PWM clock can be set to control the PWM pulse widths. The PWM clock is derived from + a 19.2MHz clock. You can set any divider, but some common ones are provided by the BCM2835_PWM_CLOCK_DIVIDER_* + values of \ref bcm2835PWMClockDivider. + + For example, say you wanted to drive a DC motor with PWM at about 1kHz, + and control the speed in 1/1024 increments from + 0/1024 (stopped) through to 1024/1024 (full on). In that case you might set the + clock divider to be 16, and the RANGE to 1024. The pulse repetition frequency will be + 1.2MHz/1024 = 1171.875Hz. + + \par SPI + + In order for bcm2835 library SPI to work, you may need to disable the SPI kernel module using: + + \code + sudo raspi-config + under Advanced Options - enable Device Tree + under Advanced Options - disable SPI + Reboot. + \endcode + + \par Real Time performance constraints + + The bcm2835 is a library for user programs (i.e. they run in 'userland'). + Such programs are not part of the kernel and are usually + subject to paging and swapping by the kernel while it does other things besides running your program. + This means that you should not expect to get real-time performance or + real-time timing constraints from such programs. In particular, there is no guarantee that the + bcm2835_delay() and bcm2835_delayMicroseconds() will return after exactly the time requested. + In fact, depending on other activity on the host, IO etc, you might get significantly longer delay times + than the one you asked for. So please dont expect to get exactly the time delay you request. + + Arjan reports that you can prevent swapping on Linux with the following code fragment: + + \code + struct sched_param sp; + memset(&sp, 0, sizeof(sp)); + sp.sched_priority = sched_get_priority_max(SCHED_FIFO); + sched_setscheduler(0, SCHED_FIFO, &sp); + mlockall(MCL_CURRENT | MCL_FUTURE); + \endcode + + \par Bindings to other languages + + mikem has made Perl bindings available at CPAN: + http://search.cpan.org/~mikem/Device-BCM2835-1.9/lib/Device/BCM2835.pm + Matthew Baker has kindly made Python bindings available at: + https: github.com/mubeta06/py-libbcm2835 + Gary Marks has created a Serial Peripheral Interface (SPI) command-line utility + for Raspberry Pi, based on the bcm2835 library. The + utility, spincl, is licensed under Open Source GNU GPLv3 by iP Solutions (http://ipsolutionscorp.com), as a + free download with source included: http://ipsolutionscorp.com/raspberry-pi-spi-utility/ + + \par Open Source Licensing GPL V2 + + This is the appropriate option if you want to share the source code of your + application with everyone you distribute it to, and you also want to give them + the right to share who uses it. If you wish to use this software under Open + Source Licensing, you must contribute all your source code to the open source + community in accordance with the GPL Version 2 when your application is + distributed. See http://www.gnu.org/copyleft/gpl.html and COPYING + + \par Acknowledgements + + Some of this code has been inspired by Dom and Gert. + The I2C code has been inspired by Alan Barr. + + \par Revision History + + \version 1.0 Initial release + + \version 1.1 Minor bug fixes + + \version 1.2 Added support for SPI + + \version 1.3 Added bcm2835_spi_transfern() + + \version 1.4 Fixed a problem that prevented SPI CE1 being used. Reported by David Robinson. + + \version 1.5 Added bcm2835_close() to deinit the library. Suggested by C?sar Ortiz + + \version 1.6 Document testing on 2012-07-15-wheezy-raspbian and Occidentalisv01 + Functions bcm2835_gpio_ren(), bcm2835_gpio_fen(), bcm2835_gpio_hen() + bcm2835_gpio_len(), bcm2835_gpio_aren() and bcm2835_gpio_afen() now + changes only the pin specified. Other pins that were already previously + enabled stay enabled. + Added bcm2835_gpio_clr_ren(), bcm2835_gpio_clr_fen(), bcm2835_gpio_clr_hen() + bcm2835_gpio_clr_len(), bcm2835_gpio_clr_aren(), bcm2835_gpio_clr_afen() + to clear the enable for individual pins, suggested by Andreas Sundstrom. + + \version 1.7 Added bcm2835_spi_transfernb to support different buffers for read and write. + + \version 1.8 Improvements to read barrier, as suggested by maddin. + + \version 1.9 Improvements contributed by mikew: + I noticed that it was mallocing memory for the mmaps on /dev/mem. + It's not necessary to do that, you can just mmap the file directly, + so I've removed the mallocs (and frees). + I've also modified delayMicroseconds() to use nanosleep() for long waits, + and a busy wait on a high resolution timer for the rest. This is because + I've found that calling nanosleep() takes at least 100-200 us. + You need to link using '-lrt' using this version. + I've added some unsigned casts to the debug prints to silence compiler + warnings I was getting, fixed some typos, and changed the value of + BCM2835_PAD_HYSTERESIS_ENABLED to 0x08 as per Gert van Loo's doc at + http://www.scribd.com/doc/101830961/GPIO-Pads-Control2 + Also added a define for the passwrd value that Gert says is needed to + change pad control settings. + + \version 1.10 Changed the names of the delay functions to bcm2835_delay() + and bcm2835_delayMicroseconds() to prevent collisions with wiringPi. + Macros to map delay()-> bcm2835_delay() and + Macros to map delayMicroseconds()-> bcm2835_delayMicroseconds(), which + can be disabled by defining BCM2835_NO_DELAY_COMPATIBILITY + + \version 1.11 Fixed incorrect link to download file + + \version 1.12 New GPIO pin definitions for RPi version 2 (which has a different GPIO mapping) + + \version 1.13 New GPIO pin definitions for RPi version 2 plug P5 + Hardware base pointers are now available (after initialisation) externally as bcm2835_gpio + bcm2835_pwm bcm2835_clk bcm2835_pads bcm2835_spi0. + + \version 1.14 Now compiles even if CLOCK_MONOTONIC_RAW is not available, uses CLOCK_MONOTONIC instead. + Fixed errors in documentation of SPI divider frequencies based on 250MHz clock. + Reported by Ben Simpson. + + \version 1.15 Added bcm2835_close() to end of examples as suggested by Mark Wolfe. + + \version 1.16 Added bcm2835_gpio_set_multi, bcm2835_gpio_clr_multi and bcm2835_gpio_write_multi + to allow a mask of pins to be set all at once. Requested by Sebastian Loncar. + + \version 1.17 Added bcm2835_gpio_write_mask. Requested by Sebastian Loncar. + + \version 1.18 Added bcm2835_i2c_* functions. Changes to bcm2835_delayMicroseconds: + now uses the RPi system timer counter, instead of clock_gettime, for improved accuracy. + No need to link with -lrt now. Contributed by Arjan van Vught. + \version 1.19 Removed inlines added by previous patch since they don't seem to work everywhere. + Reported by olly. + + \version 1.20 Patch from Mark Dootson to close /dev/mem after access to the peripherals has been granted. + + \version 1.21 delayMicroseconds is now not susceptible to 32 bit timer overruns. + Patch courtesy Jeremy Mortis. + + \version 1.22 Fixed incorrect definition of BCM2835_GPFEN0 which broke the ability to set + falling edge events. Reported by Mark Dootson. + + \version 1.23 Added bcm2835_i2c_set_baudrate and bcm2835_i2c_read_register_rs. + Improvements to bcm2835_i2c_read and bcm2835_i2c_write functions + to fix ocasional reads not completing. Patched by Mark Dootson. + + \version 1.24 Mark Dootson p[atched a problem with his previously submitted code + under high load from other processes. + + \version 1.25 Updated author and distribution location details to airspayce.com + + \version 1.26 Added missing unmapmem for pads in bcm2835_close to prevent a memory leak. + Reported by Hartmut Henkel. + + \version 1.27 bcm2835_gpio_set_pad() no longer needs BCM2835_PAD_PASSWRD: it is + now automatically included. + Added suport for PWM mode with bcm2835_pwm_* functions. + + \version 1.28 Fixed a problem where bcm2835_spi_writenb() would have problems with transfers of more than + 64 bytes dues to read buffer filling. Patched by Peter Würtz. + + \version 1.29 Further fix to SPI from Peter Würtz. + + \version 1.30 10 microsecond delays from bcm2835_spi_transfer and bcm2835_spi_transfern for + significant performance improvements, Patch by Alan Watson. + + \version 1.31 Fix a GCC warning about dummy variable, patched by Alan Watson. Thanks. + + \version 1.32 Added option I2C_V1 definition to compile for version 1 RPi. + By default I2C code is generated for the V2 RPi which has SDA1 and SCL1 connected. + Contributed by Malcolm Wiles based on work by Arvi Govindaraj. + + \version 1.33 Added command line utilities i2c and gpio to examples. Contributed by Shahrooz Shahparnia. + + \version 1.34 Added bcm2835_i2c_write_read_rs() which writes an arbitrary number of bytes, + sends a repeat start, and reads from the device. Contributed by Eduardo Steinhorst. + + \version 1.35 Fix build errors when compiled under Qt. Also performance improvements with SPI transfers. Contributed b Udo Klaas. + + \version 1.36 Make automake's test runner detect that we're skipping tests when not root, the second + one makes us skip the test when using fakeroot (as used when building + Debian packages). Contributed by Guido Günther. + + \version 1.37 Moved confiure.in to configure.ac as receommnded by autoreconf.
+ Improvements to bcm2835_st_read to account for possible timer overflow, contributed by 'Ed'.
+ Added definitions for Raspberry Pi B+ J8 header GPIO pins.
+ + \version 1.38 Added bcm2835_regbase for the benefit of C# wrappers, patch by Frank Hommers
+ + \version 1.39 Beta version of RPi2 compatibility. Not tested here on RPi2 hardware. + Testers please confirm correct operation on RPi2.
+ Unneccessary 'volatile' qualifiers removed from all variables and signatures.
+ Removed unsupportable PWM dividers, based on a report from Christophe Cecillon.
+ Minor improvements to spi.c example.
+ + \version 1.40 Correct operation on RPi2 has been confirmed.
+ Fixed a number of compiler errors and warnings that occur when bcm2835.h is included + in code compiled with -Wall -Woverflow -Wstrict-overflow -Wshadow -Wextra -pedantic. + Reported by tlhackque.
+ Fixed a problem where calling bcm2835_delayMicroseconds loops forever when debug is set. Reported by tlhackque.
+ Reinstated use of volatile in 2 functions where there was a danger of lost reads or writes. Reported by tlhackque.
+ + \version 1.41 Added BCM2835_VERSION macro and new function bcm2835_version(); Requested by tlhackque.
+ Improvements to peripheral memory barriers as suggested by tlhackque.
+ Reinstated some necessary volatile declarations as requested by tlhackque.
+ + \version 1.42 Further improvements to memory barriers with the patient assistance and patches of tlhackque.
+ + \version 1.43 Fixed problems with compiling barriers on RPI 2 with Arch Linux and gcc 4.9.2. + Reported and patched by Lars Christensen.
+ Testing on RPI 2, with ArchLinuxARM-rpi-2-latest and 2015-02-16-raspbian-wheezy.
+ + \version 1.44 Added documention about the need for device tree to be enabled on RPI2.
+ Improvements to detection of availablity of DMB instruction based on value of __ARM_ARCH macro.
+ + \version 1.45 Fixed an error in the pad group offsets that would prevent bcm2835_gpio_set_pad() + and bcm2835_gpio_pad() working correctly with non-0 pad groups. Reported by Guido. + + \version 1.46 2015-09-18 + Added symbolic definitions for remaining pins on 40 pin GPIO header on RPi 2.
+ + \version 1.47 2015-11-18 + Fixed possibly incorrect reads in bcm2835_i2c_read_register_rs, patch from Eckhardt Ulrich.
+ + \version 1.48 2015-12-08 + Added patch from Eckhardt Ulrich that fixed problems that could cause hanging with bcm2835_i2c_read_register_rs + and others. + + \version 1.49 2016-01-05 + Added patch from Jonathan Perkin with new functions bcm2835_gpio_eds_multi() and bcm2835_gpio_set_eds_multi(). + + \version 1.50 2016-02-28 + Added support for running as non-root, permitting access to GPIO only. Functions + bcm2835_spi_begin() and bcm2835_i2c_begin() will now return 0 if not running as root + (which prevents access to the SPI and I2C peripherals, amongst others). + Testing on Raspbian Jessie. + + \author Mike McCauley (mikem@airspayce.com) DO NOT CONTACT THE AUTHOR DIRECTLY: USE THE LISTS + + + Modified September 2016 by Marcelo Aquino +*/ + + +/* Defines for BCM2835 */ +#ifndef BCM2835_H +#define BCM2835_H + +#include + +#define BCM2835_VERSION 10050 /* Version 1.50 */ + +/* RPi 2 is ARM v7, and has DMB instruction for memory barriers. + Older RPis are ARM v6 and don't, so a coprocessor instruction must be used instead. + However, not all versions of gcc in all distros support the dmb assembler instruction even on conmpatible processors. + This test is so any ARMv7 or higher processors with suitable GCC will use DMB. +*/ +#if __ARM_ARCH >= 7 +#define BCM2835_HAVE_DMB +#endif + +/*! \defgroup BCM2835constantsgrp Constants for passing to and from library functions + \ingroup BCM2835grp + The values here are designed to be passed to various functions in the bcm2835 library. + @{ +*/ + +/*! This means pin HIGH, true, 3.3volts on a pin. */ +#define HIGH 0x1 +/*! This means pin LOW, false, 0volts on a pin. */ +#define LOW 0x0 + +/*! Speed of the core clock core_clk */ +#define BCM2835_CORE_CLK_HZ 250000000 /*!< 250 MHz */ + +/*! On RPi2 with BCM2836, and all recent OSs, the base of the peripherals is read from a /proc file */ +#define BMC2835_RPI2_DT_FILENAME "/proc/device-tree/soc/ranges" +/*! Offset into BMC2835_RPI2_DT_FILENAME for the peripherals base address */ +#define BMC2835_RPI2_DT_PERI_BASE_ADDRESS_OFFSET 4 +/*! Offset into BMC2835_RPI2_DT_FILENAME for the peripherals size address */ +#define BMC2835_RPI2_DT_PERI_SIZE_OFFSET 8 + +/*! Physical addresses for various peripheral register sets + Base Physical Address of the BCM 2835 peripheral registers + Note this is different for the RPi2 BCM2836, where this is derived from /proc/device-tree/soc/ranges + If /proc/device-tree/soc/ranges exists on a RPi 1 OS, it would be expected to contain the + following numbers: +*/ +/*! Peripherals block base address on RPi 1 */ +#define BCM2835_PERI_BASE 0x20000000 +/*! Size of the peripherals block on RPi 1 */ +#define BCM2835_PERI_SIZE 0x01000000 + +/*! Offsets for the bases of various peripherals within the peripherals block + / Base Address of the System Timer registers +*/ +#define BCM2835_ST_BASE 0x3000 +/*! Base Address of the Pads registers */ +#define BCM2835_GPIO_PADS 0x100000 +/*! Base Address of the Clock/timer registers */ +#define BCM2835_CLOCK_BASE 0x101000 +/*! Base Address of the GPIO registers */ +#define BCM2835_GPIO_BASE 0x200000 +/*! Base Address of the SPI0 registers */ +#define BCM2835_SPI0_BASE 0x204000 +/*! Base Address of the BSC0 registers */ +#define BCM2835_BSC0_BASE 0x205000 +/*! Base Address of the PWM registers */ +#define BCM2835_GPIO_PWM 0x20C000 +/*! Base Address of the BSC1 registers */ +#define BCM2835_BSC1_BASE 0x804000 + +/*! Physical address and size of the peripherals block + May be overridden on RPi2 +*/ +extern uint32_t *bcm2835_peripherals_base; +/*! Size of the peripherals block to be mapped */ +extern uint32_t bcm2835_peripherals_size; + +/*! Virtual memory address of the mapped peripherals block */ +extern uint32_t *bcm2835_peripherals; + +/*! Base of the ST (System Timer) registers. + Available after bcm2835_init has been called (as root) +*/ +extern volatile uint32_t *bcm2835_st; + +/*! Base of the GPIO registers. + Available after bcm2835_init has been called +*/ +extern volatile uint32_t *bcm2835_gpio; + +/*! Base of the PWM registers. + Available after bcm2835_init has been called (as root) +*/ +extern volatile uint32_t *bcm2835_pwm; + +/*! Base of the CLK registers. + Available after bcm2835_init has been called (as root) +*/ +extern volatile uint32_t *bcm2835_clk; + +/*! Base of the PADS registers. + Available after bcm2835_init has been called (as root) +*/ +extern volatile uint32_t *bcm2835_pads; + +/*! Base of the SPI0 registers. + Available after bcm2835_init has been called (as root) +*/ +extern volatile uint32_t *bcm2835_spi0; + +/*! Base of the BSC0 registers. + Available after bcm2835_init has been called (as root) +*/ +extern volatile uint32_t *bcm2835_bsc0; + +/*! Base of the BSC1 registers. + Available after bcm2835_init has been called (as root) +*/ +extern volatile uint32_t *bcm2835_bsc1; + +/*! \brief bcm2835RegisterBase + Register bases for bcm2835_regbase() +*/ +typedef enum +{ + BCM2835_REGBASE_ST = 1, /*!< Base of the ST (System Timer) registers. */ + BCM2835_REGBASE_GPIO = 2, /*!< Base of the GPIO registers. */ + BCM2835_REGBASE_PWM = 3, /*!< Base of the PWM registers. */ + BCM2835_REGBASE_CLK = 4, /*!< Base of the CLK registers. */ + BCM2835_REGBASE_PADS = 5, /*!< Base of the PADS registers. */ + BCM2835_REGBASE_SPI0 = 6, /*!< Base of the SPI0 registers. */ + BCM2835_REGBASE_BSC0 = 7, /*!< Base of the BSC0 registers. */ + BCM2835_REGBASE_BSC1 = 8 /*!< Base of the BSC1 registers. */ +} bcm2835RegisterBase; + +/*! Size of memory page on RPi */ +#define BCM2835_PAGE_SIZE (4*1024) +/*! Size of memory block on RPi */ +#define BCM2835_BLOCK_SIZE (4*1024) + + +/* Defines for GPIO + The BCM2835 has 54 GPIO pins. + BCM2835 data sheet, Page 90 onwards. +*/ +/*! GPIO register offsets from BCM2835_GPIO_BASE. + Offsets into the GPIO Peripheral block in bytes per 6.1 Register View +*/ +#define BCM2835_GPFSEL0 0x0000 /*!< GPIO Function Select 0 */ +#define BCM2835_GPFSEL1 0x0004 /*!< GPIO Function Select 1 */ +#define BCM2835_GPFSEL2 0x0008 /*!< GPIO Function Select 2 */ +#define BCM2835_GPFSEL3 0x000c /*!< GPIO Function Select 3 */ +#define BCM2835_GPFSEL4 0x0010 /*!< GPIO Function Select 4 */ +#define BCM2835_GPFSEL5 0x0014 /*!< GPIO Function Select 5 */ +#define BCM2835_GPSET0 0x001c /*!< GPIO Pin Output Set 0 */ +#define BCM2835_GPSET1 0x0020 /*!< GPIO Pin Output Set 1 */ +#define BCM2835_GPCLR0 0x0028 /*!< GPIO Pin Output Clear 0 */ +#define BCM2835_GPCLR1 0x002c /*!< GPIO Pin Output Clear 1 */ +#define BCM2835_GPLEV0 0x0034 /*!< GPIO Pin Level 0 */ +#define BCM2835_GPLEV1 0x0038 /*!< GPIO Pin Level 1 */ +#define BCM2835_GPEDS0 0x0040 /*!< GPIO Pin Event Detect Status 0 */ +#define BCM2835_GPEDS1 0x0044 /*!< GPIO Pin Event Detect Status 1 */ +#define BCM2835_GPREN0 0x004c /*!< GPIO Pin Rising Edge Detect Enable 0 */ +#define BCM2835_GPREN1 0x0050 /*!< GPIO Pin Rising Edge Detect Enable 1 */ +#define BCM2835_GPFEN0 0x0058 /*!< GPIO Pin Falling Edge Detect Enable 0 */ +#define BCM2835_GPFEN1 0x005c /*!< GPIO Pin Falling Edge Detect Enable 1 */ +#define BCM2835_GPHEN0 0x0064 /*!< GPIO Pin High Detect Enable 0 */ +#define BCM2835_GPHEN1 0x0068 /*!< GPIO Pin High Detect Enable 1 */ +#define BCM2835_GPLEN0 0x0070 /*!< GPIO Pin Low Detect Enable 0 */ +#define BCM2835_GPLEN1 0x0074 /*!< GPIO Pin Low Detect Enable 1 */ +#define BCM2835_GPAREN0 0x007c /*!< GPIO Pin Async. Rising Edge Detect 0 */ +#define BCM2835_GPAREN1 0x0080 /*!< GPIO Pin Async. Rising Edge Detect 1 */ +#define BCM2835_GPAFEN0 0x0088 /*!< GPIO Pin Async. Falling Edge Detect 0 */ +#define BCM2835_GPAFEN1 0x008c /*!< GPIO Pin Async. Falling Edge Detect 1 */ +#define BCM2835_GPPUD 0x0094 /*!< GPIO Pin Pull-up/down Enable */ +#define BCM2835_GPPUDCLK0 0x0098 /*!< GPIO Pin Pull-up/down Enable Clock 0 */ +#define BCM2835_GPPUDCLK1 0x009c /*!< GPIO Pin Pull-up/down Enable Clock 1 */ + +/*! \brief bcm2835PortFunction + Port function select modes for bcm2835_gpio_fsel() +*/ +typedef enum +{ + BCM2835_GPIO_FSEL_INPT = 0x00, /*!< Input 0b000 */ + BCM2835_GPIO_FSEL_OUTP = 0x01, /*!< Output 0b001 */ + BCM2835_GPIO_FSEL_ALT0 = 0x04, /*!< Alternate function 0 0b100 */ + BCM2835_GPIO_FSEL_ALT1 = 0x05, /*!< Alternate function 1 0b101 */ + BCM2835_GPIO_FSEL_ALT2 = 0x06, /*!< Alternate function 2 0b110, */ + BCM2835_GPIO_FSEL_ALT3 = 0x07, /*!< Alternate function 3 0b111 */ + BCM2835_GPIO_FSEL_ALT4 = 0x03, /*!< Alternate function 4 0b011 */ + BCM2835_GPIO_FSEL_ALT5 = 0x02, /*!< Alternate function 5 0b010 */ + BCM2835_GPIO_FSEL_MASK = 0x07 /*!< Function select bits mask 0b111 */ +} bcm2835FunctionSelect; + +/*! \brief bcm2835PUDControl + Pullup/Pulldown defines for bcm2835_gpio_pud() +*/ +typedef enum +{ + BCM2835_GPIO_PUD_OFF = 0x00, /*!< Off ? disable pull-up/down 0b00 */ + BCM2835_GPIO_PUD_DOWN = 0x01, /*!< Enable Pull Down control 0b01 */ + BCM2835_GPIO_PUD_UP = 0x02 /*!< Enable Pull Up control 0b10 */ +} bcm2835PUDControl; + +/*! Pad control register offsets from BCM2835_GPIO_PADS */ +#define BCM2835_PADS_GPIO_0_27 0x002c /*!< Pad control register for pads 0 to 27 */ +#define BCM2835_PADS_GPIO_28_45 0x0030 /*!< Pad control register for pads 28 to 45 */ +#define BCM2835_PADS_GPIO_46_53 0x0034 /*!< Pad control register for pads 46 to 53 */ + +/*! Pad Control masks */ +#define BCM2835_PAD_PASSWRD (0x5A << 24) /*!< Password to enable setting pad mask */ +#define BCM2835_PAD_SLEW_RATE_UNLIMITED 0x10 /*!< Slew rate unlimited */ +#define BCM2835_PAD_HYSTERESIS_ENABLED 0x08 /*!< Hysteresis enabled */ +#define BCM2835_PAD_DRIVE_2mA 0x00 /*!< 2mA drive current */ +#define BCM2835_PAD_DRIVE_4mA 0x01 /*!< 4mA drive current */ +#define BCM2835_PAD_DRIVE_6mA 0x02 /*!< 6mA drive current */ +#define BCM2835_PAD_DRIVE_8mA 0x03 /*!< 8mA drive current */ +#define BCM2835_PAD_DRIVE_10mA 0x04 /*!< 10mA drive current */ +#define BCM2835_PAD_DRIVE_12mA 0x05 /*!< 12mA drive current */ +#define BCM2835_PAD_DRIVE_14mA 0x06 /*!< 14mA drive current */ +#define BCM2835_PAD_DRIVE_16mA 0x07 /*!< 16mA drive current */ + +/*! \brief bcm2835PadGroup + Pad group specification for bcm2835_gpio_pad() +*/ +typedef enum +{ + BCM2835_PAD_GROUP_GPIO_0_27 = 0, /*!< Pad group for GPIO pads 0 to 27 */ + BCM2835_PAD_GROUP_GPIO_28_45 = 1, /*!< Pad group for GPIO pads 28 to 45 */ + BCM2835_PAD_GROUP_GPIO_46_53 = 2 /*!< Pad group for GPIO pads 46 to 53 */ +} bcm2835PadGroup; + +/*! \brief GPIO Pin Numbers + + Here we define Raspberry Pin GPIO pins on P1 in terms of the underlying BCM GPIO pin numbers. + These can be passed as a pin number to any function requiring a pin. + Not all pins on the RPi 26 bin IDE plug are connected to GPIO pins + and some can adopt an alternate function. + RPi version 2 has some slightly different pinouts, and these are values RPI_V2_*. + RPi B+ has yet differnet pinouts and these are defined in RPI_BPLUS_*. + At bootup, pins 8 and 10 are set to UART0_TXD, UART0_RXD (ie the alt0 function) respectively + When SPI0 is in use (ie after bcm2835_spi_begin()), SPI0 pins are dedicated to SPI + and cant be controlled independently. + If you are using the RPi Compute Module, just use the GPIO number: there is no need to use one of these + symbolic names +*/ +typedef enum +{ + RPI_GPIO_P1_03 = 0, /*!< Version 1, Pin P1-03 */ + RPI_GPIO_P1_05 = 1, /*!< Version 1, Pin P1-05 */ + RPI_GPIO_P1_07 = 4, /*!< Version 1, Pin P1-07 */ + RPI_GPIO_P1_08 = 14, /*!< Version 1, Pin P1-08, defaults to alt function 0 UART0_TXD */ + RPI_GPIO_P1_10 = 15, /*!< Version 1, Pin P1-10, defaults to alt function 0 UART0_RXD */ + RPI_GPIO_P1_11 = 17, /*!< Version 1, Pin P1-11 */ + RPI_GPIO_P1_12 = 18, /*!< Version 1, Pin P1-12, can be PWM channel 0 in ALT FUN 5 */ + RPI_GPIO_P1_13 = 21, /*!< Version 1, Pin P1-13 */ + RPI_GPIO_P1_15 = 22, /*!< Version 1, Pin P1-15 */ + RPI_GPIO_P1_16 = 23, /*!< Version 1, Pin P1-16 */ + RPI_GPIO_P1_18 = 24, /*!< Version 1, Pin P1-18 */ + RPI_GPIO_P1_19 = 10, /*!< Version 1, Pin P1-19, MOSI when SPI0 in use */ + RPI_GPIO_P1_21 = 9, /*!< Version 1, Pin P1-21, MISO when SPI0 in use */ + RPI_GPIO_P1_22 = 25, /*!< Version 1, Pin P1-22 */ + RPI_GPIO_P1_23 = 11, /*!< Version 1, Pin P1-23, CLK when SPI0 in use */ + RPI_GPIO_P1_24 = 8, /*!< Version 1, Pin P1-24, CE0 when SPI0 in use */ + RPI_GPIO_P1_26 = 7, /*!< Version 1, Pin P1-26, CE1 when SPI0 in use */ + + /* RPi Version 2 */ + RPI_V2_GPIO_P1_03 = 2, /*!< Version 2, Pin P1-03 */ + RPI_V2_GPIO_P1_05 = 3, /*!< Version 2, Pin P1-05 */ + RPI_V2_GPIO_P1_07 = 4, /*!< Version 2, Pin P1-07 */ + RPI_V2_GPIO_P1_08 = 14, /*!< Version 2, Pin P1-08, defaults to alt function 0 UART0_TXD */ + RPI_V2_GPIO_P1_10 = 15, /*!< Version 2, Pin P1-10, defaults to alt function 0 UART0_RXD */ + RPI_V2_GPIO_P1_11 = 17, /*!< Version 2, Pin P1-11 */ + RPI_V2_GPIO_P1_12 = 18, /*!< Version 2, Pin P1-12, can be PWM channel 0 in ALT FUN 5 */ + RPI_V2_GPIO_P1_13 = 27, /*!< Version 2, Pin P1-13 */ + RPI_V2_GPIO_P1_15 = 22, /*!< Version 2, Pin P1-15 */ + RPI_V2_GPIO_P1_16 = 23, /*!< Version 2, Pin P1-16 */ + RPI_V2_GPIO_P1_18 = 24, /*!< Version 2, Pin P1-18 */ + RPI_V2_GPIO_P1_19 = 10, /*!< Version 2, Pin P1-19, MOSI when SPI0 in use */ + RPI_V2_GPIO_P1_21 = 9, /*!< Version 2, Pin P1-21, MISO when SPI0 in use */ + RPI_V2_GPIO_P1_22 = 25, /*!< Version 2, Pin P1-22 */ + RPI_V2_GPIO_P1_23 = 11, /*!< Version 2, Pin P1-23, CLK when SPI0 in use */ + RPI_V2_GPIO_P1_24 = 8, /*!< Version 2, Pin P1-24, CE0 when SPI0 in use */ + RPI_V2_GPIO_P1_26 = 7, /*!< Version 2, Pin P1-26, CE1 when SPI0 in use */ + RPI_V2_GPIO_P1_29 = 5, /*!< Version 2, Pin P1-29 */ + RPI_V2_GPIO_P1_31 = 6, /*!< Version 2, Pin P1-31 */ + RPI_V2_GPIO_P1_32 = 12, /*!< Version 2, Pin P1-32 */ + RPI_V2_GPIO_P1_33 = 13, /*!< Version 2, Pin P1-33 */ + RPI_V2_GPIO_P1_35 = 19, /*!< Version 2, Pin P1-35 */ + RPI_V2_GPIO_P1_36 = 16, /*!< Version 2, Pin P1-36 */ + RPI_V2_GPIO_P1_37 = 26, /*!< Version 2, Pin P1-37 */ + RPI_V2_GPIO_P1_38 = 20, /*!< Version 2, Pin P1-38 */ + RPI_V2_GPIO_P1_40 = 21, /*!< Version 2, Pin P1-40 */ + + /* RPi Version 2, new plug P5 */ + RPI_V2_GPIO_P5_03 = 28, /*!< Version 2, Pin P5-03 */ + RPI_V2_GPIO_P5_04 = 29, /*!< Version 2, Pin P5-04 */ + RPI_V2_GPIO_P5_05 = 30, /*!< Version 2, Pin P5-05 */ + RPI_V2_GPIO_P5_06 = 31, /*!< Version 2, Pin P5-06 */ + + /* RPi B+ J8 header, also RPi 2 40 pin GPIO header */ + RPI_BPLUS_GPIO_J8_03 = 2, /*!< B+, Pin J8-03 */ + RPI_BPLUS_GPIO_J8_05 = 3, /*!< B+, Pin J8-05 */ + RPI_BPLUS_GPIO_J8_07 = 4, /*!< B+, Pin J8-07 */ + RPI_BPLUS_GPIO_J8_08 = 14, /*!< B+, Pin J8-08, defaults to alt function 0 UART0_TXD */ + RPI_BPLUS_GPIO_J8_10 = 15, /*!< B+, Pin J8-10, defaults to alt function 0 UART0_RXD */ + RPI_BPLUS_GPIO_J8_11 = 17, /*!< B+, Pin J8-11 */ + RPI_BPLUS_GPIO_J8_12 = 18, /*!< B+, Pin J8-12, can be PWM channel 0 in ALT FUN 5 */ + RPI_BPLUS_GPIO_J8_13 = 27, /*!< B+, Pin J8-13 */ + RPI_BPLUS_GPIO_J8_15 = 22, /*!< B+, Pin J8-15 */ + RPI_BPLUS_GPIO_J8_16 = 23, /*!< B+, Pin J8-16 */ + RPI_BPLUS_GPIO_J8_18 = 24, /*!< B+, Pin J8-18 */ + RPI_BPLUS_GPIO_J8_19 = 10, /*!< B+, Pin J8-19, MOSI when SPI0 in use */ + RPI_BPLUS_GPIO_J8_21 = 9, /*!< B+, Pin J8-21, MISO when SPI0 in use */ + RPI_BPLUS_GPIO_J8_22 = 25, /*!< B+, Pin J8-22 */ + RPI_BPLUS_GPIO_J8_23 = 11, /*!< B+, Pin J8-23, CLK when SPI0 in use */ + RPI_BPLUS_GPIO_J8_24 = 8, /*!< B+, Pin J8-24, CE0 when SPI0 in use */ + RPI_BPLUS_GPIO_J8_26 = 7, /*!< B+, Pin J8-26, CE1 when SPI0 in use */ + RPI_BPLUS_GPIO_J8_29 = 5, /*!< B+, Pin J8-29, */ + RPI_BPLUS_GPIO_J8_31 = 6, /*!< B+, Pin J8-31, */ + RPI_BPLUS_GPIO_J8_32 = 12, /*!< B+, Pin J8-32, */ + RPI_BPLUS_GPIO_J8_33 = 13, /*!< B+, Pin J8-33, */ + RPI_BPLUS_GPIO_J8_35 = 19, /*!< B+, Pin J8-35, */ + RPI_BPLUS_GPIO_J8_36 = 16, /*!< B+, Pin J8-36, */ + RPI_BPLUS_GPIO_J8_37 = 26, /*!< B+, Pin J8-37, */ + RPI_BPLUS_GPIO_J8_38 = 20, /*!< B+, Pin J8-38, */ + RPI_BPLUS_GPIO_J8_40 = 21 /*!< B+, Pin J8-40, */ +} RPiGPIOPin; + +/* Defines for SPI + GPIO register offsets from BCM2835_SPI0_BASE. + Offsets into the SPI Peripheral block in bytes per 10.5 SPI Register Map +*/ +#define BCM2835_SPI0_CS 0x0000 /*!< SPI Master Control and Status */ +#define BCM2835_SPI0_FIFO 0x0004 /*!< SPI Master TX and RX FIFOs */ +#define BCM2835_SPI0_CLK 0x0008 /*!< SPI Master Clock Divider */ +#define BCM2835_SPI0_DLEN 0x000c /*!< SPI Master Data Length */ +#define BCM2835_SPI0_LTOH 0x0010 /*!< SPI LOSSI mode TOH */ +#define BCM2835_SPI0_DC 0x0014 /*!< SPI DMA DREQ Controls */ + +/* Register masks for SPI0_CS */ +#define BCM2835_SPI0_CS_LEN_LONG 0x02000000 /*!< Enable Long data word in Lossi mode if DMA_LEN is set */ +#define BCM2835_SPI0_CS_DMA_LEN 0x01000000 /*!< Enable DMA mode in Lossi mode */ +#define BCM2835_SPI0_CS_CSPOL2 0x00800000 /*!< Chip Select 2 Polarity */ +#define BCM2835_SPI0_CS_CSPOL1 0x00400000 /*!< Chip Select 1 Polarity */ +#define BCM2835_SPI0_CS_CSPOL0 0x00200000 /*!< Chip Select 0 Polarity */ +#define BCM2835_SPI0_CS_RXF 0x00100000 /*!< RXF - RX FIFO Full */ +#define BCM2835_SPI0_CS_RXR 0x00080000 /*!< RXR RX FIFO needs Reading (full) */ +#define BCM2835_SPI0_CS_TXD 0x00040000 /*!< TXD TX FIFO can accept Data */ +#define BCM2835_SPI0_CS_RXD 0x00020000 /*!< RXD RX FIFO contains Data */ +#define BCM2835_SPI0_CS_DONE 0x00010000 /*!< Done transfer Done */ +#define BCM2835_SPI0_CS_TE_EN 0x00008000 /*!< Unused */ +#define BCM2835_SPI0_CS_LMONO 0x00004000 /*!< Unused */ +#define BCM2835_SPI0_CS_LEN 0x00002000 /*!< LEN LoSSI enable */ +#define BCM2835_SPI0_CS_REN 0x00001000 /*!< REN Read Enable */ +#define BCM2835_SPI0_CS_ADCS 0x00000800 /*!< ADCS Automatically Deassert Chip Select */ +#define BCM2835_SPI0_CS_INTR 0x00000400 /*!< INTR Interrupt on RXR */ +#define BCM2835_SPI0_CS_INTD 0x00000200 /*!< INTD Interrupt on Done */ +#define BCM2835_SPI0_CS_DMAEN 0x00000100 /*!< DMAEN DMA Enable */ +#define BCM2835_SPI0_CS_TA 0x00000080 /*!< Transfer Active */ +#define BCM2835_SPI0_CS_CSPOL 0x00000040 /*!< Chip Select Polarity */ +#define BCM2835_SPI0_CS_CLEAR 0x00000030 /*!< Clear FIFO Clear RX and TX */ +#define BCM2835_SPI0_CS_CLEAR_RX 0x00000020 /*!< Clear FIFO Clear RX */ +#define BCM2835_SPI0_CS_CLEAR_TX 0x00000010 /*!< Clear FIFO Clear TX */ +#define BCM2835_SPI0_CS_CPOL 0x00000008 /*!< Clock Polarity */ +#define BCM2835_SPI0_CS_CPHA 0x00000004 /*!< Clock Phase */ +#define BCM2835_SPI0_CS_CS 0x00000003 /*!< Chip Select */ + +/*! \brief bcm2835SPIBitOrder SPI Bit order + Specifies the SPI data bit ordering for bcm2835_spi_setBitOrder() +*/ +typedef enum +{ + BCM2835_SPI_BIT_ORDER_LSBFIRST = 0, /*!< LSB First */ + BCM2835_SPI_BIT_ORDER_MSBFIRST = 1 /*!< MSB First */ +}bcm2835SPIBitOrder; + +/*! \brief SPI Data mode + Specify the SPI data mode to be passed to bcm2835_spi_setDataMode() +*/ +typedef enum +{ + BCM2835_SPI_MODE0 = 0, /*!< CPOL = 0, CPHA = 0 */ + BCM2835_SPI_MODE1 = 1, /*!< CPOL = 0, CPHA = 1 */ + BCM2835_SPI_MODE2 = 2, /*!< CPOL = 1, CPHA = 0 */ + BCM2835_SPI_MODE3 = 3 /*!< CPOL = 1, CPHA = 1 */ +}bcm2835SPIMode; + +/*! \brief bcm2835SPIChipSelect + Specify the SPI chip select pin(s) +*/ +typedef enum +{ + BCM2835_SPI_CS0 = 0, /*!< Chip Select 0 */ + BCM2835_SPI_CS1 = 1, /*!< Chip Select 1 */ + BCM2835_SPI_CS2 = 2, /*!< Chip Select 2 (ie pins CS1 and CS2 are asserted) */ + BCM2835_SPI_CS_NONE = 3 /*!< No CS, control it yourself */ +} bcm2835SPIChipSelect; + +/*! \brief bcm2835SPIClockDivider + Specifies the divider used to generate the SPI clock from the system clock. + Figures below give the divider, clock period and clock frequency. + Clock divided is based on nominal base clock rate of 250MHz + It is reported that (contrary to the documentation) any even divider may used. + The frequencies shown for each divider have been confirmed by measurement. +*/ +typedef enum +{ + BCM2835_SPI_CLOCK_DIVIDER_65536 = 0, /*!< 65536 = 262.144us = 3.814697260kHz */ + BCM2835_SPI_CLOCK_DIVIDER_32768 = 32768, /*!< 32768 = 131.072us = 7.629394531kHz */ + BCM2835_SPI_CLOCK_DIVIDER_16384 = 16384, /*!< 16384 = 65.536us = 15.25878906kHz */ + BCM2835_SPI_CLOCK_DIVIDER_8192 = 8192, /*!< 8192 = 32.768us = 30/51757813kHz */ + BCM2835_SPI_CLOCK_DIVIDER_4096 = 4096, /*!< 4096 = 16.384us = 61.03515625kHz */ + BCM2835_SPI_CLOCK_DIVIDER_2048 = 2048, /*!< 2048 = 8.192us = 122.0703125kHz */ + BCM2835_SPI_CLOCK_DIVIDER_1024 = 1024, /*!< 1024 = 4.096us = 244.140625kHz */ + BCM2835_SPI_CLOCK_DIVIDER_512 = 512, /*!< 512 = 2.048us = 488.28125kHz */ + BCM2835_SPI_CLOCK_DIVIDER_256 = 256, /*!< 256 = 1.024us = 976.5625kHz */ + BCM2835_SPI_CLOCK_DIVIDER_128 = 128, /*!< 128 = 512ns = = 1.953125MHz */ + BCM2835_SPI_CLOCK_DIVIDER_64 = 64, /*!< 64 = 256ns = 3.90625MHz */ + BCM2835_SPI_CLOCK_DIVIDER_32 = 32, /*!< 32 = 128ns = 7.8125MHz */ + BCM2835_SPI_CLOCK_DIVIDER_16 = 16, /*!< 16 = 64ns = 15.625MHz */ + BCM2835_SPI_CLOCK_DIVIDER_8 = 8, /*!< 8 = 32ns = 31.25MHz */ + BCM2835_SPI_CLOCK_DIVIDER_4 = 4, /*!< 4 = 16ns = 62.5MHz */ + BCM2835_SPI_CLOCK_DIVIDER_2 = 2, /*!< 2 = 8ns = 125MHz, fastest you can get */ + BCM2835_SPI_CLOCK_DIVIDER_1 = 1 /*!< 1 = 262.144us = 3.814697260kHz, same as 0/65536 */ +} bcm2835SPIClockDivider; + +/* Defines for I2C + GPIO register offsets from BCM2835_BSC*_BASE. + Offsets into the BSC Peripheral block in bytes per 3.1 BSC Register Map +*/ +#define BCM2835_BSC_C 0x0000 /*!< BSC Master Control */ +#define BCM2835_BSC_S 0x0004 /*!< BSC Master Status */ +#define BCM2835_BSC_DLEN 0x0008 /*!< BSC Master Data Length */ +#define BCM2835_BSC_A 0x000c /*!< BSC Master Slave Address */ +#define BCM2835_BSC_FIFO 0x0010 /*!< BSC Master Data FIFO */ +#define BCM2835_BSC_DIV 0x0014 /*!< BSC Master Clock Divider */ +#define BCM2835_BSC_DEL 0x0018 /*!< BSC Master Data Delay */ +#define BCM2835_BSC_CLKT 0x001c /*!< BSC Master Clock Stretch Timeout */ + +/* Register masks for BSC_C */ +#define BCM2835_BSC_C_I2CEN 0x00008000 /*!< I2C Enable, 0 = disabled, 1 = enabled */ +#define BCM2835_BSC_C_INTR 0x00000400 /*!< Interrupt on RX */ +#define BCM2835_BSC_C_INTT 0x00000200 /*!< Interrupt on TX */ +#define BCM2835_BSC_C_INTD 0x00000100 /*!< Interrupt on DONE */ +#define BCM2835_BSC_C_ST 0x00000080 /*!< Start transfer, 1 = Start a new transfer */ +#define BCM2835_BSC_C_CLEAR_1 0x00000020 /*!< Clear FIFO Clear */ +#define BCM2835_BSC_C_CLEAR_2 0x00000010 /*!< Clear FIFO Clear */ +#define BCM2835_BSC_C_READ 0x00000001 /*!< Read transfer */ + +/* Register masks for BSC_S */ +#define BCM2835_BSC_S_CLKT 0x00000200 /*!< Clock stretch timeout */ +#define BCM2835_BSC_S_ERR 0x00000100 /*!< ACK error */ +#define BCM2835_BSC_S_RXF 0x00000080 /*!< RXF FIFO full, 0 = FIFO is not full, 1 = FIFO is full */ +#define BCM2835_BSC_S_TXE 0x00000040 /*!< TXE FIFO full, 0 = FIFO is not full, 1 = FIFO is full */ +#define BCM2835_BSC_S_RXD 0x00000020 /*!< RXD FIFO contains data */ +#define BCM2835_BSC_S_TXD 0x00000010 /*!< TXD FIFO can accept data */ +#define BCM2835_BSC_S_RXR 0x00000008 /*!< RXR FIFO needs reading (full) */ +#define BCM2835_BSC_S_TXW 0x00000004 /*!< TXW FIFO needs writing (full) */ +#define BCM2835_BSC_S_DONE 0x00000002 /*!< Transfer DONE */ +#define BCM2835_BSC_S_TA 0x00000001 /*!< Transfer Active */ + +#define BCM2835_BSC_FIFO_SIZE 16 /*!< BSC FIFO size */ + +/*! \brief bcm2835I2CClockDivider + Specifies the divider used to generate the I2C clock from the system clock. + Clock divided is based on nominal base clock rate of 250MHz +*/ +typedef enum +{ + BCM2835_I2C_CLOCK_DIVIDER_2500 = 2500, /*!< 2500 = 10us = 100 kHz */ + BCM2835_I2C_CLOCK_DIVIDER_626 = 626, /*!< 622 = 2.504us = 399.3610 kHz */ + BCM2835_I2C_CLOCK_DIVIDER_150 = 150, /*!< 150 = 60ns = 1.666 MHz (default at reset) */ + BCM2835_I2C_CLOCK_DIVIDER_148 = 148 /*!< 148 = 59ns = 1.689 MHz */ +} bcm2835I2CClockDivider; + +/*! \brief bcm2835I2CReasonCodes + Specifies the reason codes for the bcm2835_i2c_write and bcm2835_i2c_read functions. +*/ +typedef enum +{ + BCM2835_I2C_REASON_OK = 0x00, /*!< Success */ + BCM2835_I2C_REASON_ERROR_NACK = 0x01, /*!< Received a NACK */ + BCM2835_I2C_REASON_ERROR_CLKT = 0x02, /*!< Received Clock Stretch Timeout */ + BCM2835_I2C_REASON_ERROR_DATA = 0x04 /*!< Not all data is sent / received */ +} bcm2835I2CReasonCodes; + +/* Defines for ST + GPIO register offsets from BCM2835_ST_BASE. + Offsets into the ST Peripheral block in bytes per 12.1 System Timer Registers + The System Timer peripheral provides four 32-bit timer channels and a single 64-bit free running counter. + BCM2835_ST_CLO is the System Timer Counter Lower bits register. + The system timer free-running counter lower register is a read-only register that returns the current value + of the lower 32-bits of the free running counter. + BCM2835_ST_CHI is the System Timer Counter Upper bits register. + The system timer free-running counter upper register is a read-only register that returns the current value + of the upper 32-bits of the free running counter. +*/ +#define BCM2835_ST_CS 0x0000 /*!< System Timer Control/Status */ +#define BCM2835_ST_CLO 0x0004 /*!< System Timer Counter Lower 32 bits */ +#define BCM2835_ST_CHI 0x0008 /*!< System Timer Counter Upper 32 bits */ + +/* Defines for PWM, word offsets (ie 4 byte multiples) */ +#define BCM2835_PWM_CONTROL 0 /*!< PWM Control register */ +#define BCM2835_PWM_STATUS 1 /*!< PWM Status register */ +#define BCM2835_PWM_DMAC 2 /*!< PWM DMA Configuration */ +#define BCM2835_PWM0_RANGE 4 /*!< PWM Channel 0 Range */ +#define BCM2835_PWM0_DATA 5 /*!< PWM Channel 0 Data */ +#define BCM2835_PWM_FIF1 6 /*!< PWM FIFO Input */ +#define BCM2835_PWM1_RANGE 8 /*!< PWM Channel 1 Range */ +#define BCM2835_PWM1_DATA 9 /*!< PWM Channel 1 Data */ + +/* Defines for PWM Clock, word offsets (ie 4 byte multiples) */ +#define BCM2835_PWMCLK_CNTL 40 /*!< PWM Clock Control */ +#define BCM2835_PWMCLK_DIV 41 /*!< PWM Clock Divider */ +#define BCM2835_PWM_PASSWRD (0x5A << 24) /*!< Password to enable setting PWM clock */ + +#define BCM2835_PWM1_MS_MODE 0x8000 /*!< Run in Mark/Space mode */ +#define BCM2835_PWM1_USEFIFO 0x2000 /*!< Data from FIFO */ +#define BCM2835_PWM1_REVPOLAR 0x1000 /*!< Reverse polarity */ +#define BCM2835_PWM1_OFFSTATE 0x0800 /*!< Ouput Off state */ +#define BCM2835_PWM1_REPEATFF 0x0400 /*!< Repeat last value if FIFO empty */ +#define BCM2835_PWM1_SERIAL 0x0200 /*!< Run in serial mode */ +#define BCM2835_PWM1_ENABLE 0x0100 /*!< Channel Enable */ + +#define BCM2835_PWM0_MS_MODE 0x0080 /*!< Run in Mark/Space mode */ +#define BCM2835_PWM_CLEAR_FIFO 0x0040 /*!< Clear FIFO */ +#define BCM2835_PWM0_USEFIFO 0x0020 /*!< Data from FIFO */ +#define BCM2835_PWM0_REVPOLAR 0x0010 /*!< Reverse polarity */ +#define BCM2835_PWM0_OFFSTATE 0x0008 /*!< Ouput Off state */ +#define BCM2835_PWM0_REPEATFF 0x0004 /*!< Repeat last value if FIFO empty */ +#define BCM2835_PWM0_SERIAL 0x0002 /*!< Run in serial mode */ +#define BCM2835_PWM0_ENABLE 0x0001 /*!< Channel Enable */ + +/*! \brief bcm2835PWMClockDivider + Specifies the divider used to generate the PWM clock from the system clock. + Figures below give the divider, clock period and clock frequency. + Clock divided is based on nominal PWM base clock rate of 19.2MHz + The frequencies shown for each divider have been confirmed by measurement +*/ +typedef enum +{ + BCM2835_PWM_CLOCK_DIVIDER_2048 = 2048, /*!< 2048 = 9.375kHz */ + BCM2835_PWM_CLOCK_DIVIDER_1024 = 1024, /*!< 1024 = 18.75kHz */ + BCM2835_PWM_CLOCK_DIVIDER_512 = 512, /*!< 512 = 37.5kHz */ + BCM2835_PWM_CLOCK_DIVIDER_256 = 256, /*!< 256 = 75kHz */ + BCM2835_PWM_CLOCK_DIVIDER_128 = 128, /*!< 128 = 150kHz */ + BCM2835_PWM_CLOCK_DIVIDER_64 = 64, /*!< 64 = 300kHz */ + BCM2835_PWM_CLOCK_DIVIDER_32 = 32, /*!< 32 = 600.0kHz */ + BCM2835_PWM_CLOCK_DIVIDER_16 = 16, /*!< 16 = 1.2MHz */ + BCM2835_PWM_CLOCK_DIVIDER_8 = 8, /*!< 8 = 2.4MHz */ + BCM2835_PWM_CLOCK_DIVIDER_4 = 4, /*!< 4 = 4.8MHz */ + BCM2835_PWM_CLOCK_DIVIDER_2 = 2, /*!< 2 = 9.6MHz, fastest you can get */ + BCM2835_PWM_CLOCK_DIVIDER_1 = 1 /*!< 1 = 4.6875kHz, same as divider 4096 */ +} bcm2835PWMClockDivider; + +/*! @} */ + +/* Historical name compatibility */ +#ifndef BCM2835_NO_DELAY_COMPATIBILITY +#define delay(x) bcm2835_delay(x) +#define delayMicroseconds(x) bcm2835_delayMicroseconds(x) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + + /*! \defgroup BCM2835initgrp Library initialisation and management + \ingroup BCM2835grp + These functions allow you to intialise and control the bcm2835 library + @{ + */ + + /*! Initialise the library by opening /dev/mem (if you are root) + or /dev/gpiomem (if you are not) + and getting pointers to the + internal memory for BCM 2835 device registers. You must call this (successfully) + before calling any other + functions in this library (except bcm2835_set_debug). + If bcm2835_init() fails by returning 0, + calling any other function may result in crashes or other failures. + If bcm2835_init() succeeds but you are not running as root, then only gpio operations + are permitted, and calling any other functions may result in crashes or other failures. . + Prints messages to stderr in case of errors. + \return 1 if successful else 0 + */ + extern int bcm2835_init(void); + + /*! Close the library, deallocating any allocated memory and closing /dev/mem + \return 1 if successful else 0 + */ + extern int bcm2835_close(void); + + /*! Sets the debug level of the library. + A value of 1 prevents mapping to /dev/mem, and makes the library print out + what it would do, rather than accessing the GPIO registers. + A value of 0, the default, causes normal operation. + Call this before calling bcm2835_init(); + \param[in] debug The new debug level. 1 means debug + */ + extern void bcm2835_set_debug(uint8_t debug); + + /*! Returns the version number of the library, same as BCM2835_VERSION + \return the current library version number + */ + extern unsigned int bcm2835_version(void); + + /*! @} */ + + /*! \defgroup BCM2835lowlevelgrp Low level register access + \ingroup BCM2835grp + These functions provide low level register access, and should not generally + need to be used + + @{ + */ + + /*! Gets the base of a register + \param[in] regbase You can use one of the common values BCM2835_REGBASE_* + in \ref bcm2835RegisterBase + \return the register base + \sa Physical Addresses + */ + extern uint32_t* bcm2835_regbase(uint8_t regbase); + + /*! Reads 32 bit value from a peripheral address WITH a memory barrier before and after each read. + This is safe, but slow. The MB before protects this read from any in-flight reads that didn't + use a MB. The MB after protects subsequent reads from another peripheral. + + \param[in] paddr Physical address to read from. See BCM2835_GPIO_BASE etc. + \return the value read from the 32 bit register + \sa Physical Addresses + */ + extern uint32_t bcm2835_peri_read(volatile uint32_t* paddr); + + /*! Reads 32 bit value from a peripheral address WITHOUT the read barriers + You should only use this when: + o your code has previously called bcm2835_peri_read() for a register + within the same peripheral, and no read or write to another peripheral has occurred since. + o your code has called bcm2835_memory_barrier() since the last access to ANOTHER peripheral. + + \param[in] paddr Physical address to read from. See BCM2835_GPIO_BASE etc. + \return the value read from the 32 bit register + \sa Physical Addresses + */ + extern uint32_t bcm2835_peri_read_nb(volatile uint32_t* paddr); + + + /*! Writes 32 bit value from a peripheral address WITH a memory barrier before and after each write + This is safe, but slow. The MB before ensures that any in-flight write to another peripheral + completes before this write is issued. The MB after ensures that subsequent reads and writes + to another peripheral will see the effect of this write. + + This is a tricky optimization; if you aren't sure, use the barrier version. + + \param[in] paddr Physical address to read from. See BCM2835_GPIO_BASE etc. + \param[in] value The 32 bit value to write + \sa Physical Addresses + */ + extern void bcm2835_peri_write(volatile uint32_t* paddr, uint32_t value); + + /*! Writes 32 bit value from a peripheral address without the write barrier + You should only use this when: + o your code has previously called bcm2835_peri_write() for a register + within the same peripheral, and no other peripheral access has occurred since. + o your code has called bcm2835_memory_barrier() since the last access to ANOTHER peripheral. + + This is a tricky optimization; if you aren't sure, use the barrier version. + + \param[in] paddr Physical address to read from. See BCM2835_GPIO_BASE etc. + \param[in] value The 32 bit value to write + \sa Physical Addresses + */ + extern void bcm2835_peri_write_nb(volatile uint32_t* paddr, uint32_t value); + + /*! Alters a number of bits in a 32 peripheral regsiter. + It reads the current valu and then alters the bits defines as 1 in mask, + according to the bit value in value. + All other bits that are 0 in the mask are unaffected. + Use this to alter a subset of the bits in a register. + Memory barriers are used. Note that this is not atomic; an interrupt + routine can cause unexpected results. + \param[in] paddr Physical address to read from. See BCM2835_GPIO_BASE etc. + \param[in] value The 32 bit value to write, masked in by mask. + \param[in] mask Bitmask that defines the bits that will be altered in the register. + \sa Physical Addresses + */ + extern void bcm2835_peri_set_bits(volatile uint32_t* paddr, uint32_t value, uint32_t mask); + /*! @} end of lowlevel */ + + /*! \defgroup BCM2835gpiogrp GPIO register access + \ingroup BCM2835grp + These functions allow you to control the GPIO interface. You can set the + function of each GPIO pin, read the input state and set the output state. + @{ + */ + + /*! Sets the Function Select register for the given pin, which configures + the pin as Input, Output or one of the 6 alternate functions. + \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + \param[in] mode Mode to set the pin to, one of BCM2835_GPIO_FSEL_* from \ref bcm2835FunctionSelect + */ + extern void bcm2835_gpio_fsel(uint8_t pin, uint8_t mode); + + /*! Sets the specified pin output to + HIGH. + \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + \sa bcm2835_gpio_write() + */ + extern void bcm2835_gpio_set(uint8_t pin); + + /*! Sets the specified pin output to + LOW. + \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + \sa bcm2835_gpio_write() + */ + extern void bcm2835_gpio_clr(uint8_t pin); + + /*! Sets any of the first 32 GPIO output pins specified in the mask to + HIGH. + \param[in] mask Mask of pins to affect. Use eg: (1 << RPI_GPIO_P1_03) | (1 << RPI_GPIO_P1_05) + \sa bcm2835_gpio_write_multi() + */ + extern void bcm2835_gpio_set_multi(uint32_t mask); + + /*! Sets any of the first 32 GPIO output pins specified in the mask to + LOW. + \param[in] mask Mask of pins to affect. Use eg: (1 << RPI_GPIO_P1_03) | (1 << RPI_GPIO_P1_05) + \sa bcm2835_gpio_write_multi() + */ + extern void bcm2835_gpio_clr_multi(uint32_t mask); + + /*! Reads the current level on the specified + pin and returns either HIGH or LOW. Works whether or not the pin + is an input or an output. + \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + \return the current level either HIGH or LOW + */ + extern uint8_t bcm2835_gpio_lev(uint8_t pin); + + /*! Event Detect Status. + Tests whether the specified pin has detected a level or edge + as requested by bcm2835_gpio_ren(), bcm2835_gpio_fen(), bcm2835_gpio_hen(), + bcm2835_gpio_len(), bcm2835_gpio_aren(), bcm2835_gpio_afen(). + Clear the flag for a given pin by calling bcm2835_gpio_set_eds(pin); + \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + \return HIGH if the event detect status for the given pin is true. + */ + extern uint8_t bcm2835_gpio_eds(uint8_t pin); + + /*! Same as bcm2835_gpio_eds() but checks if any of the pins specified in + the mask have detected a level or edge. + \param[in] mask Mask of pins to check. Use eg: (1 << RPI_GPIO_P1_03) | (1 << RPI_GPIO_P1_05) + \return Mask of pins HIGH if the event detect status for the given pin is true. + */ + extern uint32_t bcm2835_gpio_eds_multi(uint32_t mask); + + /*! Sets the Event Detect Status register for a given pin to 1, + which has the effect of clearing the flag. Use this afer seeing + an Event Detect Status on the pin. + \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + */ + extern void bcm2835_gpio_set_eds(uint8_t pin); + + /*! Same as bcm2835_gpio_set_eds() but clears the flag for any pin which + is set in the mask. + \param[in] mask Mask of pins to clear. Use eg: (1 << RPI_GPIO_P1_03) | (1 << RPI_GPIO_P1_05) + */ + extern void bcm2835_gpio_set_eds_multi(uint32_t mask); + + /*! Enable Rising Edge Detect Enable for the specified pin. + When a rising edge is detected, sets the appropriate pin in Event Detect Status. + The GPRENn registers use + synchronous edge detection. This means the input signal is sampled using the + system clock and then it is looking for a ?011? pattern on the sampled signal. This + has the effect of suppressing glitches. + \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + */ + extern void bcm2835_gpio_ren(uint8_t pin); + + /*! Disable Rising Edge Detect Enable for the specified pin. + \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + */ + extern void bcm2835_gpio_clr_ren(uint8_t pin); + + /*! Enable Falling Edge Detect Enable for the specified pin. + When a falling edge is detected, sets the appropriate pin in Event Detect Status. + The GPRENn registers use + synchronous edge detection. This means the input signal is sampled using the + system clock and then it is looking for a ?100? pattern on the sampled signal. This + has the effect of suppressing glitches. + \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + */ + extern void bcm2835_gpio_fen(uint8_t pin); + + /*! Disable Falling Edge Detect Enable for the specified pin. + \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + */ + extern void bcm2835_gpio_clr_fen(uint8_t pin); + + /*! Enable High Detect Enable for the specified pin. + When a HIGH level is detected on the pin, sets the appropriate pin in Event Detect Status. + \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + */ + extern void bcm2835_gpio_hen(uint8_t pin); + + /*! Disable High Detect Enable for the specified pin. + \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + */ + extern void bcm2835_gpio_clr_hen(uint8_t pin); + + /*! Enable Low Detect Enable for the specified pin. + When a LOW level is detected on the pin, sets the appropriate pin in Event Detect Status. + \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + */ + extern void bcm2835_gpio_len(uint8_t pin); + + /*! Disable Low Detect Enable for the specified pin. + \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + */ + extern void bcm2835_gpio_clr_len(uint8_t pin); + + /*! Enable Asynchronous Rising Edge Detect Enable for the specified pin. + When a rising edge is detected, sets the appropriate pin in Event Detect Status. + Asynchronous means the incoming signal is not sampled by the system clock. As such + rising edges of very short duration can be detected. + \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + */ + extern void bcm2835_gpio_aren(uint8_t pin); + + /*! Disable Asynchronous Rising Edge Detect Enable for the specified pin. + \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + */ + extern void bcm2835_gpio_clr_aren(uint8_t pin); + + /*! Enable Asynchronous Falling Edge Detect Enable for the specified pin. + When a falling edge is detected, sets the appropriate pin in Event Detect Status. + Asynchronous means the incoming signal is not sampled by the system clock. As such + falling edges of very short duration can be detected. + \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + */ + extern void bcm2835_gpio_afen(uint8_t pin); + + /*! Disable Asynchronous Falling Edge Detect Enable for the specified pin. + \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + */ + extern void bcm2835_gpio_clr_afen(uint8_t pin); + + /*! Sets the Pull-up/down register for the given pin. This is + used with bcm2835_gpio_pudclk() to set the Pull-up/down resistor for the given pin. + However, it is usually more convenient to use bcm2835_gpio_set_pud(). + \param[in] pud The desired Pull-up/down mode. One of BCM2835_GPIO_PUD_* from bcm2835PUDControl + \sa bcm2835_gpio_set_pud() + */ + extern void bcm2835_gpio_pud(uint8_t pud); + + /*! Clocks the Pull-up/down value set earlier by bcm2835_gpio_pud() into the pin. + \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + \param[in] on HIGH to clock the value from bcm2835_gpio_pud() into the pin. + LOW to remove the clock. + \sa bcm2835_gpio_set_pud() + */ + extern void bcm2835_gpio_pudclk(uint8_t pin, uint8_t on); + + /*! Reads and returns the Pad Control for the given GPIO group. + \param[in] group The GPIO pad group number, one of BCM2835_PAD_GROUP_GPIO_* + \return Mask of bits from BCM2835_PAD_* from \ref bcm2835PadGroup + */ + extern uint32_t bcm2835_gpio_pad(uint8_t group); + + /*! Sets the Pad Control for the given GPIO group. + \param[in] group The GPIO pad group number, one of BCM2835_PAD_GROUP_GPIO_* + \param[in] control Mask of bits from BCM2835_PAD_* from \ref bcm2835PadGroup. Note + that it is not necessary to include BCM2835_PAD_PASSWRD in the mask as this + is automatically included. + */ + extern void bcm2835_gpio_set_pad(uint8_t group, uint32_t control); + + /*! Delays for the specified number of milliseconds. + Uses nanosleep(), and therefore does not use CPU until the time is up. + However, you are at the mercy of nanosleep(). From the manual for nanosleep(): + If the interval specified in req is not an exact multiple of the granularity + underlying clock (see time(7)), then the interval will be + rounded up to the next multiple. Furthermore, after the sleep completes, + there may still be a delay before the CPU becomes free to once + again execute the calling thread. + \param[in] millis Delay in milliseconds + */ + extern void bcm2835_delay (unsigned int millis); + + /*! Delays for the specified number of microseconds. + Uses a combination of nanosleep() and a busy wait loop on the BCM2835 system timers, + However, you are at the mercy of nanosleep(). From the manual for nanosleep(): + If the interval specified in req is not an exact multiple of the granularity + underlying clock (see time(7)), then the interval will be + rounded up to the next multiple. Furthermore, after the sleep completes, + there may still be a delay before the CPU becomes free to once + again execute the calling thread. + For times less than about 450 microseconds, uses a busy wait on the System Timer. + It is reported that a delay of 0 microseconds on RaspberryPi will in fact + result in a delay of about 80 microseconds. Your mileage may vary. + \param[in] micros Delay in microseconds + */ + extern void bcm2835_delayMicroseconds (uint64_t micros); + + /*! Sets the output state of the specified pin + \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + \param[in] on HIGH sets the output to HIGH and LOW to LOW. + */ + extern void bcm2835_gpio_write(uint8_t pin, uint8_t on); + + /*! Sets any of the first 32 GPIO output pins specified in the mask to the state given by on + \param[in] mask Mask of pins to affect. Use eg: (1 << RPI_GPIO_P1_03) | (1 << RPI_GPIO_P1_05) + \param[in] on HIGH sets the output to HIGH and LOW to LOW. + */ + extern void bcm2835_gpio_write_multi(uint32_t mask, uint8_t on); + + /*! Sets the first 32 GPIO output pins specified in the mask to the value given by value + \param[in] value values required for each bit masked in by mask, eg: (1 << RPI_GPIO_P1_03) | (1 << RPI_GPIO_P1_05) + \param[in] mask Mask of pins to affect. Use eg: (1 << RPI_GPIO_P1_03) | (1 << RPI_GPIO_P1_05) + */ + extern void bcm2835_gpio_write_mask(uint32_t value, uint32_t mask); + + /*! Sets the Pull-up/down mode for the specified pin. This is more convenient than + clocking the mode in with bcm2835_gpio_pud() and bcm2835_gpio_pudclk(). + \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + \param[in] pud The desired Pull-up/down mode. One of BCM2835_GPIO_PUD_* from bcm2835PUDControl + */ + extern void bcm2835_gpio_set_pud(uint8_t pin, uint8_t pud); + + /*! @} */ + + /*! \defgroup BCM2835spigrp SPI access + \ingroup BCM2835grp + These functions let you use SPI0 (Serial Peripheral Interface) to + interface with an external SPI device. + @{ + */ + + /*! Start SPI operations. + Forces RPi SPI0 pins P1-19 (MOSI), P1-21 (MISO), P1-23 (CLK), P1-24 (CE0) and P1-26 (CE1) + to alternate function ALT0, which enables those pins for SPI interface. + You should call bcm2835_spi_end() when all SPI funcitons are complete to return the pins to + their default functions. + \sa bcm2835_spi_end() + \return 1 if successful, 0 otherwise (perhaps because you are not running as root) + */ + extern int bcm2835_spi_begin(void); + + /*! End SPI operations. + SPI0 pins P1-19 (MOSI), P1-21 (MISO), P1-23 (CLK), P1-24 (CE0) and P1-26 (CE1) + are returned to their default INPUT behaviour. + */ + extern void bcm2835_spi_end(void); + + /*! Sets the SPI bit order + NOTE: has no effect. Not supported by SPI0. + Defaults to + \param[in] order The desired bit order, one of BCM2835_SPI_BIT_ORDER_*, + see \ref bcm2835SPIBitOrder + */ + extern void bcm2835_spi_setBitOrder(uint8_t order); + + /*! Sets the SPI clock divider and therefore the + SPI clock speed. + \param[in] divider The desired SPI clock divider, one of BCM2835_SPI_CLOCK_DIVIDER_*, + see \ref bcm2835SPIClockDivider + */ + extern void bcm2835_spi_setClockDivider(uint16_t divider); + + /*! Sets the SPI data mode + Sets the clock polariy and phase + \param[in] mode The desired data mode, one of BCM2835_SPI_MODE*, + see \ref bcm2835SPIMode + */ + extern void bcm2835_spi_setDataMode(uint8_t mode); + + /*! Sets the chip select pin(s) + When an bcm2835_spi_transfer() is made, the selected pin(s) will be asserted during the + transfer. + \param[in] cs Specifies the CS pins(s) that are used to activate the desired slave. + One of BCM2835_SPI_CS*, see \ref bcm2835SPIChipSelect + */ + extern void bcm2835_spi_chipSelect(uint8_t cs); + + /*! Sets the chip select pin polarity for a given pin + When an bcm2835_spi_transfer() occurs, the currently selected chip select pin(s) + will be asserted to the + value given by active. When transfers are not happening, the chip select pin(s) + return to the complement (inactive) value. + \param[in] cs The chip select pin to affect + \param[in] active Whether the chip select pin is to be active HIGH + */ + extern void bcm2835_spi_setChipSelectPolarity(uint8_t cs, uint8_t active); + + /*! Transfers one byte to and from the currently selected SPI slave. + Asserts the currently selected CS pins (as previously set by bcm2835_spi_chipSelect) + during the transfer. + Clocks the 8 bit value out on MOSI, and simultaneously clocks in data from MISO. + Returns the read data byte from the slave. + Uses polled transfer as per section 10.6.1 of the BCM 2835 ARM Peripherls manual + \param[in] value The 8 bit data byte to write to MOSI + \return The 8 bit byte simultaneously read from MISO + \sa bcm2835_spi_transfern() + */ + extern uint8_t bcm2835_spi_transfer(uint8_t value); + + /*! Transfers any number of bytes to and from the currently selected SPI slave. + Asserts the currently selected CS pins (as previously set by bcm2835_spi_chipSelect) + during the transfer. + Clocks the len 8 bit bytes out on MOSI, and simultaneously clocks in data from MISO. + The data read read from the slave is placed into rbuf. rbuf must be at least len bytes long + Uses polled transfer as per section 10.6.1 of the BCM 2835 ARM Peripherls manual + \param[in] tbuf Buffer of bytes to send. + \param[out] rbuf Received bytes will by put in this buffer + \param[in] len Number of bytes in the tbuf buffer, and the number of bytes to send/received + \sa bcm2835_spi_transfer() + */ + extern void bcm2835_spi_transfernb(char* tbuf, char* rbuf, uint32_t len); + + /*! Transfers any number of bytes to and from the currently selected SPI slave + using bcm2835_spi_transfernb. + The returned data from the slave replaces the transmitted data in the buffer. + \param[in,out] buf Buffer of bytes to send. Received bytes will replace the contents + \param[in] len Number of bytes int eh buffer, and the number of bytes to send/received + \sa bcm2835_spi_transfer() + */ + extern void bcm2835_spi_transfern(char* buf, uint32_t len); + + /*! Transfers any number of bytes to the currently selected SPI slave. + Asserts the currently selected CS pins (as previously set by bcm2835_spi_chipSelect) + during the transfer. + \param[in] buf Buffer of bytes to send. + \param[in] len Number of bytes in the tbuf buffer, and the number of bytes to send + */ + extern void bcm2835_spi_writenb(char* buf, uint32_t len); + + /*! @} */ + + /*! \defgroup BCM2835i2cgrp I2C access + \ingroup BCM2835grp + These functions let you use I2C (The Broadcom Serial Control bus with the Philips + I2C bus/interface version 2.1 January 2000.) to interface with an external I2C device. + @{ + */ + + /*! Start I2C operations. + Forces RPi I2C pins P1-03 (SDA) and P1-05 (SCL) + to alternate function ALT0, which enables those pins for I2C interface. + You should call bcm2835_i2c_end() when all I2C functions are complete to return the pins to + their default functions + \return 1 if successful, 0 otherwise (perhaps because you are not running as root) + \sa bcm2835_i2c_end() + */ + extern int bcm2835_i2c_begin(void); + + /*! End I2C operations. + I2C pins P1-03 (SDA) and P1-05 (SCL) + are returned to their default INPUT behaviour. + */ + extern void bcm2835_i2c_end(void); + + /*! Sets the I2C slave address. + \param[in] addr The I2C slave address. + */ + extern void bcm2835_i2c_setSlaveAddress(uint8_t addr); + + /*! Sets the I2C clock divider and therefore the I2C clock speed. + \param[in] divider The desired I2C clock divider, one of BCM2835_I2C_CLOCK_DIVIDER_*, + see \ref bcm2835I2CClockDivider + */ + extern void bcm2835_i2c_setClockDivider(uint16_t divider); + + /*! Sets the I2C clock divider by converting the baudrate parameter to + the equivalent I2C clock divider. ( see \sa bcm2835_i2c_setClockDivider) + For the I2C standard 100khz you would set baudrate to 100000 + The use of baudrate corresponds to its use in the I2C kernel device + driver. (Of course, bcm2835 has nothing to do with the kernel driver) + */ + extern void bcm2835_i2c_set_baudrate(uint32_t baudrate); + + /*! Transfers any number of bytes to the currently selected I2C slave. + (as previously set by \sa bcm2835_i2c_setSlaveAddress) + \param[in] buf Buffer of bytes to send. + \param[in] len Number of bytes in the buf buffer, and the number of bytes to send. + \return reason see \ref bcm2835I2CReasonCodes + */ + extern uint8_t bcm2835_i2c_write(const char * buf, uint32_t len); + + /*! Transfers any number of bytes from the currently selected I2C slave. + (as previously set by \sa bcm2835_i2c_setSlaveAddress) + \param[in] buf Buffer of bytes to receive. + \param[in] len Number of bytes in the buf buffer, and the number of bytes to received. + \return reason see \ref bcm2835I2CReasonCodes + */ + extern uint8_t bcm2835_i2c_read(char* buf, uint32_t len); + + /*! Allows reading from I2C slaves that require a repeated start (without any prior stop) + to read after the required slave register has been set. For example, the popular + MPL3115A2 pressure and temperature sensor. Note that your device must support or + require this mode. If your device does not require this mode then the standard + combined: + \sa bcm2835_i2c_write + \sa bcm2835_i2c_read + are a better choice. + Will read from the slave previously set by \sa bcm2835_i2c_setSlaveAddress + \param[in] regaddr Buffer containing the slave register you wish to read from. + \param[in] buf Buffer of bytes to receive. + \param[in] len Number of bytes in the buf buffer, and the number of bytes to received. + \return reason see \ref bcm2835I2CReasonCodes + */ + extern uint8_t bcm2835_i2c_read_register_rs(char* regaddr, char* buf, uint32_t len); + + /*! Allows sending an arbitrary number of bytes to I2C slaves before issuing a repeated + start (with no prior stop) and reading a response. + Necessary for devices that require such behavior, such as the MLX90620. + Will write to and read from the slave previously set by \sa bcm2835_i2c_setSlaveAddress + \param[in] cmds Buffer containing the bytes to send before the repeated start condition. + \param[in] cmds_len Number of bytes to send from cmds buffer + \param[in] buf Buffer of bytes to receive. + \param[in] buf_len Number of bytes to receive in the buf buffer. + \return reason see \ref bcm2835I2CReasonCodes + */ + extern uint8_t bcm2835_i2c_write_read_rs(char* cmds, uint32_t cmds_len, char* buf, uint32_t buf_len); + + /*! @} */ + + /*! \defgroup BCM2835stgrp System Timer access + \ingroup BCM2835grp + Allows access to and delays using the System Timer Counter. + @{ + */ + + /*! Read the System Timer Counter register. + \return the value read from the System Timer Counter Lower 32 bits register + */ + extern uint64_t bcm2835_st_read(void); + + /*! Delays for the specified number of microseconds with offset. + \param[in] offset_micros Offset in microseconds + \param[in] micros Delay in microseconds + */ + extern void bcm2835_st_delay(uint64_t offset_micros, uint64_t micros); + + /*! @} */ + + /*! \defgroup BCM2835pwmgrp Pulse Width Modulation + \ingroup BCM2835grp + Allows control of 2 independent PWM channels. A limited subset of GPIO pins + can be connected to one of these 2 channels, allowing PWM control of GPIO pins. + You have to set the desired pin into a particular Alt Fun to PWM output. See the PWM + documentation on the Main Page. + @{ + */ + + /*! Sets the PWM clock divisor, + to control the basic PWM pulse widths. + \param[in] divisor Divides the basic 19.2MHz PWM clock. You can use one of the common + values BCM2835_PWM_CLOCK_DIVIDER_* in \ref bcm2835PWMClockDivider + */ + extern void bcm2835_pwm_set_clock(uint32_t divisor); + + /*! Sets the mode of the given PWM channel, + allowing you to control the PWM mode and enable/disable that channel + \param[in] channel The PWM channel. 0 or 1. + \param[in] markspace Set true if you want Mark-Space mode. 0 for Balanced mode. + \param[in] enabled Set true to enable this channel and produce PWM pulses. + */ + extern void bcm2835_pwm_set_mode(uint8_t channel, uint8_t markspace, uint8_t enabled); + + /*! Sets the maximum range of the PWM output. + The data value can vary between 0 and this range to control PWM output + \param[in] channel The PWM channel. 0 or 1. + \param[in] range The maximum value permitted for DATA. + */ + extern void bcm2835_pwm_set_range(uint8_t channel, uint32_t range); + + /*! Sets the PWM pulse ratio to emit to DATA/RANGE, + where RANGE is set by bcm2835_pwm_set_range(). + \param[in] channel The PWM channel. 0 or 1. + \param[in] data Controls the PWM output ratio as a fraction of the range. + Can vary from 0 to RANGE. + */ + extern void bcm2835_pwm_set_data(uint8_t channel, uint32_t data); + + /*! @} */ +#ifdef __cplusplus +} +#endif + +#endif /* BCM2835_H */ diff --git a/drivers/RPi/piHiPri.c b/drivers/RPi/piHiPri.c new file mode 100644 index 0000000..63c0216 --- /dev/null +++ b/drivers/RPi/piHiPri.c @@ -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 . + *********************************************************************** + */ + +#include +#include + +/* + * 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) ; +} diff --git a/drivers/RPi/rpi_util.cpp b/drivers/RPi/rpi_util.cpp new file mode 100644 index 0000000..5954fcc --- /dev/null +++ b/drivers/RPi/rpi_util.cpp @@ -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 + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 + 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); +} diff --git a/drivers/RPi/rpi_util.h b/drivers/RPi/rpi_util.h new file mode 100644 index 0000000..3d31eb9 --- /dev/null +++ b/drivers/RPi/rpi_util.h @@ -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 + * 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 +#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 From 8ccb1ca86dada9ca9a7bbd19eda165b061e5bf15 Mon Sep 17 00:00:00 2001 From: Embedded Innovation Date: Thu, 22 Sep 2016 18:21:02 +0200 Subject: [PATCH 29/33] Fix gateway RX indication Added GW RX indication for MQTT and Serial gateway (PR #585). Only send RX indication when valid message has been received. --- core/MyGatewayTransportEthernet.cpp | 10 +++++++--- core/MyGatewayTransportMQTTClient.cpp | 1 + core/MyGatewayTransportSerial.cpp | 6 +++++- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/core/MyGatewayTransportEthernet.cpp b/core/MyGatewayTransportEthernet.cpp index cef41e7..299e339 100644 --- a/core/MyGatewayTransportEthernet.cpp +++ b/core/MyGatewayTransportEthernet.cpp @@ -272,19 +272,23 @@ bool gatewayTransportAvailable() if (packet_size) { //debug(PSTR("UDP packet available. Size:%d\n"), packet_size); - setIndication(INDICATION_GW_RX); #if defined(MY_GATEWAY_ESP8266) _ethernetServer.read(inputString[0].string, MY_GATEWAY_MAX_RECEIVE_LENGTH); inputString[0].string[packet_size] = 0; debug(PSTR("UDP packet received: %s\n"), inputString[0].string); - return protocolParse(_ethernetMsg, inputString[0].string); + const bool ok = protocolParse(_ethernetMsg, inputString[0].string); #else _ethernetServer.read(inputString.string, MY_GATEWAY_MAX_RECEIVE_LENGTH); inputString.string[packet_size] = 0; debug(PSTR("UDP packet received: %s\n"), inputString.string); _w5100_spi_en(false); - return protocolParse(_ethernetMsg, inputString.string); + const bool ok = protocolParse(_ethernetMsg, inputString.string); #endif + if (ok) + { + setIndication(INDICATION_GW_RX); + } + return ok; } #else #if defined(MY_GATEWAY_ESP8266) diff --git a/core/MyGatewayTransportMQTTClient.cpp b/core/MyGatewayTransportMQTTClient.cpp index b6ca377..a385382 100644 --- a/core/MyGatewayTransportMQTTClient.cpp +++ b/core/MyGatewayTransportMQTTClient.cpp @@ -131,6 +131,7 @@ void incomingMQTT(char* topic, byte* payload, unsigned int length) { _MQTT_msg.set((const char*)payload); } _MQTT_available = true; + setIndication(INDICATION_GW_RX); } } i++; diff --git a/core/MyGatewayTransportSerial.cpp b/core/MyGatewayTransportSerial.cpp index bbad4b3..74a118e 100644 --- a/core/MyGatewayTransportSerial.cpp +++ b/core/MyGatewayTransportSerial.cpp @@ -58,7 +58,11 @@ bool gatewayTransportAvailable() { if (inChar == '\n') { _serialInputString[_serialInputPos] = 0; bool ok = protocolParse(_serialMsg, _serialInputString); - _serialInputPos = 0; + if (ok) + { + setIndication(INDICATION_GW_RX); + } + _serialInputPos = 0; return ok; } else { // add it to the inputString: From 6d47c33ad8b7475d08b25900435037de0a588c96 Mon Sep 17 00:00:00 2001 From: Marcelo Aquino Date: Sat, 24 Sep 2016 21:55:01 -0300 Subject: [PATCH 30/33] Add support for RX/TX/ERR leds to RPi --- MySensors.h | 5 +++++ examples_linux/mysGateway.cpp | 11 +++++++++++ 2 files changed, 16 insertions(+) diff --git a/MySensors.h b/MySensors.h index ad59f3f..39adeee 100644 --- a/MySensors.h +++ b/MySensors.h @@ -82,6 +82,11 @@ #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 diff --git a/examples_linux/mysGateway.cpp b/examples_linux/mysGateway.cpp index 1d7c745..3c65327 100644 --- a/examples_linux/mysGateway.cpp +++ b/examples_linux/mysGateway.cpp @@ -50,6 +50,17 @@ //#define MY_MQTT_USER "username" //#define MY_MQTT_PASSWORD "password" +// Flash leds on rx/tx/err +//#define MY_LEDS_BLINKING_FEATURE +// Inverse the blinking feature +//#define MY_WITH_LEDS_BLINKING_INVERSE +// Set blinking period +//#define MY_DEFAULT_LED_BLINK_PERIOD 300 + +// Led pins used if blinking feature is enabled above +#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 // the PCB, on board #include From 0c11354a2a9a0b67079b6c2723e9134923e1137a Mon Sep 17 00:00:00 2001 From: Marcelo Aquino Date: Thu, 22 Sep 2016 21:35:22 -0300 Subject: [PATCH 31/33] Fix Client Mode for Ethernet gateway If MY_GATEWAY_CLIENT_MODE or MY_CONTROLLER_URL_ADDRESS are present, set MY_GATEWAY_CLIENT_MODE. MY_USE_UDP requires that MY_GATEWAY_CLIENT_MODE is set. Add some ifdefs to filter some variables not needed in client mode. Using global variables to keep the connection to controller open therefore, no need to reconnect every time we need to send/receive something. Add the remaining code to handle incoming messages from controllers. --- MySensors.h | 12 ++-- core/MyGatewayTransportEthernet.cpp | 99 ++++++++++++++++++++++------- 2 files changed, 84 insertions(+), 27 deletions(-) diff --git a/MySensors.h b/MySensors.h index 821af20..8d5ad27 100644 --- a/MySensors.h +++ b/MySensors.h @@ -141,6 +141,13 @@ #endif // GATEWAY - TRANSPORT +#if defined(MY_CONTROLLER_IP_ADDRESS) || defined(MY_CONTROLLER_URL_ADDRESS) + #define MY_GATEWAY_CLIENT_MODE +#endif +#if defined(MY_USE_UDP) && !defined(MY_GATEWAY_CLIENT_MODE) + #error You must specify MY_CONTROLLER_IP_ADDRESS or MY_CONTROLLER_URL_ADDRESS for UDP +#endif + #if defined(MY_GATEWAY_MQTT_CLIENT) #if defined(MY_RADIO_FEATURE) // We assume that a gateway having a radio also should act as repeater @@ -148,7 +155,7 @@ #endif // GATEWAY - COMMON FUNCTIONS // We only support MQTT Client using W5100 and ESP8266 at the moment - #if !(defined(MY_CONTROLLER_URL_ADDRESS) || defined(MY_CONTROLLER_IP_ADDRESS)) + #if !defined(MY_GATEWAY_CLIENT_MODE) #error You must specify MY_CONTROLLER_IP_ADDRESS or MY_CONTROLLER_URL_ADDRESS #endif @@ -177,9 +184,6 @@ // We assume that a gateway having a radio also should act as repeater #define MY_REPEATER_FEATURE #endif - #if defined(MY_CONTROLLER_IP_ADDRESS) - #define MY_GATEWAY_CLIENT_MODE - #endif #if !defined(MY_PORT) #error You must define MY_PORT (controller or gatway port to open) #endif diff --git a/core/MyGatewayTransportEthernet.cpp b/core/MyGatewayTransportEthernet.cpp index 299e339..c27d501 100644 --- a/core/MyGatewayTransportEthernet.cpp +++ b/core/MyGatewayTransportEthernet.cpp @@ -52,17 +52,19 @@ typedef struct IPAddress _gatewayIp(MY_IP_GATEWAY_ADDRESS); IPAddress _subnetIp(MY_IP_SUBNET_ADDRESS); #endif - static bool clientsConnected[MY_GATEWAY_MAX_CLIENTS]; + #if !defined(MY_GATEWAY_CLIENT_MODE) + static bool clientsConnected[MY_GATEWAY_MAX_CLIENTS]; + #endif #endif #if defined(MY_USE_UDP) EthernetUDP _ethernetServer; -#else +#elif !defined(MY_GATEWAY_CLIENT_MODE) EthernetServer _ethernetServer(_ethernetGatewayPort); #endif -#if defined(MY_GATEWAY_ESP8266) +#if defined(MY_GATEWAY_ESP8266) && !defined(MY_GATEWAY_CLIENT_MODE) static EthernetClient clients[MY_GATEWAY_MAX_CLIENTS]; static inputBuffer inputString[MY_GATEWAY_MAX_CLIENTS]; #else @@ -137,14 +139,29 @@ bool gatewayTransportInit() { MY_SERIALDEVICE.println(Ethernet.localIP()); // give the Ethernet interface a second to initialize delay(1000); - #endif + #endif /* MY_GATEWAY_ESP8266 */ #ifdef MY_USE_UDP _ethernetServer.begin(_ethernetGatewayPort); + #elif defined(MY_GATEWAY_CLIENT_MODE) + #if defined(MY_CONTROLLER_URL_ADDRESS) + if (!client.connect(MY_CONTROLLER_URL_ADDRESS, MY_PORT)) { + #else + 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 // we have to use pointers due to the constructor of EthernetServer _ethernetServer.begin(); - #endif /* USE_UDP */ + #endif _w5100_spi_en(false); return true; } @@ -157,25 +174,37 @@ bool gatewayTransportSend(MyMessage &message) setIndication(INDICATION_GW_TX); _w5100_spi_en(true); - #if defined(MY_CONTROLLER_IP_ADDRESS) + #if defined(MY_GATEWAY_CLIENT_MODE) #if defined(MY_USE_UDP) - _ethernetServer.beginPacket(_ethernetControllerIP, MY_PORT); + #if defined(MY_CONTROLLER_URL_ADDRESS) + _ethernetServer.beginPacket(MY_CONTROLLER_URL_ADDRESS, MY_PORT); + #else + _ethernetServer.beginPacket(_ethernetControllerIP, MY_PORT); + #endif _ethernetServer.write(_ethernetMsg, strlen(_ethernetMsg)); // returns 1 if the packet was sent successfully ret = _ethernetServer.endPacket(); #else - EthernetClient client; + if (!client.connected()) { + client.stop(); #if defined(MY_CONTROLLER_URL_ADDRESS) - if (client.connected() || client.connect(MY_CONTROLLER_URL_ADDRESS, MY_PORT)) { - #else - if (client.connected() || client.connect(_ethernetControllerIP, MY_PORT)) { - #endif - client.write(_ethernetMsg, strlen(_ethernetMsg)); - } - else { - // connecting to the server failed! - ret = false; - } + if (!client.connect(MY_CONTROLLER_URL_ADDRESS, MY_PORT)) { + #else + 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 { + ret = client.write(_ethernetMsg, strlen(_ethernetMsg)) > 0; + } #endif #else // Send message to connected clients @@ -188,16 +217,16 @@ bool gatewayTransportSend(MyMessage &message) } } #else - _ethernetServer.write(_ethernetMsg); + ret = _ethernetServer.write(_ethernetMsg) > 0; #endif - #endif + #endif /* MY_GATEWAY_CLIENT_MODE */ _w5100_spi_en(false); return ret; } -#if defined(MY_GATEWAY_ESP8266) +#if defined(MY_GATEWAY_ESP8266) && !defined(MY_GATEWAY_CLIENT_MODE) bool _readFromClient(uint8_t i) { while (clients[i].connected() && clients[i].available()) { char inChar = clients[i].read(); @@ -290,6 +319,30 @@ bool gatewayTransportAvailable() } return ok; } + #elif defined(MY_GATEWAY_CLIENT_MODE) + if (!client.connected()) { + client.stop(); + #if defined(MY_CONTROLLER_URL_ADDRESS) + if (!client.connect(MY_CONTROLLER_URL_ADDRESS, MY_PORT)) { + #else + 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(); + } + } + if (_readFromClient()) { + setIndication(INDICATION_GW_RX); + _w5100_spi_en(false); + return true; + } #else #if defined(MY_GATEWAY_ESP8266) // ESP8266: Go over list of clients and stop any that are no longer connected. @@ -356,7 +409,7 @@ bool gatewayTransportAvailable() } } } - #endif + #endif /* MY_GATEWAY_ESP8266 */ #endif _w5100_spi_en(false); return false; @@ -393,4 +446,4 @@ void gatewayTransportRenewIP() _w5100_spi_en(false); next_time = now + MY_IP_RENEWAL_INTERVAL; } -#endif /* IP_ADDRESS_DHCP */ +#endif From d8c45e9f07c7900632d0c788617a155faca545c9 Mon Sep 17 00:00:00 2001 From: Tom Date: Thu, 29 Sep 2016 20:02:32 +0100 Subject: [PATCH 32/33] V_Light Deprecated Change V_Light for V_Status as V_Light is deprecated as per is ttps://www.mysensors.org/download/serial_api_20 Change S_Light for S_Binary generic. --- examples/RelayActuator/RelayActuator.ino | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/RelayActuator/RelayActuator.ino b/examples/RelayActuator/RelayActuator.ino index 7f82c71..908cb9b 100644 --- a/examples/RelayActuator/RelayActuator.ino +++ b/examples/RelayActuator/RelayActuator.ino @@ -65,7 +65,7 @@ void presentation() for (int sensor=1, pin=RELAY_1; sensor<=NUMBER_OF_RELAYS;sensor++, pin++) { // Register all sensors to gw (they will be created as child devices) - present(sensor, S_LIGHT); + present(sensor, S_BINARY); } } @@ -77,7 +77,7 @@ void loop() void receive(const MyMessage &message) { // We only expect one type of message from controller. But we better check anyway. - if (message.type==V_LIGHT) { + if (message.type==V_STATUS) { // Change relay state digitalWrite(message.sensor-1+RELAY_1, message.getBool()?RELAY_ON:RELAY_OFF); // Store state in eeprom From 0a9351988faf56efbf90d0e3ab5c158718add591 Mon Sep 17 00:00:00 2001 From: Marcelo Aquino Date: Fri, 30 Sep 2016 17:04:40 -0300 Subject: [PATCH 33/33] Update configure script Add options to set status leds. Support controller ip address separated by dots. Fix controller url address option set. --- configure | 29 ++++++++++++++++++++++------- examples_linux/mysGateway.cpp | 6 +++--- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/configure b/configure index b2b2dc8..570edde 100755 --- a/configure +++ b/configure @@ -14,8 +14,8 @@ Help: Building options: --soc=[BCM2835|BCM2836|AM33XX|A10|A13|A20|H3] SoC type to be used. [configure autodetected] - --cpu-flags= CPU defining/optimizing flags to be used. [configure autodetected] - --extra-cxxflags= Extra C flags passed to C/C++ compilation. [] + --cpu-flags= CPU defining/optimizing flags to be used. [configure autodetected] + --extra-cxxflags= Extra C flags passed to C/C++ compilation. [] --extra-ldflags= Extra C flags passed to linking. [] --cxx_compiler= C++ compiler [arm-linux-gnueabihf-g++][g++] --no-clean Don't clean previous build artifacts. @@ -25,8 +25,7 @@ Installation options: --gateway-dir= Gateway files installation directory. [PREFIX/bin] MySensors options: - --my-debug=[enable|disable] - Enables or disables MySensors core debugging. [enable] + --my-debug=[enable|disable] Enables or disables MySensors core debugging. [enable] --my-config-file= Config file path. [/etc/mysensors.dat] --my-gateway=[none|ethernet|serial|mqtt] Gateway type, set to none to disable gateway feature. [ethernet] @@ -34,7 +33,6 @@ MySensors options: Controller or MQTT broker url. --my-controller-ip-address= Controller or MQTT broker ip. - Use commas instead of points. Example: 127,0,0,1 --my-port= The port to keep open on gateway mode. If gateway is set to mqtt, it sets the broker port. --my-serial-port= Serial port. [/dev/ttyACM0] @@ -56,6 +54,10 @@ MySensors options: --my-rf24-irq-pin= Pin number connected to nRF24L01 IRQ pin. --my-rx-message-buffer-size= Buffer size for incoming messages when using rf24 interrupts. [20] + --my-leds-err-pin= Error LED pin. + --my-leds-rx-pin= Receive LED pin. + --my-leds-tx-pin= Transmit LED pin. + --my-leds-blinking-inverse Inverse the blinking feature. EOF } @@ -263,10 +265,11 @@ for opt do CXXFLAGS="-DMY_RF24_PA_LEVEL=${optarg} $CXXFLAGS" ;; --my-controller-url-address=*) - CXXFLAGS="-DMY_CONTROLLER_URL_ADDRESS=${optarg} $CXXFLAGS" + CXXFLAGS="-DMY_CONTROLLER_URL_ADDRESS=\\\"${optarg}\\\" $CXXFLAGS" ;; --my-controller-ip-address=*) - CXXFLAGS="-DMY_CONTROLLER_IP_ADDRESS=${optarg} $CXXFLAGS" + controller_ip=`echo ${optarg//./,}` + CXXFLAGS="-DMY_CONTROLLER_IP_ADDRESS=${controller_ip} $CXXFLAGS" ;; --my-port=*) CXXFLAGS="-DMY_PORT=${optarg} $CXXFLAGS" @@ -286,6 +289,18 @@ for opt do --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" ;; diff --git a/examples_linux/mysGateway.cpp b/examples_linux/mysGateway.cpp index de508bc..c0c69c2 100644 --- a/examples_linux/mysGateway.cpp +++ b/examples_linux/mysGateway.cpp @@ -51,9 +51,9 @@ //#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 // the PCB, on board +//#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