Add support for RaspberryPi

This commit is contained in:
Marcelo Aquino
2016-08-08 01:28:18 -03:00
committed by Marcelo Aquino
parent c7efee31dd
commit 35cab8c26d
10 changed files with 661 additions and 5 deletions
+67
View File
@@ -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)
+38
View File
@@ -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 <RF24.h>
#include <RF24_config.h>
#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
+26 -3
View File
@@ -40,7 +40,7 @@
* @def MY_NODE_TYPE
* @brief Contain a string describing the class of sketch/node (gateway/repeater/sensor).
*/
#if defined(MY_GATEWAY_SERIAL) || defined(MY_GATEWAY_W5100) || defined(MY_GATEWAY_ENC28J60) || defined(MY_GATEWAY_ESP8266) || defined(MY_GATEWAY_MQTT_CLIENT)
#if defined(MY_GATEWAY_SERIAL) || defined(MY_GATEWAY_W5100) || defined(MY_GATEWAY_ENC28J60) || defined(MY_GATEWAY_ESP8266) || defined(MY_GATEWAY_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
@@ -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 <marceloaqno@gmail.org>
* Copyleft (c) 2016, Marcelo Aquino
* Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
*
* Documentation: http://www.mysensors.org
* Support Forum: http://forum.mysensors.org
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*/
#include "MyGatewayTransport.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <signal.h>
#include <list>
//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<struct MyMessage> 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(&ethernetMsg_mutex);
empty = ethernetMsg_q.empty();
pthread_mutex_unlock(&ethernetMsg_mutex);
return !empty;
}
MyMessage& gatewayTransportReceive()
{
// Return the last parsed message
pthread_mutex_lock(&ethernetMsg_mutex);
_ethernetMsg = ethernetMsg_q.front();
ethernetMsg_q.pop_front();
pthread_mutex_unlock(&ethernetMsg_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(&ethernetMsg_mutex);
ethernetMsg_q.push_back(ethernetMsg);
pthread_mutex_unlock(&ethernetMsg_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;
}
@@ -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 <marceloaqno@gmail.org>
* Copyleft (c) 2016, Marcelo Aquino
* Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
*
* Documentation: http://www.mysensors.org
* Support Forum: http://forum.mysensors.org
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*/
#include "MyHwLinuxGeneric.h"
#include <sys/time.h>
#include <stdarg.h>
#include <string.h>
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<unsigned long int>(addr);
if (length && offs + length <= _length) {
memcpy(buf, _config+offs, length);
}
}
void hwWriteConfigBlock(void* buf, void* addr, size_t length)
{
unsigned long int offs = reinterpret_cast<unsigned long int>(addr);
if (length && offs + length <= _length) {
memcpy(_config+offs, buf, length);
}
}
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
@@ -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 <marceloaqno@gmail.org>
* Copyleft (c) 2016, Marcelo Aquino
* Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
*
* Documentation: http://www.mysensors.org
* Support Forum: http://forum.mysensors.org
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*/
#ifndef MyHwLinuxGeneric_h
#define MyHwLinuxGeneric_h
#include "MyHw.h"
#include <stdlib.h>
#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
+13
View File
@@ -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;
}
@@ -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)
@@ -20,8 +20,10 @@
#include "MyConfig.h"
#include "MyTransport.h"
#include <stdint.h>
#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
@@ -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 <marceloaqno@gmail.org>
* Copyleft (c) 2016, Marcelo Aquino
* Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
*
* Documentation: http://www.mysensors.org
* Support Forum: http://forum.mysensors.org
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*/
#include <iostream>
#include <cstdio>
#include <unistd.h>
// 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 <MySensor.h>
using namespace std;
void setup() {
}
void presentation() {
// Present locally attached sensors here
}
void loop() {
// Send locally attached sensors data here
}