diff --git a/libraries/MySensors/MyConfig.h b/libraries/MySensors/MyConfig.h index 71d87c0..c8323be 100644 --- a/libraries/MySensors/MyConfig.h +++ b/libraries/MySensors/MyConfig.h @@ -22,33 +22,53 @@ #define MyConfig_h #include -// Enable debug flag for debug prints. This will add a lot to the size of the final sketch but good -// to see what is actually is happening when developing +// Enables this in sketch to show debug prints. This option will add a lot to the size of the +// final sketch but is helpful to see what is actually is happening during development //#define MY_DEBUG -// Enable this line, If you are using TX(1), RX(0) as normal I/O pin -// #define MY_DISABLED_SERIAL +// Enable this in sketch if you want to use TX(1), RX(0) as normal I/O pin +//#define MY_DISABLED_SERIAL + +// Turn off debug if serial pins is used for other stuff +#ifdef MY_DISABLED_SERIAL +#undef MY_DEBUG +#endif // Serial output baud rate (for debug prints and serial gateway) #ifndef MY_BAUD_RATE #define MY_BAUD_RATE 115200 #endif +// Node id defaults to AUTO (tries to fetch id from controller) +#ifndef MY_NODE_ID +#define MY_NODE_ID AUTO +#endif + +// Node parent defaults to AUTO (tries to find a parent automatically) +#ifndef MY_PARENT_NODE_ID +#define MY_PARENT_NODE_ID AUTO +#endif + +// Enables repeater functionality (relays messages from other nodes) +// #define MY_REPEATER_FEATURE + /********************************** * Over the air firmware updates ***********************************/ -// The following define enables the safe over-the-air firmware update feature -// which requires external flash and the DualOptiBoot bootloader. +// Enable MY_OTA_FIRMWARE_FEATURE in sketch to allow safe over-the-air firmware updates. +// This feature requires external flash and the DualOptiBoot bootloader. // Note: You can still have OTA FW updates without external flash but it // requires the MYSBootloader and disabled MY_OTA_FIRMWARE_FEATURE //#define MY_OTA_FIRMWARE_FEATURE + // Slave select pin for external flash -#ifdef MY_OTA_FLASH_SS +#ifndef MY_OTA_FLASH_SS #define MY_OTA_FLASH_SS 8 #endif + // Flash jdecid -#ifdef MY_OTA_FLASH_JDECID +#ifndef MY_OTA_FLASH_JDECID #define MY_OTA_FLASH_JDECID 0x1F65 #endif @@ -112,8 +132,10 @@ /********************************** * Message Signing Settings ***********************************/ -// Disable to completely disable signing functionality in library -//#define MY_SIGNING_FEATURE + +// Enable one of these in sketch to use signing functionality +//#define MY_SIGNING_ATSHA204 +//#define MY_SIGNING_SOFT // Define a suitable timeout for a signature verification session // Consider the turn-around from a nonce being generated to a signed message being received @@ -151,18 +173,30 @@ /********************************** * NRF24L01 Driver Defaults ***********************************/ -#define RF24_CE_PIN 9 -#define RF24_CS_PIN 10 -#define RF24_PA_LEVEL RF24_PA_MAX -#define RF24_PA_LEVEL_GW RF24_PA_LOW -// RF channel for the sensor net, 0-127 -#define RF24_CHANNEL 76 -//RF24_250KBPS for 250kbs, RF24_1MBPS for 1Mbps, or RF24_2MBPS for 2Mbps -#define RF24_DATARATE RF24_250KBPS -// This is also act as base value for sensor nodeId addresses. Change this (or channel) if you have more than one sensor network. -#define RF24_BASE_RADIO_ID ((uint64_t)0xA8A8E1FC00LL) +#ifndef MY_RF24_CE_PIN +#define MY_RF24_CE_PIN 9 +#endif +#ifndef MY_RF24_CS_PIN +#define MY_RF24_CS_PIN 10 +#endif -// Enable SOFTSPI for NRF24L01 when using the W5100 Ethernet module +#ifndef MY_RF24_PA_LEVEL +#define MY_RF24_PA_LEVEL RF24_PA_MAX +#endif +// RF channel for the sensor net, 0-127 +#ifndef MY_RF24_CHANNEL +#define MY_RF24_CHANNEL 76 +#endif +//RF24_250KBPS for 250kbs, RF24_1MBPS for 1Mbps, or RF24_2MBPS for 2Mbps +#ifndef MY_RF24_DATARATE +#define MY_RF24_DATARATE RF24_250KBPS +#endif +// This is also act as base value for sensor nodeId addresses. Change this (or channel) if you have more than one sensor network. +#ifndef MY_RF24_BASE_RADIO_ID +#define MY_RF24_BASE_RADIO_ID ((uint64_t)0xA8A8E1FC00LL) +#endif + +// Enable SOFTSPI for NRF24L01, useful for the W5100 Ethernet module //#define MY_SOFTSPI #ifndef MY_SOFT_SPI_SCK_PIN @@ -186,6 +220,11 @@ //#define MY_RFM69_FREQUENCY RF69_915MHZ #endif +// Enable this if you're running the RFM69HW model +#ifndef MY_IS_RFM69HW +#define MY_IS_RFM69HW false +#endif + // Default network id. Use the same for all nodes that will talk to each other #ifndef MY_RFM69_NETWORKID #define MY_RFM69_NETWORKID 100 @@ -210,9 +249,16 @@ * Gateway Transport Ethernet Defaults ***************************************/ // Enable to use DHCP for getting IP address. If not defined, static IP is expected -//#define IP_ADDRESS_DHCP +//#define MY_IP_ADDRESS_VIA_DHCP +#ifndef MY_IP_RENEWAL_INTERVAL +#define MY_IP_RENEWAL_INTERVAL 60000 +#endif + +#ifndef MY_MAC_ADDRESS +#define MY_MAC_ADDRESS 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED +#endif // Enable to use UDP instead of plain Ethernet -#define MY_USE_UDP +//#define MY_USE_UDP #endif diff --git a/libraries/MySensors/MySensor.h b/libraries/MySensors/MySensor.h index 7071221..e9d4e75 100644 --- a/libraries/MySensors/MySensor.h +++ b/libraries/MySensors/MySensor.h @@ -16,50 +16,164 @@ * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. */ - - #ifndef MySensor_h #define MySensor_h +#define VALUE_TO_STRING(x) #x +#define VALUE(x) VALUE_TO_STRING(x) +#define VAR_NAME_VALUE(var) #var "=" VALUE(var) +// #pragma message(VAR_NAME_VALUE(MY_GATEWAY_FEATURE)) + #include "MyConfig.h" -// Include drivers +#if defined(MY_GATEWAY_SERIAL) || defined(MY_GATEWAY_W5100_CLIENT) || defined(MY_GATEWAY_W5100_SERVER) + #define MY_GATEWAY_FEATURE + #define MY_NODE_TYPE "gateway" +#elif defined(MY_REPEATER_FEATURE) + #define MY_NODE_TYPE "repeater" +#else + #define MY_NODE_TYPE "sensor" +#endif + +// CORE #include "drivers/SPI/SPI.cpp" -#include "drivers/RF24/RF24.cpp" -#include "drivers/RFM69/RFM69.cpp" +// HARDWARE +#if defined(ARDUINO_ARCH_ESP8266) + #include "core/MyHwESP8266.cpp" +#elif defined(ARDUINO_ARCH_AVR) + #include "core/MyHwATMega328.cpp" +#endif -#include "drivers/ATSHA204/ATSHA204.cpp" -#include "drivers/ATSHA204/sha256.cpp" +// SIGNING +#if defined(MY_SIGNING_ATSHA204) || defined(MY_SIGNING_SOFT) + #include "MySigning.cpp" + #define MY_SIGNING_FEATURE + #if defined(MY_SIGNING_ATSHA204) + #include "MySigningAtsha204.cpp" + #include "drivers/ATSHA204/ATSHA204.cpp" + MySigningAtsha204 signer; + #elif defined(MY_SIGNING_SOFT) + #include "MySigningAtsha204Soft.cpp" + #include "drivers/ATSHA204/sha256.cpp" + MySigningAtsha204Soft signer; + #endif +#endif -#include "drivers/SPIFlash/SPIFlash.cpp" +// RADIO +#if defined(MY_RADIO_NRF24) || defined(MY_RADIO_RFM69) + #define MY_RADIO_FEATURE + // SOFTSPI + #ifdef MY_SOFTSPI + #include "drivers/DigitalIO/SoftI2cMaster.cpp" + #include "drivers/DigitalIO/PinIO.cpp" + #endif -#include "drivers/Ethernet_W5100/utility/socket.cpp" -#include "drivers/Ethernet_W5100/utility/w5100.cpp" -#include "drivers/Ethernet_W5100/DNS.cpp" -#include "drivers/Ethernet_W5100/Ethernet.cpp" -#include "drivers/Ethernet_W5100/EthernetUdp.cpp" -#include "drivers/Ethernet_W5100/IPAddress.cpp" + // FLASH + #ifdef MY_OTA_FIRMWARE_FEATURE + SPIFlash flash(MY_OTA_FLASH_SS, MY_OTA_FLASH_JDECID); + #include "drivers/SPIFlash/SPIFlash.cpp" + #endif + + #include "core/MyTransport.cpp" + #if defined(MY_RADIO_NRF24) + #include "drivers/RF24/RF24.cpp" + #include "core/MyTransportNRF24.cpp" + MyTransportNRF24 transport; + + #elif defined(MY_RADIO_RFM69) + #include "drivers/RFM69/RFM69.cpp" + #include "core/MyTransportRFM69.cpp" + MyTransportRFM69 transport; + #endif +#endif +// GATEWAY - TRANSPORT +#if defined(MY_GATEWAY_FEATURE) + // GATEWAY - PROTOCOL + #include "core/MyProtocol.cpp" + #if defined(MY_GATEWAY_PROTOCOL_DEFAULT) + #include "core/MyProtocolDefault.cpp" + MyProtocolDefault _protocol; + #else + #error No gateway protocol specified! + #endif + #if defined(MY_RADIO_FEATURE) + // We assume that a gateway having a radio also should act as repeater + #define MY_REPEATER_FEATURE + #endif + #if defined(MY_GATEWAY_W5100_CLIENT) || defined(MY_GATEWAY_W5100_SERVER) + #include "drivers/Ethernet_W5100/utility/socket.cpp" + #include "drivers/Ethernet_W5100/utility/w5100.cpp" + #include "drivers/Ethernet_W5100/DNS.cpp" + #include "drivers/Ethernet_W5100/Ethernet.cpp" + #include "drivers/Ethernet_W5100/EthernetUdp.cpp" + #include "drivers/Ethernet_W5100/IPAddress.cpp" + #if !defined(MY_PORT) + #error You must define MY_PORT + #endif + #if !defined(USE_UDP) + #include "drivers/Ethernet_W5100/EthernetServer.cpp" + #endif + #if defined(MY_GATEWAY_W5100_CLIENT) && !defined(MY_CONTROLLER_IP_ADDRESS) + #error You must define MY_CONTROLLER_IP_ADDRESS when acting as client + #endif + + #if defined(MY_GATEWAY_W5100_CLIENT) + #include "drivers/Ethernet_W5100/EthernetClient.cpp" + #include "core/MyGatewayTransportEthernet.cpp" + #endif + #elif defined(MY_GATEWAY_SERIAL) + #include "core/MyGatewayTransportSerial.cpp" + MyGatewayTransportSerial gwTransport(protocol); + #endif +#else + #undef MY_GATEWAY_FEATURE +#endif + +// Make sure to disable child features when parent feature is disabled +#if !defined(MY_RADIO_FEATURE) + #undef MY_OTA_FIRMWARE_FEATURE + #undef MY_REPEATER_FEATURE + #undef MY_SECURE_NODE_WHITELISTING + #undef MY_SIGNING_FEATURE +#endif + +#if !defined(MY_GATEWAY_FEATURE) + #undef MY_INCLUSION_MODE_FEATURE + #undef MY_INCLUSION_BUTTON_FEATURE +#endif + +// Defines used in code for if-statements +#if defined(MY_REPEATER_FEATURE) + #define MY_IS_REPEATER true +#else + #define MY_IS_REPEATER false +#endif + +#if defined(MY_GATEWAY_FEATURE) + #define MY_IS_GATEWAY true +#else + #define MY_IS_GATEWAY false +#endif -// Include core classes -#include "core/MyHw.cpp" -#include "core/MyHwATMega328.cpp" -#include "core/MyTransport.cpp" -#include "core/MyTransportNRF24.cpp" -//#include "core/MyTransportRFM69.cpp" -#include "core/MyGatewayTransport.cpp" -#include "core/MyGatewayTransportSerial.cpp" #include "core/MyMessage.cpp" #include "core/MySensorCore.cpp" +extern void setup(); +extern void loop(); -// Initialize library -//MyTransportRFM69 transport; -MySensor gw; - - +// Initialize library and call sketch functions +int main() { + begin(); + setup(); + while(1) { + // We handle incoming message processing here + process(); + loop(); + } +} #endif diff --git a/libraries/MySensors/core/MyGatewayTransport.cpp b/libraries/MySensors/core/MyGatewayTransport.cpp deleted file mode 100644 index 2306d5f..0000000 --- a/libraries/MySensors/core/MyGatewayTransport.cpp +++ /dev/null @@ -1,25 +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 Henrik Ekblad - * Copyright (C) 2013-2015 Sensnology AB - * 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" - - -MyGatewayTransport::MyGatewayTransport(MyProtocol &_protocol) : protocol(_protocol) { - -} diff --git a/libraries/MySensors/core/MyGatewayTransport.h b/libraries/MySensors/core/MyGatewayTransport.h deleted file mode 100644 index da03ff5..0000000 --- a/libraries/MySensors/core/MyGatewayTransport.h +++ /dev/null @@ -1,47 +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 Henrik Ekblad - * Copyright (C) 2013-2015 Sensnology AB - * 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 MyGatewayTransport_h -#define MyGatewayTransport_h - -#include "MyMessage.h" -#include "MyProtocol.h" -#include "MyProtocolDefault.h" - -class MyGatewayTransport -{ -public: - // MyProtocol constructor - MyGatewayTransport(MyProtocol &protocol = *new MyProtocolDefault()); - - // initialize the transport driver - virtual bool begin() = 0; - - // Send message to controller - virtual bool send(MyMessage &message) = 0; - - // Check if a new message is available from controller - virtual bool available() = 0; - - // Pick up last message received from controller - virtual MyMessage& receive() = 0; -protected: - MyProtocol& protocol; -}; -#endif diff --git a/libraries/MySensors/core/MyGatewayTransportEthernet.cpp b/libraries/MySensors/core/MyGatewayTransportEthernet.cpp index 7b9083d..b2b21ee 100644 --- a/libraries/MySensors/core/MyGatewayTransportEthernet.cpp +++ b/libraries/MySensors/core/MyGatewayTransportEthernet.cpp @@ -19,39 +19,28 @@ #include "MyGatewayTransportEthernet.h" -MyGatewayTransportEthernet::MyGatewayTransportEthernet( - MyProtocol &protocol, - uint8_t *gw_mac -#ifndef IP_ADDRESS_DHCP - , IPAddress gw_ip -#endif /* not IP_ADDRESS_DHCP */ - , uint16_t gw_port -#ifdef IP_ADDRESS_DHCP - , unsigned long ip_renew_interval -#endif /* IP_ADDRESS_DHCP */ - , IPAddress controller_IP - , uint16_t controller_port) : - MyGatewayTransport(protocol), - gatewayMAC(gw_mac), -#ifndef IP_ADDRESS_DHCP - gatewayIP(gw_ip), -#endif /* not IP_ADDRESS_DHCP */ - gatewayPort(gw_port), -#ifdef IP_ADDRESS_DHCP - ip_renewal_period(ip_renew_interval), -#endif /* IP_ADDRESS_DHCP */ - controllerIP(controller_IP), - controllerPort(controller_port) -{ - ; -} +IPAddress _ethernetControllerIP(MY_CONTROLLER_IP_ADDRESS); +IPAddress _ethernetGatewayIP(MY_IP_ADDRESS); +byte _ethernetGatewayMAC[] = { MY_MAC_ADDRESS }; +uint16_t _ethernetGatewayPort = MY_PORT; +char _ethernetInputBuffer[MAX_RECEIVE_LENGTH]; // A buffer for incoming commands from serial interface +MyMessage _ethernetMsg; -bool MyGatewayTransportEthernet::begin() { -#ifdef IP_ADDRESS_DHCP - // Get IP address from DHCP - Ethernet.begin(gatewayMAC); +#ifdef MY_USE_UDP + EthernetUDP _ethernetServer; #else - Ethernet.begin(gatewayMAC, gatewayIP); + // we have to use pointers due to the constructor of EthernetServer + EthernetServer _ethernetServer(_ethernetGatewayPort); + uint8_t _ethernetInputPos; +#endif /* USE_UDP */ + + +bool gatewayTransportBegin() { +#ifdef MY_IP_ADDRESS + Ethernet.begin(_ethernetGatewayMAC, _ethernetGatewayIP); +#else + // Get IP address from DHCP + Ethernet.begin(_ethernetGatewayMAC); #endif /* IP_ADDRESS_DHCP */ // give the Ethernet interface a second to initialize @@ -59,34 +48,32 @@ bool MyGatewayTransportEthernet::begin() { delay(1000); #ifdef MY_USE_UDP - server = new EthernetUDP(); - server->begin(gatewayPort); + _ethernetServer.begin(_ethernetGatewayPort); #else // we have to use pointers due to the constructor of EthernetServer - server = new EthernetServer(gatewayPort); - server->begin(); + _ethernetServer.begin(); #endif /* USE_UDP */ } -bool MyGatewayTransportEthernet::send(MyMessage &message) +bool gatewayTransportSend(MyMessage &message) { - char *msg = NULL; + char *_ethernetMsg = NULL; - if (controllerIP == INADDR_NONE) { + if (_ethernetControllerIP == INADDR_NONE) { // no controller IP address set! return false; } else { - msg = protocol.format(message); + _ethernetMsg = _protocol.format(message); #ifdef MY_USE_UDP - server->beginPacket(controllerIP, controllerPort); - server->write(msg, strlen(msg)); + _ethernetServer.beginPacket(_ethernetControllerIP, MY_CONTROLLER_PORT); + _ethernetServer.write(_ethernetMsg, strlen(_ethernetMsg)); // returns 1 if the packet was sent successfully - return server->endPacket(); + return _ethernetServer.endPacket(); #else EthernetClient client; - if (client.connect(controllerIP, controllerPort)) { - client.write(msg, strlen(msg)); + if (client.connect(_ethernetControllerIP, MY_CONTROLLER_PORT)) { + client.write(_ethernetMsg, strlen(_ethernetMsg)); client.stop(); return true; } @@ -98,71 +85,64 @@ bool MyGatewayTransportEthernet::send(MyMessage &message) } } -bool MyGatewayTransportEthernet::available() +bool gatewayTransportAvailable() { bool available = false; -#ifdef IP_ADDRESS_DHCP +#ifndef MY_IP_ADDRESS // renew IP address using DHCP - renewIP(); + gatewayTransportRenewIP(); #endif #ifdef MY_USE_UDP - int packet_size = server->parsePacket(); + int packet_size = _ethernetServer.parsePacket(); - if (server->available()) { - server->read(inputBuffer, MAX_RECEIVE_LENGTH); + if (_ethernetServer.available()) { + _ethernetServer.read(_ethernetInputBuffer, MAX_RECEIVE_LENGTH); available = true; } #else // if an incoming client connects, there will be // bytes available to read via the client object - EthernetClient client = server->available(); + EthernetClient client = _ethernetServer.available(); if (client) { while (client.available()) { // read the bytes incoming from the client char inChar = client.read(); - if (inputPos < MAX_RECEIVE_LENGTH - 1) { + if (_ethernetInputPos < MAX_RECEIVE_LENGTH - 1) { // if newline then command is complete if (inChar == '\n') { - inputBuffer[inputPos] = 0; + _ethernetInputBuffer[_ethernetInputPos] = 0; available = true; } else { - inputBuffer[inputPos] = inChar; - inputPos++; + _ethernetInputBuffer[_ethernetInputPos] = inChar; + _ethernetInputPos++; } } } } - inputPos = 0; + _ethernetInputPos = 0; #endif if (available) { // Parse message and return parse result - available = protocol.parse(msg, inputBuffer); + available = _protocol.parse(_ethernetMsg, _ethernetInputBuffer); } return available; } -MyMessage& MyGatewayTransportEthernet::receive() +MyMessage& gatewayTransportReceive() { // Return the last parsed message - return msg; + return _ethernetMsg; } -void MyGatewayTransportEthernet::setControllerIPPort(const IPAddress& addr, const uint16_t port) { - controllerIP = addr; - /* set the port if some legal value passed */ - if (port > 0) - controllerPort = port; -} - -#ifdef IP_ADDRESS_DHCP -void MyGatewayTransportEthernet::renewIP() +#ifndef MY_IP_ADDRES +void gatewayTransportRenewIP() { /* renew/rebind IP address 0 - nothing happened @@ -172,7 +152,7 @@ void MyGatewayTransportEthernet::renewIP() 4 - rebind success */ // TODO: Use millis from MyHw ?? - static unsigned long next_time = millis() + ip_renewal_period; + static unsigned long next_time = millis() + MY_IP_RENEWAL_INTERVAL; unsigned long now = millis(); // http://playground.arduino.cc/Code/TimingRollover @@ -184,6 +164,6 @@ void MyGatewayTransportEthernet::renewIP() return; } - next_time = now + ip_renewal_period; + next_time = now + MY_IP_RENEWAL_INTERVAL; } #endif /* IP_ADDRESS_DHCP */ diff --git a/libraries/MySensors/core/MyGatewayTransportEthernet.h b/libraries/MySensors/core/MyGatewayTransportEthernet.h index 7256b1a..da1abc1 100644 --- a/libraries/MySensors/core/MyGatewayTransportEthernet.h +++ b/libraries/MySensors/core/MyGatewayTransportEthernet.h @@ -23,7 +23,6 @@ #include "MyConfig.h" #include "MyMessage.h" #include "MyProtocol.h" -#include "MyGatewayTransport.h" #include "MyProtocolDefault.h" #include "drivers/Ethernet_W5100/IPAddress.h" @@ -43,58 +42,20 @@ #define MAX_RECEIVE_LENGTH 100 // Maximum message length for messages coming from controller -class MyGatewayTransportEthernet : public MyGatewayTransport -{ - public: - MyGatewayTransportEthernet(MyProtocol &protocol, - uint8_t *gw_mac -#ifndef IP_ADDRESS_DHCP - , IPAddress gw_ip -#endif /* not IP_ADDRESS_DHCP */ - , uint16_t gw_port -#ifdef IP_ADDRESS_DHCP - , unsigned long ip_renew_interval -#endif /* IP_ADDRESS_DHCP */ - , IPAddress controller_IP - , uint16_t controller_port - ); +// initialize the driver +bool gatewayTransportBegin(); - // initialize the driver - bool begin(); +// Send message to controller +bool gatewayTransportSend(MyMessage &message); - // Send message to controller - bool send(MyMessage &message); +// Check if a new message is available from controller +bool gatewayTransportAvailable(); - // Check if a new message is available from controller - bool available(); +// Pick up last message received from controller +MyMessage& gatewayTransportReceive(); - // Pick up last message received from controller - MyMessage& receive(); - - // Set the controller IP and Port - void setControllerIPPort(const IPAddress& addr, const uint16_t port); - -protected: - IPAddress controllerIP; - uint16_t controllerPort; - -#ifdef IP_ADDRESS_DHCP - void renewIP(); - unsigned long ip_renewal_period; -#endif /* IP_ADDRESS_DHCP */ - uint8_t *gatewayMAC; - IPAddress gatewayIP; - uint16_t gatewayPort; - -#ifdef MY_USE_UDP - EthernetUDP *server; -#else - EthernetServer *server; - uint8_t inputPos; +#ifndef MY_IP_ADDRESS + void gatewayTransportRenewIP(); #endif - char inputBuffer[MAX_RECEIVE_LENGTH]; // A buffer for incoming commands from serial interface - MyMessage msg; -}; - #endif /* MyGatewayTransportEthernet_h */ diff --git a/libraries/MySensors/core/MyGatewayTransportSerial.h b/libraries/MySensors/core/MyGatewayTransportSerial.h index 44434cf..302a06c 100644 --- a/libraries/MySensors/core/MyGatewayTransportSerial.h +++ b/libraries/MySensors/core/MyGatewayTransportSerial.h @@ -22,7 +22,6 @@ #include "MyMessage.h" #include "MyProtocol.h" -#include "MyGatewayTransport.h" #include "MyProtocolDefault.h" #define MAX_RECEIVE_LENGTH 100 // Maximum message length for messages coming from controller diff --git a/libraries/MySensors/core/MyHw.cpp b/libraries/MySensors/core/MyHw.cpp deleted file mode 100644 index 3016a6b..0000000 --- a/libraries/MySensors/core/MyHw.cpp +++ /dev/null @@ -1,23 +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 Henrik Ekblad - * Copyright (C) 2013-2015 Sensnology AB - * 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 "MyHw.h" - -MyHw::MyHw() { -} diff --git a/libraries/MySensors/core/MyHw.h b/libraries/MySensors/core/MyHw.h deleted file mode 100644 index 506648b..0000000 --- a/libraries/MySensors/core/MyHw.h +++ /dev/null @@ -1,54 +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 Henrik Ekblad - * Copyright (C) 2013-2015 Sensnology AB - * 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 MyHw_h -#define MyHw_h - -#include -#include -#include "MyConfig.h" - -/** - * The Hw contains all hardware specific details of the platform - */ - -class MyHw -{ -public: - // MyHw constructor - MyHw(); -/* // The following is defined as macros to save space - virtual void init() = 0; - virtual void watchdogReset() = 0; - virtual void reboot() = 0; - virtual unsigned long millisec() = 0; - virtual uint8_t readConfig(uint8_t pos) = 0; - virtual void writeConfig(uint8_t pos, uint8_t value) = 0; - virtual void readConfigBlock(void* buf, void * pos, size_t length) = 0; - virtual void writeConfigBlock(void* pos, void* buf, uint16_t length) = 0; */ - virtual void sleep(unsigned long ms) = 0; - virtual bool sleep(uint8_t interrupt, uint8_t mode, unsigned long ms) = 0; - virtual uint8_t sleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2, unsigned long ms) = 0; - #ifdef MY_DEBUG - virtual void debugPrint(bool isGW, const char *fmt, ... ) = 0; - #endif - -}; - -#endif diff --git a/libraries/MySensors/core/MyHwATMega328.cpp b/libraries/MySensors/core/MyHwATMega328.cpp index b20d337..38e16fe 100644 --- a/libraries/MySensors/core/MyHwATMega328.cpp +++ b/libraries/MySensors/core/MyHwATMega328.cpp @@ -19,7 +19,6 @@ #ifdef ARDUINO_ARCH_AVR -#include "MyHw.h" #include "MyHwATMega328.h" int8_t pinIntTrigger = 0; @@ -40,51 +39,10 @@ ISR (WDT_vect) wdt_disable(); } -MyHwATMega328::MyHwATMega328() : MyHw() -{ -} -/* -// The following was redifined as macros to save space - -inline uint8_t MyHwATMega328::readConfig(uint8_t pos) { - return eeprom_read_byte((uint8_t*)pos); -} - -inline void MyHwATMega328::writeConfig(uint8_t pos, uint8_t value) { - eeprom_update_byte((uint8_t*)pos, value); -} - -inline void MyHwATMega328::readConfigBlock(void* buf, void * pos, size_t length) { - eeprom_read_block(buf, (void*)pos, length); -} - -inline void MyHwATMega328::writeConfigBlock(void* pos, void* buf, size_t length) { - eeprom_write_block((void*)pos, (void*)buf, length); -} - - -inline void MyHwATMega328::init() { - Serial.begin(BAUD_RATE); -} - -inline void MyHwATMega328::watchdogReset() { - wdt_reset(); -} - -inline void MyHwATMega328::reboot() { - wdt_enable(WDTO_15MS); for (;;); -} - -inline unsigned long MyHwATMega328::millis() { - return ::millis(); -} -*/ - - -void powerDown(period_t period) { +void hwPowerDown(period_t period) { ADCSRA &= ~(1 << ADEN); @@ -119,35 +77,35 @@ void powerDown(period_t period) { ADCSRA |= (1 << ADEN); } -void MyHwATMega328::internalSleep(unsigned long ms) { +void hwInternalSleep(unsigned long ms) { // Let serial prints finish (debug, log etc) #ifndef MY_DISABLED_SERIAL Serial.flush(); #endif pinIntTrigger = 0; - while (!pinIntTrigger && ms >= 8000) { powerDown(SLEEP_8S); ms -= 8000; } - if (!pinIntTrigger && ms >= 4000) { powerDown(SLEEP_4S); ms -= 4000; } - if (!pinIntTrigger && ms >= 2000) { powerDown(SLEEP_2S); ms -= 2000; } - if (!pinIntTrigger && ms >= 1000) { powerDown(SLEEP_1S); ms -= 1000; } - if (!pinIntTrigger && ms >= 500) { powerDown(SLEEP_500MS); ms -= 500; } - if (!pinIntTrigger && ms >= 250) { powerDown(SLEEP_250MS); ms -= 250; } - if (!pinIntTrigger && ms >= 125) { powerDown(SLEEP_120MS); ms -= 120; } - if (!pinIntTrigger && ms >= 64) { powerDown(SLEEP_60MS); ms -= 60; } - if (!pinIntTrigger && ms >= 32) { powerDown(SLEEP_30MS); ms -= 30; } - if (!pinIntTrigger && ms >= 16) { powerDown(SLEEP_15Ms); ms -= 15; } + while (!pinIntTrigger && ms >= 8000) { hwPowerDown(SLEEP_8S); ms -= 8000; } + if (!pinIntTrigger && ms >= 4000) { hwPowerDown(SLEEP_4S); ms -= 4000; } + if (!pinIntTrigger && ms >= 2000) { hwPowerDown(SLEEP_2S); ms -= 2000; } + if (!pinIntTrigger && ms >= 1000) { hwPowerDown(SLEEP_1S); ms -= 1000; } + if (!pinIntTrigger && ms >= 500) { hwPowerDown(SLEEP_500MS); ms -= 500; } + if (!pinIntTrigger && ms >= 250) { hwPowerDown(SLEEP_250MS); ms -= 250; } + if (!pinIntTrigger && ms >= 125) { hwPowerDown(SLEEP_120MS); ms -= 120; } + if (!pinIntTrigger && ms >= 64) { hwPowerDown(SLEEP_60MS); ms -= 60; } + if (!pinIntTrigger && ms >= 32) { hwPowerDown(SLEEP_30MS); ms -= 30; } + if (!pinIntTrigger && ms >= 16) { hwPowerDown(SLEEP_15Ms); ms -= 15; } } -void MyHwATMega328::sleep(unsigned long ms) { - internalSleep(ms); +void hwSleep(unsigned long ms) { + hwInternalSleep(ms); } -bool MyHwATMega328::sleep(uint8_t interrupt, uint8_t mode, unsigned long ms) { +bool hwSleep(uint8_t interrupt, uint8_t mode, unsigned long ms) { // Let serial prints finish (debug, log etc) bool pinTriggeredWakeup = true; attachInterrupt(interrupt, wakeUp, mode); if (ms>0) { pinIntTrigger = 0; - internalSleep(ms); + hwInternalSleep(ms); if (0 == pinIntTrigger) { pinTriggeredWakeup = false; } @@ -155,19 +113,19 @@ bool MyHwATMega328::sleep(uint8_t interrupt, uint8_t mode, unsigned long ms) { #ifndef MY_DISABLED_SERIAL Serial.flush(); #endif - powerDown(SLEEP_FOREVER); + hwPowerDown(SLEEP_FOREVER); } detachInterrupt(interrupt); return pinTriggeredWakeup; } -inline uint8_t MyHwATMega328::sleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2, unsigned long ms) { +uint8_t hwSleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2, unsigned long ms) { int8_t retVal = 1; attachInterrupt(interrupt1, wakeUp, mode1); attachInterrupt(interrupt2, wakeUp2, mode2); if (ms>0) { pinIntTrigger = 0; - internalSleep(ms); + hwInternalSleep(ms); if (0 == pinIntTrigger) { retVal = -1; } @@ -175,7 +133,7 @@ inline uint8_t MyHwATMega328::sleep(uint8_t interrupt1, uint8_t mode1, uint8_t i #ifndef MY_DISABLED_SERIAL Serial.flush(); #endif - powerDown(SLEEP_FOREVER); + hwPowerDown(SLEEP_FOREVER); } detachInterrupt(interrupt1); detachInterrupt(interrupt2); @@ -191,24 +149,24 @@ inline uint8_t MyHwATMega328::sleep(uint8_t interrupt1, uint8_t mode1, uint8_t i #ifdef MY_DEBUG -void MyHwATMega328::debugPrint(bool isGW, const char *fmt, ... ) { +void hwDebugPrint(const char *fmt, ... ) { char fmtBuffer[300]; - if (isGW) { + #ifdef MY_GATEWAY_FEATURE // prepend debug message to be handled correctly by controller (C_INTERNAL, I_LOG_MESSAGE) snprintf_P(fmtBuffer, 299, PSTR("0;0;%d;0;%d;"), C_INTERNAL, I_LOG_MESSAGE); Serial.print(fmtBuffer); - } + #endif va_list args; va_start (args, fmt ); va_end (args); - if (isGW) { + #ifdef MY_GATEWAY_FEATURE // Truncate message if this is gateway node vsnprintf_P(fmtBuffer, 60, fmt, args); fmtBuffer[59] = '\n'; fmtBuffer[60] = '\0'; - } else { + #else vsnprintf_P(fmtBuffer, 299, fmt, args); - } + #endif va_end (args); Serial.print(fmtBuffer); Serial.flush(); diff --git a/libraries/MySensors/core/MyHwATMega328.h b/libraries/MySensors/core/MyHwATMega328.h index 3468db6..90ea09b 100644 --- a/libraries/MySensors/core/MyHwATMega328.h +++ b/libraries/MySensors/core/MyHwATMega328.h @@ -22,7 +22,6 @@ #ifdef ARDUINO_ARCH_AVR -#include "MyHw.h" #include "MyConfig.h" #include "MyMessage.h" #include @@ -42,7 +41,7 @@ #ifndef sleep_bod_disable #define sleep_bod_disable() \ do { \ - unsigned char tempreg; \ + unsigned char tempreg; \ __asm__ __volatile__("in %[tempreg], %[mcucr]" "\n\t" \ "ori %[tempreg], %[bods_bodse]" "\n\t" \ "out %[mcucr], %[tempreg]" "\n\t" \ @@ -59,22 +58,22 @@ do { \ // Define these as macros to save valuable space -#define hw_digitalWrite(__pin, __value) (digitalWrite(__pin, __value)) -#define hw_init() Serial.begin(MY_BAUD_RATE) -#define hw_watchdogReset() wdt_reset() -#define hw_reboot() wdt_enable(WDTO_15MS); while (1) -#define hw_millis() millis() -#define hw_readConfig(__pos) (eeprom_read_byte((uint8_t*)(__pos))) +#define hwDigitalWrite(__pin, __value) (digitalWrite(__pin, __value)) +#define hwInit() Serial.begin(MY_BAUD_RATE) +#define hwWatchdogReset() wdt_reset() +#define hwReboot() wdt_enable(WDTO_15MS); while (1) +#define hwMillis() millis() +#define hwReadConfig(__pos) (eeprom_read_byte((uint8_t*)(__pos))) #ifndef eeprom_update_byte - #define hw_writeConfig(loc, val) if((uint8_t)(val) != eeprom_read_byte((uint8_t*)(loc))) { eeprom_write_byte((uint8_t*)(loc), (val)); } + #define hwWriteConfig(loc, val) if((uint8_t)(val) != eeprom_read_byte((uint8_t*)(loc))) { eeprom_write_byte((uint8_t*)(loc), (val)); } #else - #define hw_writeConfig(__pos, __value) (eeprom_update_byte((uint8_t*)(__pos), (__value))) + #define hwWriteConfig(__pos, __value) (eeprom_update_byte((uint8_t*)(__pos), (__value))) #endif // -#define hw_readConfigBlock(__buf, __pos, __length) (eeprom_read_block((__buf), (void*)(__pos), (__length))) -#define hw_writeConfigBlock(__pos, __buf, __length) (eeprom_write_block((void*)(__pos), (void*)__buf, (__length))) +#define hwReadConfigBlock(__buf, __pos, __length) (eeprom_read_block((__buf), (void*)(__pos), (__length))) +#define hwWriteConfigBlock(__pos, __buf, __length) (eeprom_write_block((void*)(__pos), (void*)__buf, (__length))) @@ -93,28 +92,14 @@ enum period_t SLEEP_FOREVER }; -class MyHwATMega328 : public MyHw -{ -public: - MyHwATMega328(); -/* void init(); - void watchdogReset(); - void reboot(); - unsigned long millis(); - uint8_t readConfig(uint8_t pos); - void writeConfig(uint8_t pos, uint8_t value); - void readConfigBlock(void* buf, void * pos, size_t length); - void writeConfigBlock(void* pos, void* buf, size_t length); */ - - void sleep(unsigned long ms); - bool sleep(uint8_t interrupt, uint8_t mode, unsigned long ms); - uint8_t sleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2, unsigned long ms); +void hwSleep(unsigned long ms); +bool hwSleep(uint8_t interrupt, uint8_t mode, unsigned long ms); +uint8_t hwSleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2, unsigned long ms); #ifdef MY_DEBUG - void debugPrint(bool isGW, const char *fmt, ... ); -#endif -private: - void internalSleep(unsigned long ms); -}; + void hwDebugPrint(const char *fmt, ... ); #endif +void hwInternalSleep(unsigned long ms); + #endif // #ifdef ARDUINO_ARCH_AVR +#endif diff --git a/libraries/MySensors/core/MyHwESP8266.cpp b/libraries/MySensors/core/MyHwESP8266.cpp index fa36ebf..5a0eb64 100644 --- a/libraries/MySensors/core/MyHwESP8266.cpp +++ b/libraries/MySensors/core/MyHwESP8266.cpp @@ -19,7 +19,6 @@ #ifdef ARDUINO_ARCH_ESP8266 -#include "MyHw.h" #include "MyHwESP8266.h" #include @@ -43,7 +42,7 @@ ISR (WDT_vect) } */ -static void hw_initConfigBlock( size_t length = 1024 /*ATMega328 has 1024 bytes*/ ) +static void hwInitConfigBlock( size_t length = 1024 /*ATMega328 has 1024 bytes*/ ) { static bool initDone = false; if (!initDone) @@ -53,7 +52,7 @@ static void hw_initConfigBlock( size_t length = 1024 /*ATMega328 has 1024 bytes* } } -void hw_readConfigBlock(void* buf, void* adr, size_t length) +void hwReadConfigBlock(void* buf, void* adr, size_t length) { hw_initConfigBlock(); uint8_t* dst = static_cast(buf); @@ -64,7 +63,7 @@ void hw_readConfigBlock(void* buf, void* adr, size_t length) } } -void hw_writeConfigBlock(void* buf, void* adr, size_t length) +void hwWriteConfigBlock(void* buf, void* adr, size_t length) { hw_initConfigBlock(); uint8_t* src = static_cast(buf); @@ -76,14 +75,14 @@ void hw_writeConfigBlock(void* buf, void* adr, size_t length) EEPROM.commit(); } -uint8_t hw_readConfig(int adr) +uint8_t hwReadConfig(int adr) { uint8_t value; hw_readConfigBlock(&value, reinterpret_cast(adr), 1); return value; } -void hw_writeConfig(int adr, uint8_t value) +void hwWriteConfig(int adr, uint8_t value) { uint8_t curr = hw_readConfig(adr); if (curr != value) @@ -94,85 +93,40 @@ void hw_writeConfig(int adr, uint8_t value) -MyHwESP8266::MyHwESP8266() : MyHw() -{ -} - -/* - -// The following was redifined as macros to save space - -inline uint8_t MyHwATMega328::readConfig(uint8_t pos) { - return eeprom_read_byte((uint8_t*)pos); -} - -inline void MyHwATMega328::writeConfig(uint8_t pos, uint8_t value) { - eeprom_update_byte((uint8_t*)pos, value); -} - -inline void MyHwATMega328::readConfigBlock(void* buf, void * pos, size_t length) { - eeprom_read_block(buf, (void*)pos, length); -} - -inline void MyHwATMega328::writeConfigBlock(void* pos, void* buf, size_t length) { - eeprom_write_block((void*)pos, (void*)buf, length); -} - - -inline void MyHwATMega328::init() { - Serial.begin(BAUD_RATE); -} - -inline void MyHwATMega328::watchdogReset() { - wdt_reset(); -} - -inline void MyHwATMega328::reboot() { - wdt_enable(WDTO_15MS); for (;;); -} - -inline unsigned long MyHwATMega328::millis() { - return ::millis(); -} -*/ - - -void MyHwESP8266::sleep(unsigned long ms) { +void hwSleep(unsigned long ms) { // TODO: Not supported! } -bool MyHwESP8266::sleep(uint8_t interrupt, uint8_t mode, unsigned long ms) { +bool hwSleep(uint8_t interrupt, uint8_t mode, unsigned long ms) { // TODO: Not supported! return false; } -inline uint8_t MyHwESP8266::sleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2, unsigned long ms) { +uint8_t hwSleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2, unsigned long ms) { // TODO: Not supported! return 0; } - - #ifdef MY_DEBUG -void MyHwESP8266::debugPrint(bool isGW, const char *fmt, ... ) { +void hwDebugPrint(bool isGW, const char *fmt, ... ) { char fmtBuffer[300]; - if (isGW) { + #ifdef MY_GATEWAY_FEATURE // prepend debug message to be handled correctly by controller (C_INTERNAL, I_LOG_MESSAGE) snprintf_P(fmtBuffer, 299, PSTR("0;0;%d;0;%d;"), C_INTERNAL, I_LOG_MESSAGE); Serial.print(fmtBuffer); - } + #endif va_list args; va_start (args, fmt ); va_end (args); - if (isGW) { + #ifdef MY_GATEWAY_FEATURE // Truncate message if this is gateway node vsnprintf_P(fmtBuffer, 60, fmt, args); fmtBuffer[59] = '\n'; fmtBuffer[60] = '\0'; - } else { + #else vsnprintf_P(fmtBuffer, 299, fmt, args); - } + #endif va_end (args); Serial.print(fmtBuffer); Serial.flush(); diff --git a/libraries/MySensors/core/MyHwESP8266.h b/libraries/MySensors/core/MyHwESP8266.h index 03f47c3..6f99201 100644 --- a/libraries/MySensors/core/MyHwESP8266.h +++ b/libraries/MySensors/core/MyHwESP8266.h @@ -22,7 +22,6 @@ #ifndef MyHwESP8266_h #define MyHwESP8266_h -#include "MyHw.h" #include "MyConfig.h" #include "MyMessage.h" @@ -34,16 +33,16 @@ // Define these as macros to save valuable space -#define hw_digitalWrite(__pin, __value) (digitalWrite(__pin, __value)) -#define hw_init() Serial.begin(BAUD_RATE) -#define hw_watchdogReset() wdt_reset() -#define hw_reboot() wdt_enable(WDTO_15MS); while (1) -#define hw_millis() millis() +#define hwDigitalWrite(__pin, __value) (digitalWrite(__pin, __value)) +#define hwInit() Serial.begin(BAUD_RATE) +#define hwWatchdogReset() wdt_reset() +#define hwReboot() wdt_enable(WDTO_15MS); while (1) +#define hwMillis() millis() -void hw_readConfigBlock(void* buf, void* adr, size_t length); -void hw_writeConfigBlock(void* buf, void* adr, size_t length); -void hw_writeConfig(int adr, uint8_t value); -uint8_t hw_readConfig(int adr); +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); enum period_t { @@ -60,27 +59,13 @@ enum period_t SLEEP_FOREVER }; -class MyHwESP8266 : public MyHw -{ -public: - MyHwESP8266(); -/* void init(); - void watchdogReset(); - void reboot(); - unsigned long millis(); - uint8_t readConfig(uint8_t pos); - void writeConfig(uint8_t pos, uint8_t value); - void readConfigBlock(void* buf, void * pos, size_t length); - void writeConfigBlock(void* pos, void* buf, size_t length); */ - void sleep(unsigned long ms); - bool sleep(uint8_t interrupt, uint8_t mode, unsigned long ms); - uint8_t sleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2, unsigned long ms); +void hwSleep(unsigned long ms); +bool hwSleep(uint8_t interrupt, uint8_t mode, unsigned long ms); +uint8_t hwSleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2, unsigned long ms); #ifdef MY_DEBUG - void debugPrint(bool isGW, const char *fmt, ... ); -#endif -}; + void hwDebugPrint(const char *fmt, ... ); #endif #endif // #ifdef ARDUINO_ARCH_ESP8266 diff --git a/libraries/MySensors/core/MySensorCore.cpp b/libraries/MySensors/core/MySensorCore.cpp index 32b4e8e..05121cf 100644 --- a/libraries/MySensors/core/MySensorCore.cpp +++ b/libraries/MySensors/core/MySensorCore.cpp @@ -21,17 +21,58 @@ #include "MySensorCore.h" + +NodeConfig _nc; // Essential settings for node to work +ControllerConfig _cc; // Configuration coming from controller +#ifdef MY_OTA_FIRMWARE_FEATURE +NodeFirmwareConfig _fc; +bool _fwUpdateOngoing; +unsigned long _fwLastRequestTime; +uint16_t _fwBlock; +uint8_t _fwRetry; +#endif + +#ifdef MY_RADIO_FEATURE +bool _autoFindParent; +uint8_t _failedTransmissions; +#endif + +MyMessage _msg; // Buffer for incoming messages. +MyMessage _msgTmp ; // Buffer for temporary messages (acks and nonces among others). + +#ifdef MY_LEDS_BLINKING_FEATURE +// these variables don't need to be volatile, since we are not using interrupts +uint8_t _countRx; +uint8_t _countTx; +uint8_t _countErr; +unsigned long _blink_next_time; +#endif + +#ifdef MY_SIGNING_FEATURE +uint16_t _doSign[16]; // Bitfield indicating which sensors require signed communication +MyMessage _msgSign; // Buffer for message to sign. +#endif + +#ifdef MY_DEBUG + char convBuf[MAX_PAYLOAD*2+1]; +#endif + +uint16_t _heartbeat; +void (*_timeCallback)(unsigned long); // Callback for requested time messages +void (*_msgCallback)(const MyMessage &); // Callback for incoming messages from other nodes and gateway. + + #define DISTANCE_INVALID (0xFF) #ifdef MY_SIGNING_FEATURE // Macros for manipulating signing requirement table -#define DO_SIGN(node) (node == 0 ? (~doSign[0]&1) : (~doSign[node>>4]&(node%16))) -#define SET_SIGN(node) (node == 0 ? (doSign[0]&=~1) : (doSign[node>>4]&=~(node%16))) -#define CLEAR_SIGN(node) (node == 0 ? (doSign[0]|=1) : (doSign[node>>4]|=(node%16))) +#define DO_SIGN(node) (node == 0 ? (~_doSign[0]&1) : (~_doSign[node>>4]&(node%16))) +#define SET_SIGN(node) (node == 0 ? (_doSign[0]&=~1) : (_doSign[node>>4]&=~(node%16))) +#define CLEAR_SIGN(node) (node == 0 ? (_doSign[0]|=1) : (_doSign[node>>4]|=(node%16))) #endif // Inline function and macros -static inline MyMessage& build (MyMessage &msg, uint8_t sender, uint8_t destination, uint8_t sensor, uint8_t command, uint8_t type, bool enableAck) { +static inline MyMessage& build(MyMessage &msg, uint8_t sender, uint8_t destination, uint8_t sensor, uint8_t command, uint8_t type, bool enableAck) { msg.sender = sender; msg.destination = destination; msg.sensor = sensor; @@ -42,858 +83,6 @@ static inline MyMessage& build (MyMessage &msg, uint8_t sender, uint8_t destinat return msg; } -static inline bool isValidParent( const uint8_t parent ) { - return parent != AUTO; -} -static inline bool isValidDistance( const uint8_t distance ) { - return distance != DISTANCE_INVALID; -} - - -MySensor::MySensor(MyTransport &_radio, MyHw &_hw -#ifdef MY_SIGNING_FEATURE - , MySigning &_signer -#endif - ) - : - radio(_radio), -#ifdef MY_SIGNING_FEATURE - signer(_signer), -#endif -#ifdef MY_OTA_FIRMWARE_FEATURE - flash(MY_OTA_FLASH_SS, MY_OTA_FLASH_JDECID), -#endif - hw(_hw) -{ -} - - -#ifdef MY_OTA_FIRMWARE_FEATURE - -// do a crc16 on the whole received firmware -bool MySensor::isValidFirmware() { - // init crc - uint16_t crc = ~0; - for (uint16_t i = 0; i < fc.blocks * FIRMWARE_BLOCK_SIZE; ++i) { - crc ^= flash.readByte(i + FIRMWARE_START_OFFSET); - for (int8_t j = 0; j < 8; ++j) { - if (crc & 1) - crc = (crc >> 1) ^ 0xA001; - else - crc = (crc >> 1); - } - } - return crc == fc.crc; -} - -#endif - -#ifdef MY_LEDS_BLINKING_FEATURE -void MySensor::handleLedsBlinking() { - static unsigned long next_time = hw_millis() + MY_DEFAULT_LED_BLINK_PERIOD; - - // Just return if it is not the time... - // http://playground.arduino.cc/Code/TimingRollover - if ((long)(hw_millis() - blink_next_time) < 0) - return; - else - blink_next_time = hw_millis() + ledBlinkPeriod; - - // do the actual blinking - if(countRx && countRx != 255) { - // switch led on - hw_digitalWrite(MY_DEFAULT_RX_LED_PIN, LED_ON); - } - else if(!countRx) { - // switching off - hw_digitalWrite(MY_DEFAULT_RX_LED_PIN, LED_OFF); - } - if(countRx != 255) - --countRx; - - if(countTx && countTx != 255) { - // switch led on - hw_digitalWrite(MY_DEFAULT_TX_LED_PIN, LED_ON); - } - else if(!countTx) { - // switching off - hw_digitalWrite(MY_DEFAULT_TX_LED_PIN, LED_OFF); - } - if(countTx != 255) - --countTx; - - if(countErr && countErr != 255) { - // switch led on - hw_digitalWrite(MY_DEFAULT_ERR_LED_PIN, LED_ON); - } - else if(!countErr) { - // switching off - hw_digitalWrite(MY_DEFAULT_ERR_LED_PIN, LED_OFF); - } - if(countErr != 255) - --countErr; -} - -void MySensor::rxBlink(uint8_t cnt) { - if(countRx == 255) { countRx = cnt; } -} - -void MySensor::txBlink(uint8_t cnt) { - if(countTx == 255) { countTx = cnt; } -} - -void MySensor::errBlink(uint8_t cnt) { - if(countErr == 255) { countErr = cnt; } -} -#endif - -void MySensor::begin(void (*_msgCallback)(const MyMessage &), uint8_t _nodeId, boolean _repeaterMode, uint8_t _parentNodeId) { - #ifndef MY_DISABLED_SERIAL - hw_init(); - #endif - repeaterMode = _repeaterMode; - msgCallback = _msgCallback; - failedTransmissions = 0; - - // Only gateway should use node id 0! - isGateway = _nodeId == GATEWAY_ADDRESS; - - // Setup radio - if (!radio.init()) { - debug(PSTR("radio init fail\n")); - while(1); // Nothing more we can do - } - -#ifdef MY_SIGNING_FEATURE - // Read out the signing requirements from EEPROM - hw_readConfigBlock((void*)doSign, (void*)EEPROM_SIGNING_REQUIREMENT_TABLE_ADDRESS, sizeof(doSign)); -#endif - -#ifdef MY_LEDS_BLINKING_FEATURE - // Setup led pins - pinMode(MY_DEFAULT_RX_LED_PIN, OUTPUT); - pinMode(MY_DEFAULT_TX_LED_PIN, OUTPUT); - pinMode(MY_DEFAULT_ERR_LED_PIN, OUTPUT); - - // Set initial state of leds - hw_digitalWrite(MY_DEFAULT_RX_LED_PIN, LED_OFF); - hw_digitalWrite(MY_DEFAULT_TX_LED_PIN, LED_OFF); - hw_digitalWrite(MY_DEFAULT_ERR_LED_PIN, LED_OFF); - - // initialize counters - countRx = 0; - countTx = 0; - countErr = 0; -#endif - - if (isGateway) { - // Set configuration for gateway - nc.parentNodeId = GATEWAY_ADDRESS; - nc.distance = 0; - nc.nodeId = GATEWAY_ADDRESS; - } else { - // Read settings from eeprom - hw_readConfigBlock((void*)&nc, (void*)EEPROM_NODE_ID_ADDRESS, sizeof(NodeConfig)); - // Read latest received controller configuration from EEPROM - hw_readConfigBlock((void*)&cc, (void*)EEPROM_CONTROLLER_CONFIG_ADDRESS, sizeof(ControllerConfig)); -#ifdef MY_OTA_FIRMWARE_FEATURE - // Read firmware config from EEPROM, i.e. type, version, CRC, blocks - hw_readConfigBlock((void*)&fc, (void*)EEPROM_FIRMWARE_TYPE_ADDRESS, sizeof(NodeFirmwareConfig)); -#endif - - if (cc.isMetric == 0xff) { - // Eeprom empty, set default to metric - cc.isMetric = 0x01; - } - - autoFindParent = _parentNodeId == AUTO; - if (!autoFindParent) { - nc.parentNodeId = _parentNodeId; - // Save static parent id in eeprom (used by bootloader) - hw_writeConfig(EEPROM_PARENT_NODE_ID_ADDRESS, _parentNodeId); - // We don't actually know the distance to gw here. Let's pretend it is 1. - // If the current node is also repeater, be aware of this. - nc.distance = 1; - } else if (!isValidParent(nc.parentNodeId)) { - // Auto find parent, but parent in eeprom is invalid. Try find one. - findParentNode(); - } - - if (_nodeId != AUTO) { - // Set static id - nc.nodeId = _nodeId; - // Save static id in eeprom - hw_writeConfig(EEPROM_NODE_ID_ADDRESS, _nodeId); - } else if (nc.nodeId == AUTO && isValidParent(nc.parentNodeId)) { - // Try to fetch node-id from gateway - requestNodeId(); - } - } - - setupNode(); - debug(PSTR("%s started, id=%d, parent=%d, distance=%d\n"), isGateway?"gateway":(repeaterMode?"repeater":"sensor"), nc.nodeId, nc.parentNodeId, nc.distance); -} - - -uint8_t MySensor::getNodeId() { - return nc.nodeId; -} - -ControllerConfig MySensor::getConfig() { - return cc; -} - -void MySensor::requestNodeId() { - debug(PSTR("req id\n")); - radio.setAddress(nc.nodeId); - build(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_ID_REQUEST, false).set(""); - sendWrite(nc.parentNodeId, msg); - wait(2000); -} - -void MySensor::setupNode() { - // Open reading pipe for messages directed to this node (set write pipe to same) - radio.setAddress(nc.nodeId); - - // Present node and request config - if (!isGateway && nc.nodeId != AUTO) { -#ifdef MY_SIGNING_FEATURE - // Notify gateway (and possibly controller) about the signing preferences of this node - sendRoute(build(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_REQUEST_SIGNING, false).set(signer.requestSignatures())); - - // If we do require signing, wait for the gateway to tell us how it prefer us to transmit our messages - if (signer.requestSignatures()) { - wait(2000); - } -#endif - - // Send presentation for this radio node (attach - present(NODE_SENSOR_ID, repeaterMode? S_ARDUINO_REPEATER_NODE : S_ARDUINO_NODE); - - // Send a configuration exchange request to controller - // Node sends parent node. Controller answers with latest node configuration - // which is picked up in process() - sendRoute(build(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_CONFIG, false).set(nc.parentNodeId)); - - // Wait configuration reply. - wait(2000); - -#ifdef MY_OTA_FIRMWARE_FEATURE - RequestFirmwareConfig *reqFWConfig = (RequestFirmwareConfig *)msg.data; - mSetLength(msg, sizeof(RequestFirmwareConfig)); - mSetCommand(msg, C_STREAM); - mSetPayloadType(msg,P_CUSTOM); - // copy node settings to reqFWConfig - memcpy(reqFWConfig,&fc,sizeof(NodeFirmwareConfig)); - // add bootloader information - reqFWConfig->BLVersion = MY_OTA_BOOTLOADER_VERSION; - fwUpdateOngoing = false; - sendRoute(build(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_STREAM, ST_FIRMWARE_CONFIG_REQUEST, false)); -#endif - - - } -} - -void MySensor::findParentNode() { - static boolean findingParentNode = false; - - if (findingParentNode) - return; - findingParentNode = true; - - failedTransmissions = 0; - - // Set distance to max - nc.distance = 255; - - // Send ping message to BROADCAST_ADDRESS (to which all relaying nodes and gateway listens and should reply to) - debug(PSTR("find parent\n")); - - build(msg, nc.nodeId, BROADCAST_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_FIND_PARENT, false).set(""); - // Write msg, but suppress recursive parent search - sendWrite(BROADCAST_ADDRESS, msg); - - // Wait for ping response. - wait(2000); - findingParentNode = false; -} - -boolean MySensor::sendRoute(MyMessage &message) { - uint8_t sender = message.sender; - uint8_t dest = message.destination; - uint8_t last = message.last; - bool ok; - - // If we still don't have any parent id, re-request and skip this message. - if (nc.parentNodeId == AUTO) { - findParentNode(); -#ifdef MY_LEDS_BLINKING_FEATURE - errBlink(1); -#endif - return false; - } - - // If we still don't have any node id, re-request and skip this message. - if (nc.nodeId == AUTO) { - requestNodeId(); -#ifdef MY_LEDS_BLINKING_FEATURE - errBlink(1); -#endif - return false; - } - - mSetVersion(message, PROTOCOL_VERSION); - -#ifdef MY_SIGNING_FEATURE - // If destination is known to require signed messages and we are the sender, sign this message unless it is an ACK or a handshake message - if (DO_SIGN(message.destination) && message.sender == nc.nodeId && !mGetAck(message) && mGetLength(message) && - (mGetCommand(message) != C_INTERNAL || - (message.type != I_GET_NONCE && message.type != I_GET_NONCE_RESPONSE && message.type != I_REQUEST_SIGNING && - message.type != I_ID_REQUEST && message.type != I_ID_RESPONSE && - message.type != I_FIND_PARENT && message.type != I_FIND_PARENT_RESPONSE))) { - bool signOk = false; - // Send nonce-request - if (!sendRoute(build(tmpMsg, nc.nodeId, message.destination, message.sensor, C_INTERNAL, I_GET_NONCE, false).set(""))) { - debug(PSTR("nonce tr err\n")); - return false; - } - // We have to wait for the nonce to arrive before we can sign our original message - // Other messages could come in-between. We trust process() takes care of them - unsigned long enter = hw_millis(); - msgSign = message; // Copy the message to sign since message buffer might be touched in process() - while (hw_millis() - enter < MY_VERIFICATION_TIMEOUT_MS) { - if (process()) { - if (mGetCommand(getLastMessage()) == C_INTERNAL && getLastMessage().type == I_GET_NONCE_RESPONSE) { - // Proceed with signing if nonce has been received - if (signer.putNonce(getLastMessage()) && signer.signMsg(msgSign)) { - message = msgSign; // Write the signed message back - signOk = true; - } - break; - } - } - } - if (hw_millis() - enter > MY_VERIFICATION_TIMEOUT_MS) { - debug(PSTR("nonce tmo\n")); -#ifdef MY_LEDS_BLINKING_FEATURE - errBlink(1); -#endif - return false; - } - if (!signOk) { - debug(PSTR("sign fail\n")); -#ifdef MY_LEDS_BLINKING_FEATURE - errBlink(1); -#endif - return false; - } - // After this point, only the 'last' member of the message structure is allowed to be altered if the message has been signed, - // or signature will become invalid and the message rejected by the receiver - } else mSetSigned(message, 0); // Message is not supposed to be signed, make sure it is marked unsigned -#endif - - if (dest == GATEWAY_ADDRESS || !repeaterMode) { - // Store this address in routing table (if repeater) - if (repeaterMode) { - hw_writeConfig(EEPROM_ROUTES_ADDRESS+sender, last); - } - // If destination is the gateway or if we aren't a repeater, let - // our parent take care of the message - ok = sendWrite(nc.parentNodeId, message); - } else { - // Relay the message - uint8_t route = hw_readConfig(EEPROM_ROUTES_ADDRESS+dest); - if (route > GATEWAY_ADDRESS && route < BROADCAST_ADDRESS) { - // This message should be forwarded to a child node. If we send message - // to this nodes pipe then all children will receive it because the are - // all listening to this nodes pipe. - // - // +----B - // -A - // +----C------D - // - // We're node C, Message comes from A and has destination D - // - // Message destination is not gateway and is in routing table for this node. - // Send it downstream - return sendWrite(route, message); - } else if (sender == GATEWAY_ADDRESS && dest == BROADCAST_ADDRESS) { - // Node has not yet received any id. We need to send it - // by doing a broadcast sending, - return sendWrite(BROADCAST_ADDRESS, message); - } else if (isGateway) { - // Destination isn't in our routing table and isn't a broadcast address - // Nothing to do here - return false; - } else { - // A message comes from a child node and we have no - // route for it. - // - // +----B - // -A - // +----C------D <-- Message comes from D - // - // We're node C - // - // Message should be passed to node A (this nodes relay) - - // This message should be routed back towards sensor net gateway - ok = sendWrite(nc.parentNodeId, message); - // Add this child to our "routing table" if it not already exist - hw_writeConfig(EEPROM_ROUTES_ADDRESS+sender, last); - } - } - - if (!ok) { - // Failure when sending to parent node. The parent node might be down and we - // need to find another route to gateway. -#ifdef MY_LEDS_BLINKING_FEATURE - errBlink(1); -#endif - failedTransmissions++; - if (autoFindParent && failedTransmissions > SEARCH_FAILURES) { - findParentNode(); - } - } else { - failedTransmissions = 0; - } - return ok; -} - -boolean MySensor::sendWrite(uint8_t to, MyMessage &message) { - mSetVersion(message, PROTOCOL_VERSION); - uint8_t length = mGetSigned(message) ? MAX_MESSAGE_LENGTH : mGetLength(message); - message.last = nc.nodeId; -#ifdef MY_LEDS_BLINKING_FEATURE - txBlink(1); -#endif - bool ok = radio.send(to, &message, min(MAX_MESSAGE_LENGTH, HEADER_SIZE + length)); - - debug(PSTR("send: %d-%d-%d-%d s=%d,c=%d,t=%d,pt=%d,l=%d,sg=%d,st=%s:%s\n"), - message.sender,message.last, to, message.destination, message.sensor, mGetCommand(message), message.type, - mGetPayloadType(message), mGetLength(message), mGetSigned(message), to==BROADCAST_ADDRESS ? "bc" : (ok ? "ok":"fail"), message.getString(convBuf)); - - return ok; -} - -bool MySensor::send(MyMessage &message, bool enableAck) { - message.sender = nc.nodeId; - mSetCommand(message,C_SET); - mSetRequestAck(message,enableAck); - return sendRoute(message); -} - -void MySensor::sendBatteryLevel(uint8_t value, bool enableAck) { - sendRoute(build(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_BATTERY_LEVEL, enableAck).set(value)); -} - -void MySensor::sendHeartbeat(void) { - sendRoute(build(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_HEARTBEAT, false).set(heartbeat++)); - -} - -void MySensor::present(uint8_t childSensorId, uint8_t sensorType, const char *description, bool enableAck) { - sendRoute(build(msg, nc.nodeId, GATEWAY_ADDRESS, childSensorId, C_PRESENTATION, sensorType, enableAck).set(childSensorId==NODE_SENSOR_ID?LIBRARY_VERSION:description)); -} - -void MySensor::sendSketchInfo(const char *name, const char *version, bool enableAck) { - if (name != NULL) { - sendRoute(build(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_SKETCH_NAME, enableAck).set(name)); - } - if (version != NULL) { - sendRoute(build(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_SKETCH_VERSION, enableAck).set(version)); - } -} - -void MySensor::request(uint8_t childSensorId, uint8_t variableType, uint8_t destination) { - sendRoute(build(msg, nc.nodeId, destination, childSensorId, C_REQ, variableType, false).set("")); -} - -void MySensor::requestTime(void (* _timeCallback)(unsigned long)) { - timeCallback = _timeCallback; - sendRoute(build(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_TIME, false).set("")); -} - -boolean MySensor::process() { - hw_watchdogReset(); - -#ifdef MY_LEDS_BLINKING_FEATURE - handleLedsBlinking(); -#endif - - uint8_t to = 0; - if (!radio.available(&to)) - { -#ifdef MY_OTA_FIRMWARE_FEATURE - unsigned long enter = hw_millis(); - if (fwUpdateOngoing && (enter - fwLastRequestTime > MY_OTA_RETRY_DELAY)) { - if (!fwRetry) { - debug(PSTR("fw upd fail\n")); - // Give up. We have requested MY_OTA_RETRY times without any packet in return. - fwUpdateOngoing = false; -#ifdef MY_LEDS_BLINKING_FEATURE - errBlink(1); -#endif - return false; - } - fwRetry--; - fwLastRequestTime = enter; - // Time to (re-)request firmware block from controller - RequestFWBlock *firmwareRequest = (RequestFWBlock *)msg.data; - mSetLength(msg, sizeof(RequestFWBlock)); - firmwareRequest->type = fc.type; - firmwareRequest->version = fc.version; - firmwareRequest->block = (fwBlock - 1); - sendRoute(build(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_STREAM, ST_FIRMWARE_REQUEST, false)); - } -#endif - return false; - } - -#ifdef MY_SIGNING_FEATURE - (void)signer.checkTimer(); // Manage signing timeout -#endif - - uint8_t len = radio.receive((uint8_t *)&msg); - (void)len; //until somebody makes use of 'len' -#ifdef MY_LEDS_BLINKING_FEATURE - rxBlink(1); -#endif - -#ifdef MY_SIGNING_FEATURE - // Before processing message, reject unsigned messages if signing is required and check signature (if it is signed and addressed to us) - // Note that we do not care at all about any signature found if we do not require signing, nor do we care about ACKs (they are never signed) - if (signer.requestSignatures() && msg.destination == nc.nodeId && mGetLength(msg) && !mGetAck(msg) && - (mGetCommand(msg) != C_INTERNAL || - (msg.type != I_GET_NONCE_RESPONSE && msg.type != I_GET_NONCE && msg.type != I_REQUEST_SIGNING && - msg.type != I_ID_REQUEST && msg.type != I_ID_RESPONSE && - msg.type != I_FIND_PARENT && msg.type != I_FIND_PARENT_RESPONSE))) { - if (!mGetSigned(msg)) { - // Got unsigned message that should have been signed - debug(PSTR("no sign\n")); -#ifdef MY_LEDS_BLINKING_FEATURE - errBlink(1); -#endif - return false; - } - else if (!signer.verifyMsg(msg)) { - debug(PSTR("verify fail\n")); -#ifdef MY_LEDS_BLINKING_FEATURE - errBlink(1); -#endif - return false; // This signed message has been tampered with! - } - } -#endif - - // Add string termination, good if we later would want to print it. - msg.data[mGetLength(msg)] = '\0'; - debug(PSTR("read: %d-%d-%d s=%d,c=%d,t=%d,pt=%d,l=%d,sg=%d:%s\n"), - msg.sender, msg.last, msg.destination, msg.sensor, mGetCommand(msg), msg.type, mGetPayloadType(msg), mGetLength(msg), mGetSigned(msg), msg.getString(convBuf)); - mSetSigned(msg,0); // Clear the sign-flag now as verification (and debug printing) is completed - - if(!(mGetVersion(msg) == PROTOCOL_VERSION)) { - debug(PSTR("ver mismatch\n")); -#ifdef MY_LEDS_BLINKING_FEATURE - errBlink(1); -#endif - return false; - } - - uint8_t command = mGetCommand(msg); - uint8_t type = msg.type; - uint8_t sender = msg.sender; - uint8_t last = msg.last; - uint8_t destination = msg.destination; - - if (destination == nc.nodeId) { - // This message is addressed to this node - - if (repeaterMode && last != nc.parentNodeId) { - // Message is from one of the child nodes. Add it to routing table. - hw_writeConfig(EEPROM_ROUTES_ADDRESS+sender, last); - } - - // Check if sender requests an ack back. - if (mGetRequestAck(msg)) { - // Copy message - tmpMsg = msg; - mSetRequestAck(tmpMsg,false); // Reply without ack flag (otherwise we would end up in an eternal loop) - mSetAck(tmpMsg,true); - tmpMsg.sender = nc.nodeId; - tmpMsg.destination = msg.sender; - sendRoute(tmpMsg); - } - - if (command == C_INTERNAL) { - if (type == I_FIND_PARENT_RESPONSE) { - if (autoFindParent) { - // We've received a reply to a FIND_PARENT message. Check if the distance is - // shorter than we already have. - uint8_t distance = msg.getByte(); - if (isValidDistance(distance)) - { - // Distance to gateway is one more for us w.r.t. parent - distance++; - if (isValidDistance(distance) && (distance < nc.distance)) { - // Found a neighbor closer to GW than previously found - nc.distance = distance; - nc.parentNodeId = msg.sender; - hw_writeConfig(EEPROM_PARENT_NODE_ID_ADDRESS, nc.parentNodeId); - hw_writeConfig(EEPROM_DISTANCE_ADDRESS, nc.distance); - debug(PSTR("parent=%d, d=%d\n"), nc.parentNodeId, nc.distance); - } - } - } - return false; -#ifdef MY_SIGNING_FEATURE - } else if (type == I_GET_NONCE) { - if (signer.getNonce(msg)) { - sendRoute(build(msg, nc.nodeId, msg.sender, NODE_SENSOR_ID, C_INTERNAL, I_GET_NONCE_RESPONSE, false)); - } - return false; // Nonce exchange is an internal MySensor protocol message, no need to inform caller about this - } else if (type == I_REQUEST_SIGNING) { - if (msg.getBool()) { - // We received an indicator that the sender require us to sign all messages we send to it - SET_SIGN(msg.sender); - } else { - // We received an indicator that the sender does not require us to sign all messages we send to it - CLEAR_SIGN(msg.sender); - } - // Save updated table - hw_writeConfigBlock((void*)doSign, (void*)EEPROM_SIGNING_REQUIREMENT_TABLE_ADDRESS, sizeof(doSign)); - - // Inform sender about our preference if we are a gateway, but only require signing if the sender required signing - // We do not currently want a gateway to require signing from all nodes in a network just because it wants one node - // to sign it's messages - if (isGateway) { - if (signer.requestSignatures() && DO_SIGN(msg.sender)) - sendRoute(build(msg, nc.nodeId, msg.sender, NODE_SENSOR_ID, C_INTERNAL, I_REQUEST_SIGNING, false).set(true)); - else - sendRoute(build(msg, nc.nodeId, msg.sender, NODE_SENSOR_ID, C_INTERNAL, I_REQUEST_SIGNING, false).set(false)); - } - return false; // Signing request is an internal MySensor protocol message, no need to inform caller about this - } else if (type == I_GET_NONCE_RESPONSE) { - return true; // Just pass along nonce silently (no need to call callback for these) -#endif - } else if (sender == GATEWAY_ADDRESS) { - if (type == I_ID_RESPONSE && nc.nodeId == AUTO) { - nc.nodeId = msg.getByte(); - if (nc.nodeId == AUTO) { - // sensor net gateway will return max id if all sensor id are taken - debug(PSTR("full\n")); - while (1); // Wait here. Nothing else we can do... - } - setupNode(); - // Write id to EEPROM - hw_writeConfig(EEPROM_NODE_ID_ADDRESS, nc.nodeId); - debug(PSTR("id=%d\n"), nc.nodeId); - } else { - processInternalMessages(msg); - } - return false; - } - } -#ifdef MY_OTA_FIRMWARE_FEATURE - else if (command == C_STREAM) { - if (type == ST_FIRMWARE_CONFIG_RESPONSE) { - NodeFirmwareConfig *firmwareConfigResponse = (NodeFirmwareConfig *)msg.data; - // compare with current node configuration, if they differ, start fw fetch process - if (memcmp(&fc,firmwareConfigResponse,sizeof(NodeFirmwareConfig))) { - debug(PSTR("fw update\n")); - // copy new FW config - memcpy(&fc,firmwareConfigResponse,sizeof(NodeFirmwareConfig)); - // Init flash - if (!flash.initialize()) { - debug(PSTR("flash init fail\n")); - fwUpdateOngoing = false; - } else { - // erase lower 32K -> max flash size for ATMEGA328 - flash.blockErase32K(0); - // wait until flash erased - while ( flash.busy() ); - fwBlock = fc.blocks; - fwUpdateOngoing = true; - // reset flags - fwRetry = MY_OTA_RETRY+1; - fwLastRequestTime = 0; - } - return false; - } else debug(PSTR("fw update skipped\n")); - } else if (type == ST_FIRMWARE_RESPONSE) { - if (fwUpdateOngoing) { - // Save block to flash - debug(PSTR("fw block %d\n"), fwBlock); - // extract FW block - ReplyFWBlock *firmwareResponse = (ReplyFWBlock *)msg.data; - // write to flash - flash.writeBytes( ((fwBlock - 1) * FIRMWARE_BLOCK_SIZE) + FIRMWARE_START_OFFSET, firmwareResponse->data, FIRMWARE_BLOCK_SIZE); - // wait until flash written - while ( flash.busy() ); - fwBlock--; - if (!fwBlock) { - // We're finished! Do a checksum and reboot. - fwUpdateOngoing = false; - if (isValidFirmware()) { - debug(PSTR("fw checksum ok\n")); - // All seems ok, write size and signature to flash (DualOptiboot will pick this up and flash it) - uint16_t fwsize = FIRMWARE_BLOCK_SIZE * fc.blocks; - uint8_t OTAbuffer[10] = {'F','L','X','I','M','G',':',(fwsize >> 8),fwsize,':'}; - flash.writeBytes(0, OTAbuffer, 10); - // Write the new firmware config to eeprom - hw_writeConfigBlock((void*)&fc, (void*)EEPROM_FIRMWARE_TYPE_ADDRESS, sizeof(NodeFirmwareConfig)); - hw_reboot(); - } else { - debug(PSTR("fw checksum fail\n")); - } - } - // reset flags - fwRetry = MY_OTA_RETRY+1; - fwLastRequestTime = 0; - } else { - debug(PSTR("No fw update ongoing\n")); - } - return false; - } - - } -#endif - // Call incoming message callback if available - if (msgCallback != NULL) { - msgCallback(msg); - } - // Return true if message was addressed for this node... - return true; - } else if (repeaterMode && nc.nodeId != AUTO) { - // If this node have an id, relay the message - - if (command == C_INTERNAL && type == I_FIND_PARENT) { - if (sender != nc.parentNodeId) { - if (nc.distance == DISTANCE_INVALID) - findParentNode(); - - if (nc.distance != DISTANCE_INVALID) { - // Relaying nodes should always answer ping messages - // Wait a random delay of 0-2 seconds to minimize collision - // between ping ack messages from other relaying nodes - wait(hw_millis() & 0x3ff); - sendWrite(sender, build(msg, nc.nodeId, sender, NODE_SENSOR_ID, C_INTERNAL, I_FIND_PARENT_RESPONSE, false).set(nc.distance)); - } - } - } else if (to == nc.nodeId) { - // We should try to relay this message to another node - sendRoute(msg); - } - } - return false; -} - - -void MySensor::processInternalMessages(MyMessage &msg) { - bool isMetric; - uint8_t type = msg.type; - - if (type == I_REBOOT) { - // Requires MySensors or other bootloader with watchdogs enabled - hw_reboot(); - } else if (type == I_CONFIG) { - // Pick up configuration from controller (currently only metric/imperial) - // and store it in eeprom if changed - isMetric = msg.getString()[0] == 'M' ; - cc.isMetric = isMetric; - hw_writeConfig(EEPROM_CONTROLLER_CONFIG_ADDRESS, isMetric); - } else if (type == I_CHILDREN) { - if (repeaterMode && msg.getString()[0] == 'C') { - // Clears child relay data for this node - debug(PSTR("clear\n")); - uint8_t i = 255; - do { - hw_writeConfig(EEPROM_ROUTES_ADDRESS+i, 0xff); - } while (i--); - // Clear parent node id & distance to gw - hw_writeConfig(EEPROM_PARENT_NODE_ID_ADDRESS, 0xFF); - hw_writeConfig(EEPROM_DISTANCE_ADDRESS, 0xFF); - // Find parent node - findParentNode(); - sendRoute(build(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_CHILDREN,false).set("")); - } - } else if (type == I_TIME) { - if (timeCallback != NULL) { - // Deliver time to callback - timeCallback(msg.getULong()); - } - } -} - - -MyMessage& MySensor::getLastMessage() { - return msg; -} - -void MySensor::saveState(uint8_t pos, uint8_t value) { - hw_writeConfig(EEPROM_LOCAL_CONFIG_ADDRESS+pos, value); -} -uint8_t MySensor::loadState(uint8_t pos) { - return hw_readConfig(EEPROM_LOCAL_CONFIG_ADDRESS+pos); -} - -void MySensor::wait(unsigned long ms) { - unsigned long enter = hw_millis(); - while (hw_millis() - enter < ms) { - process(); - } -} - -void MySensor::sleep(unsigned long ms) { -#ifdef MY_OTA_FIRMWARE_FEATURE - if (fwUpdateOngoing) { - // Do not sleep node while fw update is ongoing - process(); - } else { -#endif - radio.powerDown(); - hw.sleep(ms); -#ifdef MY_OTA_FIRMWARE_FEATURE - } -#endif -} - -bool MySensor::sleep(uint8_t interrupt, uint8_t mode, unsigned long ms) { -#ifdef MY_OTA_FIRMWARE_FEATURE - if (fwUpdateOngoing) { - // Do not sleep node while fw update is ongoing - process(); - return false; - } else { -#endif - radio.powerDown(); - return hw.sleep(interrupt, mode, ms) ; -#ifdef MY_OTA_FIRMWARE_FEATURE - } -#endif -} - -int8_t MySensor::sleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2, unsigned long ms) { -#ifdef MY_OTA_FIRMWARE_FEATURE - if (fwUpdateOngoing) { - // Do not sleep node while fw update is ongoing - process(); - return -1; - } else { -#endif - radio.powerDown(); - return hw.sleep(interrupt1, mode1, interrupt2, mode2, ms) ; -#ifdef MY_OTA_FIRMWARE_FEATURE - } -#endif -} - - - - - -// Inline function and macros static inline MyMessage& buildGw(MyMessage &msg, uint8_t type) { msg.sender = GATEWAY_ADDRESS; msg.destination = GATEWAY_ADDRESS; @@ -905,120 +94,954 @@ static inline MyMessage& buildGw(MyMessage &msg, uint8_t type) { return msg; } -MyGateway::MyGateway(MyGatewayTransport &_transport, MyTransport &_radio, - MyHw &_hw -#ifdef MY_SIGNING_FEATURE - , MySigning &_signer -#endif - ) : - transport(_transport), MySensor(_radio, _hw -#ifdef MY_SIGNING_FEATURE - , _signer -#endif - ) -{ -#ifdef MY_INCLUSION_MODE_FEATURE - inclusionMode = false; -#endif +static inline bool isValidParent( const uint8_t parent ) { + return parent != AUTO; +} +static inline bool isValidDistance( const uint8_t distance ) { + return distance != DISTANCE_INVALID; +} + + + +#ifdef MY_OTA_FIRMWARE_FEATURE +// do a crc16 on the whole received firmware +bool isValidFirmware() { + // init crc + uint16_t crc = ~0; + for (uint16_t i = 0; i < _fc.blocks * FIRMWARE_BLOCK_SIZE; ++i) { + crc ^= flash.readByte(i + FIRMWARE_START_OFFSET); + for (int8_t j = 0; j < 8; ++j) { + if (crc & 1) + crc = (crc >> 1) ^ 0xA001; + else + crc = (crc >> 1); + } + } + return crc == _fc.crc; } -void MyGateway::begin(void (*_msgCallback)(const MyMessage &)) { - // Setup digital in that triggers inclusion mode -#ifdef MY_INCLUSION_MODE_FEATURE -#ifdef MY_INCLUSION_BUTTON_FEATURE - pinMode(MY_INCLUSION_MODE_BUTTON_PIN, INPUT); - digitalWrite(MY_INCLUSION_MODE_BUTTON_PIN, HIGH); -#endif #endif - // initialize the transport driver - if (!transport.begin()) { - debug(PSTR("transport driver init fail\n")); - while(1); // Nothing more we can do +#ifdef MY_LEDS_BLINKING_FEATURE +void handleLedsBlinking() { + static unsigned long next_time = hwMillis() + MY_DEFAULT_LED_BLINK_PERIOD; + + // Just return if it is not the time... + // http://playground.arduino.cc/Code/TimingRollover + if ((long)(hwMillis() - _blink_next_time) < 0) + return; + else + _blink_next_time = hwMillis() + ledBlinkPeriod; + + // do the actual blinking + if(_countRx && _countRx != 255) { + // switch led on + hwDigitalWrite(MY_DEFAULT_RX_LED_PIN, LED_ON); + } + else if(!_countRx) { + // switching off + hwDigitalWrite(MY_DEFAULT_RX_LED_PIN, LED_OFF); + } + if(_countRx != 255) + --_countRx; + + if(_countTx && _countTx != 255) { + // switch led on + hwDigitalWrite(MY_DEFAULT_TX_LED_PIN, LED_ON); + } + else if(!_countTx) { + // switching off + hwDigitalWrite(MY_DEFAULT_TX_LED_PIN, LED_OFF); + } + if(_countTx != 255) + --_countTx; + + if(_countErr && _countErr != 255) { + // switch led on + hwDigitalWrite(MY_DEFAULT_ERR_LED_PIN, LED_ON); + } + else if(!_countErr) { + // switching off + hwDigitalWrite(MY_DEFAULT_ERR_LED_PIN, LED_OFF); + } + if(_countErr != 255) + --_countErr; +} + +void rxBlink(uint8_t cnt) { + if(_countRx == 255) { _countRx = cnt; } +} + +void txBlink(uint8_t cnt) { + if(_countTx == 255) { _countTx = cnt; } +} + +void errBlink(uint8_t cnt) { + if(_countErr == 255) { _countErr = cnt; } +} +#endif + + +void dataCallback(void (*msgCallback)(const MyMessage &)) { + _msgCallback = msgCallback; +} + +void begin() { + + #if defined(MY_INCLUSION_MODE_FEATURE) + inclusionMode = false; + #endif + + #ifndef MY_DISABLED_SERIAL + hwInit(); + #endif + + #ifdef MY_GATEWAY_FEATURE + #if defined(MY_INCLUSION_MODE_FEATURE) && defined(MY_INCLUSION_BUTTON_FEATURE) + // Setup digital in that triggers inclusion mode + pinMode(MY_INCLUSION_MODE_BUTTON_PIN, INPUT); + digitalWrite(MY_INCLUSION_MODE_BUTTON_PIN, HIGH); + #endif + // initialize the transport driver + if (!gatewayTransportBegin()) { + debug(PSTR("transport driver init fail\n")); + while(1); // Nothing more we can do + } + #endif + + #if defined(MY_GATEWAY_FEATURE) + gatewayTransportSend(buildGw(_msg, I_GATEWAY_READY).set("Gateway startup complete.")); + #endif + + #if defined(MY_RADIO_FEATURE) + _failedTransmissions = 0; + + // Setup radio + if (!transport.init()) { + debug(PSTR("radio init fail\n")); + while(1); // Nothing more we can do + } + #endif + + #ifdef MY_SIGNING_FEATURE + // Read out the signing requirements from EEPROM + hwReadConfigBlock((void*)_doSign, (void*)EEPROM_SIGNING_REQUIREMENT_TABLE_ADDRESS, sizeof(_doSign)); + #endif + +#ifdef MY_LEDS_BLINKING_FEATURE + // Setup led pins + pinMode(MY_DEFAULT_RX_LED_PIN, OUTPUT); + pinMode(MY_DEFAULT_TX_LED_PIN, OUTPUT); + pinMode(MY_DEFAULT_ERR_LED_PIN, OUTPUT); + + // Set initial state of leds + hwDigitalWrite(MY_DEFAULT_RX_LED_PIN, LED_OFF); + hwDigitalWrite(MY_DEFAULT_TX_LED_PIN, LED_OFF); + hwDigitalWrite(MY_DEFAULT_ERR_LED_PIN, LED_OFF); + + // initialize counters + _countRx = 0; + _countTx = 0; + _countErr = 0; +#endif + + // Read latest received controller configuration from EEPROM + hwReadConfigBlock((void*)&_cc, (void*)EEPROM_CONTROLLER_CONFIG_ADDRESS, sizeof(ControllerConfig)); + if (_cc.isMetric == 0xff) { + // Eeprom empty, set default to metric + _cc.isMetric = 0x01; } - // Start MySensors in repeater mode - MySensor::begin(_msgCallback, GATEWAY_ADDRESS, true, GATEWAY_ADDRESS); + #if defined(MY_GATEWAY_FEATURE) + // Set configuration for gateway + _nc.parentNodeId = GATEWAY_ADDRESS; + _nc.distance = 0; + _nc.nodeId = GATEWAY_ADDRESS; + #else + // Read settings from eeprom + hwReadConfigBlock((void*)&_nc, (void*)EEPROM_NODE_ID_ADDRESS, sizeof(NodeConfig)); + // Read latest received controller configuration from EEPROM + hwReadConfigBlock((void*)&_cc, (void*)EEPROM_CONTROLLER_CONFIG_ADDRESS, sizeof(ControllerConfig)); + #ifdef MY_OTA_FIRMWARE_FEATURE + // Read firmware config from EEPROM, i.e. type, version, CRC, blocks + hwReadConfigBlock((void*)&_fc, (void*)EEPROM_FIRMWARE_TYPE_ADDRESS, sizeof(NodeFirmwareConfig)); + #endif - // Startup complete - transport.send(buildGw(msg, I_GATEWAY_READY).set("Gateway startup complete.")); + _autoFindParent = MY_PARENT_NODE_ID == AUTO; + if (!_autoFindParent) { + _nc.parentNodeId = MY_PARENT_NODE_ID; + // Save static parent id in eeprom (used by bootloader) + hwWriteConfig(EEPROM_PARENT_NODE_ID_ADDRESS, MY_PARENT_NODE_ID); + // We don't actually know the distance to gw here. Let's pretend it is 1. + // If the current node is also repeater, be aware of this. + _nc.distance = 1; + } else if (!isValidParent(_nc.parentNodeId)) { + // Auto find parent, but parent in eeprom is invalid. Try find one. + findParentNode(); + } + if (MY_NODE_ID != AUTO) { + // Set static id + _nc.nodeId = MY_NODE_ID; + // Save static id in eeprom + hwWriteConfig(EEPROM_NODE_ID_ADDRESS, MY_NODE_ID); + } else if (_nc.nodeId == AUTO && isValidParent(_nc.parentNodeId)) { + // Try to fetch node-id from gateway + requestNodeId(); + } + #endif + + #if defined(MY_RADIO_FEATURE) + setupNode(); + #endif + debug(PSTR("%s started, id=%d, parent=%d, distance=%d\n"), MY_NODE_TYPE, _nc.nodeId, _nc.parentNodeId, _nc.distance); } -boolean MyGateway::sendRoute(MyMessage &message) { - if (message.destination == GATEWAY_ADDRESS) { - // This is a message sent from a sensor attached on the gateway node. - // Pass it directly to the gateway transport layer. - transport.send(message); - } else { - MySensor::sendRoute(message); - } + + + +uint8_t getNodeId() { + return _nc.nodeId; } -boolean MyGateway::process() { -#ifdef MY_INCLUSION_MODE_FEATURE - checkInclusionMode(); +ControllerConfig getConfig() { + return _cc; +} + +#if defined(MY_RADIO_FEATURE) +void requestNodeId() { + debug(PSTR("req id\n")); + transport.setAddress(_nc.nodeId); + build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_ID_REQUEST, false).set(""); + sendWrite(_nc.parentNodeId, _msg); + wait(2000); +} #endif - bool newMessage = false; - if (transport.available()) { - MyMessage &gmsg = transport.receive(); - if (msg.destination == GATEWAY_ADDRESS) { - // Check if sender requests an ack back. - if (mGetRequestAck(gmsg)) { - // Copy message - tmpMsg = gmsg; - mSetRequestAck(tmpMsg, false); // Reply without ack flag (otherwise we would end up in an eternal loop) - mSetAck(tmpMsg, true); - tmpMsg.sender = nc.nodeId; - tmpMsg.destination = msg.sender; - transport.send(tmpMsg); + +#if defined(MY_RADIO_FEATURE) + + +void setupNode() { + // Open reading pipe for messages directed to this node (set write pipe to same) + transport.setAddress(_nc.nodeId); + // Present node and request config + #ifndef MY_GATEWAY_FEATURE + if (_nc.nodeId != AUTO) { + #ifdef MY_SIGNING_FEATURE + // Notify gateway (and possibly controller) about the signing preferences of this node + sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_REQUEST_SIGNING, false).set(signer.requestSignatures())); + + // If we do require signing, wait for the gateway to tell us how it prefer us to transmit our messages + if (signer.requestSignatures()) { + wait(2000); + } + #endif + + // Send presentation for this radio node + #ifdef MY_REPEATER_FEATURE + present(NODE_SENSOR_ID, S_ARDUINO_REPEATER_NODE); + #else + present(NODE_SENSOR_ID, S_ARDUINO_NODE); + #endif + // Send a configuration exchange request to controller + // Node sends parent node. Controller answers with latest node configuration + // which is picked up in process() + sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_CONFIG, false).set(_nc.parentNodeId)); + + // Wait configuration reply. + wait(2000); + + #ifdef MY_OTA_FIRMWARE_FEATURE + RequestFirmwareConfig *reqFWConfig = (RequestFirmwareConfig *)_msg.data; + mSetLength(_msg, sizeof(RequestFirmwareConfig)); + mSetCommand(_msg, C_STREAM); + mSetPayloadType(_msg,P_CUSTOM); + // copy node settings to reqFWConfig + memcpy(reqFWConfig,&_fc,sizeof(NodeFirmwareConfig)); + // add bootloader information + reqFWConfig->BLVersion = MY_OTA_BOOTLOADER_VERSION; + _fwUpdateOngoing = false; + sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_STREAM, ST_FIRMWARE_CONFIG_REQUEST, false)); + #endif + } + #endif +} +#endif + +#if defined(MY_RADIO_FEATURE) +void findParentNode() { + static boolean findingParentNode = false; + + if (findingParentNode) + return; + findingParentNode = true; + + _failedTransmissions = 0; + + // Set distance to max + _nc.distance = 255; + + // Send ping message to BROADCAST_ADDRESS (to which all relaying nodes and gateway listens and should reply to) + debug(PSTR("find parent\n")); + + build(_msg, _nc.nodeId, BROADCAST_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_FIND_PARENT, false).set(""); + // Write msg, but suppress recursive parent search + sendWrite(BROADCAST_ADDRESS, _msg); + + // Wait for ping response. + wait(2000); + findingParentNode = false; +} +#endif + + + +boolean sendRoute(MyMessage &message) { + + #if defined(MY_GATEWAY_FEATURE) + if (message.destination == _nc.nodeId) { + // This is a message sent from a sensor attached on the gateway node. + // Pass it directly to the gateway transport layer. + #ifdef MY_LEDS_BLINKING_FEATURE + txBlink(1); + #endif + return gatewayTransportSend(message); + } + #endif + + + #if defined(MY_RADIO_FEATURE) + uint8_t sender = message.sender; + uint8_t dest = message.destination; + uint8_t last = message.last; + bool ok; + + // If we still don't have any parent id, re-request and skip this message. + if (_nc.parentNodeId == AUTO) { + findParentNode(); + #ifdef MY_LEDS_BLINKING_FEATURE + errBlink(1); + #endif + return false; + } + + // If we still don't have any node id, re-request and skip this message. + if (_nc.nodeId == AUTO) { + requestNodeId(); + #ifdef MY_LEDS_BLINKING_FEATURE + errBlink(1); + #endif + return false; + } + + mSetVersion(message, PROTOCOL_VERSION); + + #ifdef MY_SIGNING_FEATURE + // If destination is known to require signed messages and we are the sender, sign this message unless it is an ACK or a handshake message + if (DO_SIGN(message.destination) && message.sender == _nc.nodeId && !mGetAck(message) && mGetLength(message) && + (mGetCommand(message) != C_INTERNAL || + (message.type != I_GET_NONCE && message.type != I_GET_NONCE_RESPONSE && message.type != I_REQUEST_SIGNING && + message.type != I_ID_REQUEST && message.type != I_ID_RESPONSE && + message.type != I_FIND_PARENT && message.type != I_FIND_PARENT_RESPONSE))) { + bool signOk = false; + // Send nonce-request + if (!sendRoute(build(_msgTmp, _nc.nodeId, message.destination, message.sensor, C_INTERNAL, I_GET_NONCE, false).set(""))) { + debug(PSTR("nonce tr err\n")); + return false; } - if (mGetCommand(gmsg) == C_INTERNAL) { - if (msg.type == I_VERSION) { - // Request for version. Create the response - transport.send(buildGw(msg, I_VERSION).set(LIBRARY_VERSION)); -#ifdef MY_INCLUSION_MODE_FEATURE - } else if (msg.type == I_INCLUSION_MODE) { - // Request to change inclusion mode - setInclusionMode(atoi(msg.data) == 1); + // We have to wait for the nonce to arrive before we can sign our original message + // Other messages could come in-between. We trust process() takes care of them + unsigned long enter = hwMillis(); + _msgSign = message; // Copy the message to sign since message buffer might be touched in process() + while (hwMillis() - enter < MY_VERIFICATION_TIMEOUT_MS) { + if (process()) { + if (mGetCommand(getLastMessage()) == C_INTERNAL && getLastMessage().type == I_GET_NONCE_RESPONSE) { + // Proceed with signing if nonce has been received + if (signer.putNonce(getLastMessage()) && signer.signMsg(_msgSign)) { + message = _msgSign; // Write the signed message back + signOk = true; + } + break; + } + } + } + if (hwMillis() - enter > MY_VERIFICATION_TIMEOUT_MS) { + debug(PSTR("nonce tmo\n")); + #ifdef MY_LEDS_BLINKING_FEATURE + errBlink(1); + #endif + return false; + } + if (!signOk) { + debug(PSTR("sign fail\n")); + #ifdef MY_LEDS_BLINKING_FEATURE + errBlink(1); + #endif + return false; + } + // After this point, only the 'last' member of the message structure is allowed to be altered if the message has been signed, + // or signature will become invalid and the message rejected by the receiver + } else mSetSigned(message, 0); // Message is not supposed to be signed, make sure it is marked unsigned + #endif + + if (dest == GATEWAY_ADDRESS || !MY_IS_REPEATER) { + // Store this address in routing table (if repeater) + #if defined (MY_IS_REPEATER) + hwWriteConfig(EEPROM_ROUTES_ADDRESS+sender, last); + #endif + // If destination is the gateway or if we aren't a repeater, let + // our parent take care of the message + ok = sendWrite(_nc.parentNodeId, message); + } else { + // Relay the message + uint8_t route = hwReadConfig(EEPROM_ROUTES_ADDRESS+dest); + if (route > GATEWAY_ADDRESS && route < BROADCAST_ADDRESS) { + // This message should be forwarded to a child node. If we send message + // to this nodes pipe then all children will receive it because the are + // all listening to this nodes pipe. + // + // +----B + // -A + // +----C------D + // + // We're node C, Message comes from A and has destination D + // + // Message destination is not gateway and is in routing table for this node. + // Send it downstream + return sendWrite(route, message); + } else if (sender == GATEWAY_ADDRESS && dest == BROADCAST_ADDRESS) { + // Node has not yet received any id. We need to send it + // by doing a broadcast sending, + return sendWrite(BROADCAST_ADDRESS, message); + } else if (MY_IS_GATEWAY) { + // Destination isn't in our routing table and isn't a broadcast address + // Nothing to do here + return false; + } else { + // A message comes from a child node and we have no + // route for it. + // + // +----B + // -A + // +----C------D <-- Message comes from D + // + // We're node C + // + // Message should be passed to node A (this nodes relay) + + // This message should be routed back towards sensor net gateway + ok = sendWrite(_nc.parentNodeId, message); + // Add this child to our "routing table" if it not already exist + hwWriteConfig(EEPROM_ROUTES_ADDRESS+sender, last); + } + } + + if (!ok) { + // Failure when sending to parent node. The parent node might be down and we + // need to find another route to gateway. + #ifdef MY_LEDS_BLINKING_FEATURE + errBlink(1); + #endif + _failedTransmissions++; + if (_autoFindParent && _failedTransmissions > SEARCH_FAILURES) { + findParentNode(); + } + } else { + _failedTransmissions = 0; + } + return ok; + #else + return false; + #endif +} + +#ifdef MY_RADIO_FEATURE +boolean sendWrite(uint8_t to, MyMessage &message) { + mSetVersion(message, PROTOCOL_VERSION); + uint8_t length = mGetSigned(message) ? MAX_MESSAGE_LENGTH : mGetLength(message); + message.last = _nc.nodeId; + #ifdef MY_LEDS_BLINKING_FEATURE + txBlink(1); + #endif + bool ok = transport.send(to, &message, min(MAX_MESSAGE_LENGTH, HEADER_SIZE + length)); + + debug(PSTR("send: %d-%d-%d-%d s=%d,c=%d,t=%d,pt=%d,l=%d,sg=%d,st=%s:%s\n"), + message.sender,message.last, to, message.destination, message.sensor, mGetCommand(message), message.type, + mGetPayloadType(message), mGetLength(message), mGetSigned(message), to==BROADCAST_ADDRESS ? "bc" : (ok ? "ok":"fail"), message.getString(convBuf)); + + return ok; +} #endif + +bool send(MyMessage &message, bool enableAck) { + message.sender = _nc.nodeId; + mSetCommand(message,C_SET); + mSetRequestAck(message,enableAck); + return sendRoute(message); +} + +void sendBatteryLevel(uint8_t value, bool enableAck) { + sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_BATTERY_LEVEL, enableAck).set(value)); +} + +void sendHeartbeat(void) { + sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_HEARTBEAT, false).set(_heartbeat++)); + +} + +void present(uint8_t childSensorId, uint8_t sensorType, const char *description, bool enableAck) { + sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, childSensorId, C_PRESENTATION, sensorType, enableAck).set(childSensorId==NODE_SENSOR_ID?LIBRARY_VERSION:description)); +} + +void sendSketchInfo(const char *name, const char *version, bool enableAck) { + if (name != NULL) { + sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_SKETCH_NAME, enableAck).set(name)); + } + if (version != NULL) { + sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_SKETCH_VERSION, enableAck).set(version)); + } +} + +void request(uint8_t childSensorId, uint8_t variableType, uint8_t destination) { + sendRoute(build(_msg, _nc.nodeId, destination, childSensorId, C_REQ, variableType, false).set("")); +} + +void requestTime(void (* timeCallback)(unsigned long)) { + _timeCallback = timeCallback; + sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_TIME, false).set("")); +} + +void process() { + hwWatchdogReset(); + + #ifdef MY_LEDS_BLINKING_FEATURE + handleLedsBlinking(); + #endif + + #ifdef MY_INCLUSION_MODE_FEATURE + checkInclusionMode(); + #endif + + #if defined (MY_GATEWAY_FEATURE) + if (gatewayTransportAvailable()) { + MyMessage &_msg = gatewayTransportReceive(); + if (_msg.destination == GATEWAY_ADDRESS) { + // Check if sender requests an ack back. + if (mGetRequestAck(_msg)) { + // Copy message + _msgTmp = _msg; + mSetRequestAck(_msgTmp, false); // Reply without ack flag (otherwise we would end up in an eternal loop) + mSetAck(_msgTmp, true); + _msgTmp.sender = _nc.nodeId; + _msgTmp.destination = _msg.sender; + gatewayTransportSend(_msgTmp); + } + if (mGetCommand(_msg) == C_INTERNAL) { + if (_msg.type == I_VERSION) { + // Request for version. Create the response + gatewayTransportSend(buildGw(_msg, I_VERSION).set(LIBRARY_VERSION)); + #ifdef MY_INCLUSION_MODE_FEATURE + } else if (_msg.type == I_INCLUSION_MODE) { + // Request to change inclusion mode + setInclusionMode(atoi(_msg.data) == 1); + #endif } else { - processInternalMessages(gmsg); + processInternalMessages(); } } else { // Call incoming message callback if available - if (msgCallback != NULL) { - msgCallback(msg); + if (_msgCallback != NULL) { + _msgCallback(_msg); } - newMessage = true; } } } + #endif - // new message from sensor node - if (MySensor::process()) { - MyMessage &msg = MySensor::getLastMessage(); - newMessage = true; - transport.send(msg); + #if defined(MY_RADIO_FEATURE) + uint8_t to = 0; + if (!transport.available(&to)) + { + #ifdef MY_OTA_FIRMWARE_FEATURE + unsigned long enter = hwMillis(); + if (_fwUpdateOngoing && (enter - _fwLastRequestTime > MY_OTA_RETRY_DELAY)) { + if (!_fwRetry) { + debug(PSTR("fw upd fail\n")); + // Give up. We have requested MY_OTA_RETRY times without any packet in return. + _fwUpdateOngoing = false; + #ifdef MY_LEDS_BLINKING_FEATURE + errBlink(1); + #endif + return; + } + _fwRetry--; + _fwLastRequestTime = enter; + // Time to (re-)request firmware block from controller + RequestFWBlock *firmwareRequest = (RequestFWBlock *)_msg.data; + mSetLength(_msg, sizeof(RequestFWBlock)); + firmwareRequest->type = _fc.type; + firmwareRequest->version = _fc.version; + firmwareRequest->block = (_fwBlock - 1); + sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_STREAM, ST_FIRMWARE_REQUEST, false)); + } + #endif + return; } - return newMessage; + #ifdef MY_SIGNING_FEATURE + (void)signer.checkTimer(); // Manage signing timeout + #endif + + uint8_t len = transport.receive((uint8_t *)&_msg); + (void)len; //until somebody makes use of 'len' + #ifdef MY_LEDS_BLINKING_FEATURE + rxBlink(1); + #endif + + #ifdef MY_SIGNING_FEATURE + // Before processing message, reject unsigned messages if signing is required and check signature (if it is signed and addressed to us) + // Note that we do not care at all about any signature found if we do not require signing, nor do we care about ACKs (they are never signed) + if (signer.requestSignatures() && _msg.destination == _nc.nodeId && mGetLength(_msg) && !mGetAck(_msg) && + (mGetCommand(_msg) != C_INTERNAL || + (_msg.type != I_GET_NONCE_RESPONSE && _msg.type != I_GET_NONCE && _msg.type != I_REQUEST_SIGNING && + _msg.type != I_ID_REQUEST && _msg.type != I_ID_RESPONSE && + _msg.type != I_FIND_PARENT && _msg.type != I_FIND_PARENT_RESPONSE))) { + if (!mGetSigned(_msg)) { + // Got unsigned message that should have been signed + debug(PSTR("no sign\n")); + #ifdef MY_LEDS_BLINKING_FEATURE + errBlink(1); + #endif + return; + } + else if (!signer.verifyMsg(_msg)) { + debug(PSTR("verify fail\n")); + #ifdef MY_LEDS_BLINKING_FEATURE + errBlink(1); + #endif + return; // This signed message has been tampered with! + } + } + #endif + + // Add string termination, good if we later would want to print it. + _msg.data[mGetLength(_msg)] = '\0'; + debug(PSTR("read: %d-%d-%d s=%d,c=%d,t=%d,pt=%d,l=%d,sg=%d:%s\n"), + _msg.sender, _msg.last, _msg.destination, _msg.sensor, mGetCommand(_msg), _msg.type, mGetPayloadType(_msg), mGetLength(_msg), mGetSigned(_msg), _msg.getString(convBuf)); + mSetSigned(_msg,0); // Clear the sign-flag now as verification (and debug printing) is completed + + if(!(mGetVersion(_msg) == PROTOCOL_VERSION)) { + debug(PSTR("ver mismatch\n")); + #ifdef MY_LEDS_BLINKING_FEATURE + errBlink(1); + #endif + return; + } + + uint8_t command = mGetCommand(_msg); + uint8_t type = _msg.type; + uint8_t sender = _msg.sender; + uint8_t last = _msg.last; + uint8_t destination = _msg.destination; + + if (destination == _nc.nodeId) { + // This message is addressed to this node + + if (MY_IS_REPEATER && last != _nc.parentNodeId) { + // Message is from one of the child nodes. Add it to routing table. + hwWriteConfig(EEPROM_ROUTES_ADDRESS+sender, last); + } + + // Check if sender requests an ack back. + if (mGetRequestAck(_msg)) { + // Copy message + _msgTmp = _msg; + mSetRequestAck(_msgTmp,false); // Reply without ack flag (otherwise we would end up in an eternal loop) + mSetAck(_msgTmp,true); + _msgTmp.sender = _nc.nodeId; + _msgTmp.destination = _msg.sender; + sendRoute(_msgTmp); + } + + if (command == C_INTERNAL) { + if (type == I_FIND_PARENT_RESPONSE) { + if (_autoFindParent) { + // We've received a reply to a FIND_PARENT message. Check if the distance is + // shorter than we already have. + uint8_t distance = _msg.getByte(); + if (isValidDistance(distance)) + { + // Distance to gateway is one more for us w.r.t. parent + distance++; + if (isValidDistance(distance) && (distance < _nc.distance)) { + // Found a neighbor closer to GW than previously found + _nc.distance = distance; + _nc.parentNodeId = _msg.sender; + hwWriteConfig(EEPROM_PARENT_NODE_ID_ADDRESS, _nc.parentNodeId); + hwWriteConfig(EEPROM_DISTANCE_ADDRESS, _nc.distance); + debug(PSTR("parent=%d, d=%d\n"), _nc.parentNodeId, _nc.distance); + } + } + } + return; + #ifdef MY_SIGNING_FEATURE + } else if (type == I_GET_NONCE) { + if (signer.getNonce(_msg)) { + sendRoute(build(_msg, _nc.nodeId, _msg.sender, NODE_SENSOR_ID, C_INTERNAL, I_GET_NONCE_RESPONSE, false)); + } + return false; // Nonce exchange is an internal MySensor protocol message, no need to inform caller about this + } else if (type == I_REQUEST_SIGNING) { + if (_msg.getBool()) { + // We received an indicator that the sender require us to sign all messages we send to it + SET_SIGN(_msg.sender); + } else { + // We received an indicator that the sender does not require us to sign all messages we send to it + CLEAR_SIGN(_msg.sender); + } + // Save updated table + hwWriteConfigBlock((void*)_doSign, (void*)EEPROM_SIGNING_REQUIREMENT_TABLE_ADDRESS, sizeof(_doSign)); + + // Inform sender about our preference if we are a gateway, but only require signing if the sender required signing + // We do not currently want a gateway to require signing from all nodes in a network just because it wants one node + // to sign it's messages + if (MY_GATEWAY_FEATURE) { + if (signer.requestSignatures() && DO_SIGN(_msg.sender)) + sendRoute(build(_msg, _nc.nodeId, _msg.sender, NODE_SENSOR_ID, C_INTERNAL, I_REQUEST_SIGNING, false).set(true)); + else + sendRoute(build(_msg, _nc.nodeId, _msg.sender, NODE_SENSOR_ID, C_INTERNAL, I_REQUEST_SIGNING, false).set(false)); + } + return; // Signing request is an internal MySensor protocol message, no need to inform caller about this + } else if (type == I_GET_NONCE_RESPONSE) { + return; // Just pass along nonce silently (no need to call callback for these) + #endif + } else if (sender == GATEWAY_ADDRESS) { + if (type == I_ID_RESPONSE && _nc.nodeId == AUTO) { + _nc.nodeId = _msg.getByte(); + if (_nc.nodeId == AUTO) { + // sensor net gateway will return max id if all sensor id are taken + debug(PSTR("full\n")); + while (1); // Wait here. Nothing else we can do... + } + setupNode(); + // Write id to EEPROM + hwWriteConfig(EEPROM_NODE_ID_ADDRESS, _nc.nodeId); + debug(PSTR("id=%d\n"), _nc.nodeId); + } else { + processInternalMessages(); + } + return; + } + } + #ifdef MY_OTA_FIRMWARE_FEATURE + else if (command == C_STREAM) { + if (type == ST_FIRMWARE_CONFIG_RESPONSE) { + NodeFirmwareConfig *firmwareConfigResponse = (NodeFirmwareConfig *)_msg.data; + // compare with current node configuration, if they differ, start fw fetch process + if (memcmp(&_fc,firmwareConfigResponse,sizeof(NodeFirmwareConfig))) { + debug(PSTR("fw update\n")); + // copy new FW config + memcpy(&_fc,firmwareConfigResponse,sizeof(NodeFirmwareConfig)); + // Init flash + if (!flash.initialize()) { + debug(PSTR("flash init fail\n")); + _fwUpdateOngoing = false; + } else { + // erase lower 32K -> max flash size for ATMEGA328 + flash.blockErase32K(0); + // wait until flash erased + while ( flash.busy() ); + _fwBlock = _fc.blocks; + _fwUpdateOngoing = true; + // reset flags + _fwRetry = MY_OTA_RETRY+1; + _fwLastRequestTime = 0; + } + return ; + } else debug(PSTR("fw update skipped\n")); + } else if (type == ST_FIRMWARE_RESPONSE) { + if (_fwUpdateOngoing) { + // Save block to flash + debug(PSTR("fw block %d\n"), _fwBlock); + // extract FW block + ReplyFWBlock *firmwareResponse = (ReplyFWBlock *)_msg.data; + // write to flash + flash.writeBytes( ((_fwBlock - 1) * FIRMWARE_BLOCK_SIZE) + FIRMWARE_START_OFFSET, firmwareResponse->data, FIRMWARE_BLOCK_SIZE); + // wait until flash written + while ( flash.busy() ); + _fwBlock--; + if (!_fwBlock) { + // We're finished! Do a checksum and reboot. + _fwUpdateOngoing = false; + if (isValidFirmware()) { + debug(PSTR("fw checksum ok\n")); + // All seems ok, write size and signature to flash (DualOptiboot will pick this up and flash it) + uint16_t fwsize = FIRMWARE_BLOCK_SIZE * _fc.blocks; + uint8_t OTAbuffer[10] = {'F','L','X','I','M','G',':',(fwsize >> 8),fwsize,':'}; + flash.writeBytes(0, OTAbuffer, 10); + // Write the new firmware config to eeprom + hwWriteConfigBlock((void*)&_fc, (void*)EEPROM_FIRMWARE_TYPE_ADDRESS, sizeof(NodeFirmwareConfig)); + hwReboot(); + } else { + debug(PSTR("fw checksum fail\n")); + } + } + // reset flags + _fwRetry = MY_OTA_RETRY+1; + _fwLastRequestTime = 0; + } else { + debug(PSTR("No fw update ongoing\n")); + } + return; + } + + } + #endif + // Call incoming message callback if available + if (_msgCallback != NULL) { + _msgCallback(_msg); + } + return; + } else if (MY_IS_REPEATER && _nc.nodeId != AUTO) { + // If this node have an id, relay the message + if (command == C_INTERNAL && type == I_FIND_PARENT) { + if (sender != _nc.parentNodeId) { + if (_nc.distance == DISTANCE_INVALID) + findParentNode(); + + if (_nc.distance != DISTANCE_INVALID) { + // Relaying nodes should always answer ping messages + // Wait a random delay of 0-2 seconds to minimize collision + // between ping ack messages from other relaying nodes + wait(hwMillis() & 0x3ff); + sendWrite(sender, build(_msg, _nc.nodeId, sender, NODE_SENSOR_ID, C_INTERNAL, I_FIND_PARENT_RESPONSE, false).set(_nc.distance)); + } + } + } else if (to == _nc.nodeId) { + // We should try to relay this message to another node + sendRoute(_msg); + } + } + #endif } + +void processInternalMessages() { + bool isMetric; + uint8_t type = _msg.type; + + if (type == I_REBOOT) { + // Requires MySensors or other bootloader with watchdogs enabled + hwReboot(); + } else if (type == I_CONFIG) { + // Pick up configuration from controller (currently only metric/imperial) + // and store it in eeprom if changed + isMetric = _msg.getString()[0] == 'M' ; + _cc.isMetric = isMetric; + hwWriteConfig(EEPROM_CONTROLLER_CONFIG_ADDRESS, isMetric); + } else if (type == I_CHILDREN) { + if (MY_IS_REPEATER && _msg.getString()[0] == 'C') { + // Clears child relay data for this node + debug(PSTR("clear\n")); + uint8_t i = 255; + do { + hwWriteConfig(EEPROM_ROUTES_ADDRESS+i, 0xff); + } while (i--); + // Clear parent node id & distance to gw + hwWriteConfig(EEPROM_PARENT_NODE_ID_ADDRESS, 0xFF); + hwWriteConfig(EEPROM_DISTANCE_ADDRESS, 0xFF); + // Find parent node + findParentNode(); + sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_CHILDREN,false).set("")); + } + } else if (type == I_TIME) { + if (_timeCallback != NULL) { + // Deliver time to callback + _timeCallback(_msg.getULong()); + } + } +} + + +#if defined (MY_REPEATER_FEATURE) +void saveState(uint8_t pos, uint8_t value) { + hwWriteConfig(EEPROM_LOCAL_CONFIG_ADDRESS+pos, value); +} +uint8_t loadState(uint8_t pos) { + return hwReadConfig(EEPROM_LOCAL_CONFIG_ADDRESS+pos); +} +#endif + +void wait(unsigned long ms) { + unsigned long enter = hwMillis(); + while (hwMillis() - enter < ms) { + process(); + } +} + +void sleep(unsigned long ms) { + #if defined(MY_OTA_FIRMWARE_FEATURE) + if (_fwUpdateOngoing) { + // Do not sleep node while fw update is ongoing + process(); + } else { + #endif + #if defined(MY_RADIO_FEATURE) + transport.powerDown(); + #endif + hwSleep(ms); + #if defined(MY_OTA_FIRMWARE_FEATURE) + } + #endif +} + +bool sleep(uint8_t interrupt, uint8_t mode, unsigned long ms) { + #if defined(MY_OTA_FIRMWARE_FEATURE) + if (_fwUpdateOngoing) { + // Do not sleep node while fw update is ongoing + process(); + return false; + } else { + #endif + #if defined(MY_RADIO_FEATURE) + transport.powerDown(); + #endif + return hwSleep(interrupt, mode, ms) ; + #if defined(MY_OTA_FIRMWARE_FEATURE) + } + #endif +} + +int8_t sleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2, unsigned long ms) { + #if defined(MY_OTA_FIRMWARE_FEATURE) + if (_fwUpdateOngoing) { + // Do not sleep node while fw update is ongoing + process(); + return -1; + } else { + #endif + #if defined(MY_RADIO_FEATURE) + transport.powerDown(); + #endif + return hwSleep(interrupt1, mode1, interrupt2, mode2, ms) ; + #if defined(MY_OTA_FIRMWARE_FEATURE) + } + #endif +} + + #ifdef MY_INCLUSION_MODE_FEATURE -void MyGateway::setInclusionMode(boolean newMode) { +void setInclusionMode(boolean newMode) { if (newMode != inclusionMode) { inclusionMode = newMode; // Send back mode change to controller - transport.send(buildGw(msg, I_INCLUSION_MODE).set(inclusionMode?1:0)); + gatewayTransportSend(buildGw(_msg, I_INCLUSION_MODE).set(inclusionMode?1:0)); if (inclusionMode) { - inclusionStartTime = hw_millis(); + inclusionStartTime = hwMillis(); } } } -void MyGateway::checkInclusionMode() { - +void checkInclusionMode() { #ifdef MY_INCLUSION_BUTTON_FEATURE if (!inclusionMode && digitalRead(MY_INCLUSION_MODE_BUTTON_PIN) == LOW) { // Start inclusion mode @@ -1026,7 +1049,7 @@ void MyGateway::checkInclusionMode() { } #endif - if (inclusionMode && hw_millis()-inclusionStartTime>MY_INCLUSION_MODE_DURATION*1000L) { + if (inclusionMode && hwMillis()-inclusionStartTime>MY_INCLUSION_MODE_DURATION*1000L) { // inclusionTimeInMinutes minute(s) has passed.. stop inclusion mode setInclusionMode(false); } diff --git a/libraries/MySensors/core/MySensorCore.h b/libraries/MySensors/core/MySensorCore.h index 5636b29..6deb634 100644 --- a/libraries/MySensors/core/MySensorCore.h +++ b/libraries/MySensors/core/MySensorCore.h @@ -23,9 +23,7 @@ #include "Version.h" // Auto generated by bot #include "MyConfig.h" -#include "MyHw.h" #include "MyTransport.h" -#include "MyTransportNRF24.h" #ifdef MY_SIGNING_FEATURE #include "MySigning.h" #include "MySigningNone.h" @@ -35,25 +33,14 @@ #include "drivers/SPIFlash/SPIFlash.h" #endif -#include "MyGatewayTransport.h" -#include "MyGatewayTransportSerial.h" - #include #include -// Set the hardware driver to use (initialized by MySensor-class) -#if defined(ARDUINO_ARCH_ESP8266) -#include "MyHwESP8266.h" -typedef MyHwESP8266 MyHwDriver; -#elif defined(ARDUINO_ARCH_AVR) -#include "MyHwATMega328.h" -typedef MyHwATMega328 MyHwDriver; -#endif #ifdef MY_DEBUG -#define debug(x,...) hw.debugPrint(isGateway, x, ##__VA_ARGS__) +#define debug(x,...) hwDebugPrint(x, ##__VA_ARGS__) #else #define debug(x,...) #endif @@ -146,179 +133,165 @@ typedef struct { } __attribute__((packed)) ReplyFWBlock; -#ifdef __cplusplus -class MySensor -{ - public: - /** - * Constructor - * - * Creates a new instance of Sensor class. - * - */ - MySensor(MyTransport &radio =*new MyTransportNRF24(), MyHw &hw=*new MyHwDriver() -#ifdef MY_SIGNING_FEATURE - , MySigning &signer=*new MySigningNone() -#endif - ); - /** - * Begin operation of the MySensors library - * - * Call this in setup(), before calling any other sensor net library methods. - * @param incomingMessageCallback Callback function for incoming messages from other nodes or controller and request responses. Default is NULL. - * @param nodeId The unique id (1-254) for this sensor. Default is AUTO(255) which means sensor tries to fetch an id from controller. - * @param repeaterMode Activate repeater mode. This node will forward messages to other nodes in the radio network. Make sure to call process() regularly. Default in false - * @param parentNodeId Use this to force node to always communicate with a certain parent node. Default is AUTO which means node automatically tries to find a parent. - */ - - void begin(void (* msgCallback)(const MyMessage &)=NULL, uint8_t nodeId=AUTO, boolean repeaterMode=false, uint8_t parentNodeId=AUTO); - - /** - * Return the nodes nodeId. - */ - uint8_t getNodeId(); - - /** - * Each node must present all attached sensors before any values can be handled correctly by the controller. - * It is usually good to present all attached sensors after power-up in setup(). - * - * @param sensorId Select a unique sensor id for this sensor. Choose a number between 0-254. - * @param sensorType The sensor type. See sensor typedef in MyMessage.h. - * @param description A textual description of the sensor. - * @param ack Set this to true if you want destination node to send ack back to this node. Default is not to request any ack. - * @param description A textual description of the sensor. - */ - void present(uint8_t sensorId, uint8_t sensorType, const char *description="", bool ack=false); - - /** - * Sends sketch meta information to the gateway. Not mandatory but a nice thing to do. - * @param name String containing a short Sketch name or NULL if not applicable - * @param version String containing a short Sketch version or NULL if not applicable - * @param ack Set this to true if you want destination node to send ack back to this node. Default is not to request any ack. - * - */ - void sendSketchInfo(const char *name, const char *version, bool ack=false); - - /** - * Sends a message to gateway or one of the other nodes in the radio network - * - * @param msg Message to send - * @param ack Set this to true if you want destination node to send ack back to this node. Default is not to request any ack. - * @return true Returns true if message reached the first stop on its way to destination. - */ - bool send(MyMessage &msg, bool ack=false); +/** +* Begin operation of the MySensors library +* +* Call this in setup(), before calling any other sensor net library methods. +* @param incomingMessageCallback Callback function for incoming messages from other nodes or controller and request responses. Default is NULL. +*/ - /** - * Send this nodes battery level to gateway. - * @param level Level between 0-100(%) - * @param ack Set this to true if you want destination node to send ack back to this node. Default is not to request any ack. - * - */ - void sendBatteryLevel(uint8_t level, bool ack=false); +void dataCallback(void (* msgCallback)(const MyMessage &)=NULL); - /** - * Send a heartbeat message (I'm alive!) to the gateway/controller. - * The payload will be an incremental 16 bit integer value starting at 1 when sensor is powered on. - */ - void sendHeartbeat(void); +/** + * Return the nodes nodeId. + */ +uint8_t getNodeId(); - /** - * Requests a value from gateway or some other sensor in the radio network. - * Make sure to add callback-method in begin-method to handle request responses. - * - * @param childSensorId The unique child id for the different sensors connected to this Arduino. 0-254. - * @param variableType The variableType to fetch - * @param destination The nodeId of other node in radio network. Default is gateway - */ - void request(uint8_t childSensorId, uint8_t variableType, uint8_t destination=GATEWAY_ADDRESS); +/** +* Each node must present all attached sensors before any values can be handled correctly by the controller. +* It is usually good to present all attached sensors after power-up in setup(). +* +* @param sensorId Select a unique sensor id for this sensor. Choose a number between 0-254. +* @param sensorType The sensor type. See sensor typedef in MyMessage.h. +* @param description A textual description of the sensor. +* @param ack Set this to true if you want destination node to send ack back to this node. Default is not to request any ack. +* @param description A textual description of the sensor. +*/ +void present(uint8_t sensorId, uint8_t sensorType, const char *description="", bool ack=false); - /** - * Requests time from controller. Answer will be delivered to callback. - * - * @param callback for time request. Incoming argument is seconds since 1970. - */ - void requestTime(void (* timeCallback)(unsigned long)); +/** + * Sends sketch meta information to the gateway. Not mandatory but a nice thing to do. + * @param name String containing a short Sketch name or NULL if not applicable + * @param version String containing a short Sketch version or NULL if not applicable + * @param ack Set this to true if you want destination node to send ack back to this node. Default is not to request any ack. + * + */ +void sendSketchInfo(const char *name, const char *version, bool ack=false); + +/** +* Sends a message to gateway or one of the other nodes in the radio network +* +* @param msg Message to send +* @param ack Set this to true if you want destination node to send ack back to this node. Default is not to request any ack. +* @return true Returns true if message reached the first stop on its way to destination. +*/ +bool send(MyMessage &msg, bool ack=false); - /** - * Processes incoming messages to this node. If this is a relaying node it will - * Returns true if there is a message addressed for this node just was received. - * Use callback to handle incoming messages. - */ - boolean process(); +/** + * Send this nodes battery level to gateway. + * @param level Level between 0-100(%) + * @param ack Set this to true if you want destination node to send ack back to this node. Default is not to request any ack. + * + */ +void sendBatteryLevel(uint8_t level, bool ack=false); - /** - * Returns the most recent node configuration received from controller - */ - ControllerConfig getConfig(); +/** + * Send a heartbeat message (I'm alive!) to the gateway/controller. + * The payload will be an incremental 16 bit integer value starting at 1 when sensor is powered on. + */ +void sendHeartbeat(void); - /** - * Save a state (in local EEPROM). Good for actuators to "remember" state between - * power cycles. - * - * You have 256 bytes to play with. Note that there is a limitation on the number - * of writes the EEPROM can handle (~100 000 cycles on ATMega328). - * - * @param pos The position to store value in (0-255) - * @param Value to store in position - */ - void saveState(uint8_t pos, uint8_t value); +/** +* Requests a value from gateway or some other sensor in the radio network. +* Make sure to add callback-method in begin-method to handle request responses. +* +* @param childSensorId The unique child id for the different sensors connected to this Arduino. 0-254. +* @param variableType The variableType to fetch +* @param destination The nodeId of other node in radio network. Default is gateway +*/ +void request(uint8_t childSensorId, uint8_t variableType, uint8_t destination=GATEWAY_ADDRESS); - /** - * Load a state (from local EEPROM). - * - * @param pos The position to fetch value from (0-255) - * @return Value to store in position - */ - uint8_t loadState(uint8_t pos); - - /** - * Returns the last received message - */ - MyMessage& getLastMessage(void); +/** + * Requests time from controller. Answer will be delivered to callback. + * + * @param callback for time request. Incoming argument is seconds since 1970. + */ +void requestTime(void (* timeCallback)(unsigned long)); - /** - * Wait for a specified amount of time to pass. Keeps process()ing. - * This does not power-down the radio nor the Arduino. - * Because this calls process() in a loop, it is a good way to wait - * in your loop() on a repeater node or sensor that listens to messages. - * @param ms Number of milliseconds to sleep. - */ - void wait(unsigned long ms); +/** + * Returns the most recent node configuration received from controller + */ +ControllerConfig getConfig(); - /** - * Sleep (PowerDownMode) the MCU and radio. Wake up on timer. - * @param ms Number of milliseconds to sleep. - */ - void sleep(unsigned long ms); +/** + * Save a state (in local EEPROM). Good for actuators to "remember" state between + * power cycles. + * + * You have 256 bytes to play with. Note that there is a limitation on the number + * of writes the EEPROM can handle (~100 000 cycles on ATMega328). + * + * @param pos The position to store value in (0-255) + * @param Value to store in position + */ +void saveState(uint8_t pos, uint8_t value); - /** - * Sleep (PowerDownMode) the MCU and radio. Wake up on timer or pin change. - * See: http://arduino.cc/en/Reference/attachInterrupt for details on modes and which pin - * is assigned to what interrupt. On Nano/Pro Mini: 0=Pin2, 1=Pin3 - * @param interrupt Interrupt that should trigger the wakeup - * @param mode RISING, FALLING, CHANGE - * @param ms Number of milliseconds to sleep or 0 to sleep forever - * @return true if wake up was triggered by pin change and false means timer woke it up. - */ - bool sleep(uint8_t interrupt, uint8_t mode, unsigned long ms=0); +/** + * Load a state (from local EEPROM). + * + * @param pos The position to fetch value from (0-255) + * @return Value to store in position + */ +uint8_t loadState(uint8_t pos); - /** - * Sleep (PowerDownMode) the MCU and radio. Wake up on timer or pin change for two separate interrupts. - * See: http://arduino.cc/en/Reference/attachInterrupt for details on modes and which pin - * is assigned to what interrupt. On Nano/Pro Mini: 0=Pin2, 1=Pin3 - * @param interrupt1 First interrupt that should trigger the wakeup - * @param mode1 Mode for first interrupt (RISING, FALLING, CHANGE) - * @param interrupt2 Second interrupt that should trigger the wakeup - * @param mode2 Mode for second interrupt (RISING, FALLING, CHANGE) - * @param ms Number of milliseconds to sleep or 0 to sleep forever - * @return Interrupt number wake up was triggered by pin change and negative if timer woke it up. - */ - int8_t sleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2, unsigned long ms=0); +/** +* Returns the last received message +*/ +MyMessage& getLastMessage(void); + + + +/** + * Wait for a specified amount of time to pass. Keeps process()ing. + * This does not power-down the radio nor the Arduino. + * Because this calls process() in a loop, it is a good way to wait + * in your loop() on a repeater node or sensor that listens to messages. + * @param ms Number of milliseconds to sleep. + */ +void wait(unsigned long ms); + +/** + * Sleep (PowerDownMode) the MCU and radio. Wake up on timer. + * @param ms Number of milliseconds to sleep. + */ +void sleep(unsigned long ms); + +/** + * Sleep (PowerDownMode) the MCU and radio. Wake up on timer or pin change. + * See: http://arduino.cc/en/Reference/attachInterrupt for details on modes and which pin + * is assigned to what interrupt. On Nano/Pro Mini: 0=Pin2, 1=Pin3 + * @param interrupt Interrupt that should trigger the wakeup + * @param mode RISING, FALLING, CHANGE + * @param ms Number of milliseconds to sleep or 0 to sleep forever + * @return true if wake up was triggered by pin change and false means timer woke it up. + */ +bool sleep(uint8_t interrupt, uint8_t mode, unsigned long ms=0); + +/** + * Sleep (PowerDownMode) the MCU and radio. Wake up on timer or pin change for two separate interrupts. + * See: http://arduino.cc/en/Reference/attachInterrupt for details on modes and which pin + * is assigned to what interrupt. On Nano/Pro Mini: 0=Pin2, 1=Pin3 + * @param interrupt1 First interrupt that should trigger the wakeup + * @param mode1 Mode for first interrupt (RISING, FALLING, CHANGE) + * @param interrupt2 Second interrupt that should trigger the wakeup + * @param mode2 Mode for second interrupt (RISING, FALLING, CHANGE) + * @param ms Number of milliseconds to sleep or 0 to sleep forever + * @return Interrupt number wake up was triggered by pin change and negative if timer woke it up. + */ +int8_t sleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2, unsigned long ms=0); + + + +/****** PRIVATE ********/ +void begin(); + +void process(void); + +void processInternalMessages(); #ifdef MY_LEDS_BLINKING_FEATURE /** @@ -328,121 +301,31 @@ class MySensor void rxBlink(uint8_t cnt); void txBlink(uint8_t cnt); void errBlink(uint8_t cnt); -#endif - - boolean sendRoute(MyMessage &message); - - protected: - NodeConfig nc; // Essential settings for node to work - ControllerConfig cc; // Configuration coming from controller -#ifdef MY_OTA_FIRMWARE_FEATURE - NodeFirmwareConfig fc; - bool fwUpdateOngoing; - unsigned long fwLastRequestTime; - uint16_t fwBlock; - uint8_t fwRetry; - SPIFlash flash; -#endif - - bool repeaterMode; - bool autoFindParent; - bool isGateway; - - MyMessage msg; // Buffer for incoming messages. - MyMessage tmpMsg ; // Buffer for temporary messages (acks and nonces among others). - -#ifdef MY_LEDS_BLINKING_FEATURE - // these variables don't need to be volatile, since we are not using interrupts - uint8_t countRx; - uint8_t countTx; - uint8_t countErr; - void handleLedsBlinking(); // do the actual blinking - unsigned long blink_next_time; #endif - MyTransport& radio; -#ifdef MY_SIGNING_FEATURE - uint16_t doSign[16]; // Bitfield indicating which sensors require signed communication - MyMessage msgSign; // Buffer for message to sign. - MySigning& signer; -#endif - MyHw& hw; - - boolean sendWrite(uint8_t dest, MyMessage &message); - void processInternalMessages(MyMessage &msg); -#ifdef MY_DEBUG - char convBuf[MAX_PAYLOAD*2+1]; -#endif - uint8_t failedTransmissions; - uint16_t heartbeat; - void (*timeCallback)(unsigned long); // Callback for requested time messages - void (*msgCallback)(const MyMessage &); // Callback for incoming messages from other nodes and gateway. - #ifdef MY_OTA_FIRMWARE_FEATURE -// do a crc16 on the whole received firmware - bool isValidFirmware(); + // do a crc16 on the whole received firmware + bool isValidFirmware(); #endif - private: - - void requestNodeId(); - void setupNode(); - void findParentNode(); - uint8_t crc8Message(MyMessage &message); -}; -#endif - - -class MyGateway : public MySensor -{ -public: - // MyProtocolSerial constructor - MyGateway(MyGatewayTransport &transport=*new MyGatewayTransportSerial(), MyTransport &radio =*new MyTransportNRF24(), MyHw &hw=*new MyHwDriver() -#ifdef MY_SIGNING_FEATURE - , MySigning &signer=*new MySigningNone() -#endif - ); - - - /** - * Begin operation of the MySensors library - * - * Call this in setup(), before calling any other sensor net library methods. - * @param incomingMessageCallback Callback function for incoming messages from other nodes or controller and request responses. Default is NULL. - * @param inclusionModeDuration Number of milliseconds inclusion mode should be enabled - * @param inclusionModeButtonPin Button pin used for enabling inclusion mode - */ - - void begin(void (* msgCallback)(const MyMessage &)=NULL); - - - /** - * See MySensor.h - * - * Overloaded to handle local sensor data sent to controller. - */ - boolean sendRoute(MyMessage &message); - - /** - * Processes incoming messages to this node. - * Returns true if there is a message addressed for this node was received. - * Preferred way of handling incoming messages is to use the callback defined in begin(). - */ - boolean process(); - -private: - - MyGatewayTransport& transport; #ifdef MY_INCLUSION_MODE_FEATURE - void setInclusionMode(bool newMode); void checkInclusionMode(); unsigned long inclusionStartTime; bool inclusionMode; #endif -}; + + +boolean sendRoute(MyMessage &message); +#if defined(MY_RADIO_FEATURE) + boolean sendWrite(uint8_t dest, MyMessage &message); + void requestNodeId(); + void setupNode(); + void findParentNode(); +#endif + #endif diff --git a/libraries/MySensors/core/MyTransportNRF24.cpp b/libraries/MySensors/core/MyTransportNRF24.cpp index d11545d..5affb12 100644 --- a/libraries/MySensors/core/MyTransportNRF24.cpp +++ b/libraries/MySensors/core/MyTransportNRF24.cpp @@ -20,11 +20,10 @@ #include "MyTransport.h" #include "MyTransportNRF24.h" -MyTransportNRF24::MyTransportNRF24(uint8_t ce, uint8_t cs, uint8_t paLevel) +MyTransportNRF24::MyTransportNRF24() : MyTransport(), - rf24(ce, cs), - _paLevel(paLevel) + rf24(MY_RF24_CE_PIN, MY_RF24_CS_PIN) { } @@ -38,9 +37,9 @@ bool MyTransportNRF24::init() { rf24.setAutoAck(1); rf24.setAutoAck(BROADCAST_PIPE,false); // Turn off auto ack for broadcast rf24.enableAckPayload(); - rf24.setChannel(RF24_CHANNEL); + rf24.setChannel(MY_RF24_CHANNEL); rf24.setPALevel(_paLevel); - rf24.setDataRate(RF24_DATARATE); + rf24.setDataRate(MY_RF24_DATARATE); rf24.setRetries(5,15); rf24.setCRCLength(RF24_CRC_16); rf24.enableDynamicPayloads(); diff --git a/libraries/MySensors/core/MyTransportNRF24.h b/libraries/MySensors/core/MyTransportNRF24.h index 535186d..d8a7b81 100644 --- a/libraries/MySensors/core/MyTransportNRF24.h +++ b/libraries/MySensors/core/MyTransportNRF24.h @@ -26,7 +26,7 @@ #include "drivers/RF24/RF24.h" #include "drivers/RF24/RF24_config.h" -#define TO_ADDR(x) (RF24_BASE_RADIO_ID + x) +#define TO_ADDR(x) (MY_RF24_BASE_RADIO_ID + x) #define WRITE_PIPE ((uint8_t)0) #define CURRENT_NODE_PIPE ((uint8_t)1) @@ -35,7 +35,7 @@ class MyTransportNRF24 : public MyTransport { public: - MyTransportNRF24(uint8_t ce=RF24_CE_PIN, uint8_t cs=RF24_CS_PIN, uint8_t paLevel=RF24_PA_LEVEL); + MyTransportNRF24(); bool init(); void setAddress(uint8_t address); uint8_t getAddress(); diff --git a/libraries/MySensors/core/MyTransportRFM69.cpp b/libraries/MySensors/core/MyTransportRFM69.cpp index ca29585..30e923e 100644 --- a/libraries/MySensors/core/MyTransportRFM69.cpp +++ b/libraries/MySensors/core/MyTransportRFM69.cpp @@ -20,18 +20,16 @@ #include "MyTransport.h" #include "MyTransportRFM69.h" -MyTransportRFM69::MyTransportRFM69(uint8_t freqBand, uint8_t networkId, uint8_t slaveSelectPin, uint8_t interruptPin, bool isRFM69HW, uint8_t interruptNum) +MyTransportRFM69::MyTransportRFM69() : MyTransport(), - radio(slaveSelectPin, interruptPin, isRFM69HW, interruptNum), - _freqBand(freqBand), - _networkId(networkId) + radio(MY_RF69_SPI_CS, MY_RF69_IRQ_PIN, MY_IS_RFM69HW, MY_RF69_IRQ_NUM) { } bool MyTransportRFM69::init() { // Start up the radio library (_address will be set later by the MySensors library) - radio.initialize(_freqBand, _address, _networkId); + radio.initialize(MY_RFM69_FREQUENCY, _address, MY_RFM69_NETWORKID); #ifdef MY_RFM69_ENABLE_ENCRYPTION radio.encrypt(RFM69_ENCRYPTKEY); #endif diff --git a/libraries/MySensors/core/MyTransportRFM69.h b/libraries/MySensors/core/MyTransportRFM69.h index affbb25..1336d1e 100644 --- a/libraries/MySensors/core/MyTransportRFM69.h +++ b/libraries/MySensors/core/MyTransportRFM69.h @@ -30,7 +30,7 @@ class MyTransportRFM69 : public MyTransport { public: - MyTransportRFM69(uint8_t freqBand=MY_RFM69_FREQUENCY, uint8_t networkId=MY_RFM69_NETWORKID, uint8_t slaveSelectPin=MY_RF69_SPI_CS, uint8_t interruptPin=MY_RF69_IRQ_PIN, bool isRFM69HW=false, uint8_t interruptNum=MY_RF69_IRQ_NUM); + MyTransportRFM69(); bool init(); void setAddress(uint8_t address); uint8_t getAddress(); @@ -41,8 +41,6 @@ public: private: RFM69 radio; uint8_t _address; - uint8_t _freqBand; - uint8_t _networkId; }; #endif diff --git a/libraries/MySensors/drivers/DigitalIO/DigitalIO.h b/libraries/MySensors/drivers/DigitalIO/DigitalIO.h index 8f47bf1..f984ef8 100644 --- a/libraries/MySensors/drivers/DigitalIO/DigitalIO.h +++ b/libraries/MySensors/drivers/DigitalIO/DigitalIO.h @@ -28,9 +28,9 @@ /** DigitalPin version YYYYMMDD */ #define DIGITAL_IO_VERSION 20140217 //------------------------------------------------------------------------------ -#include -#include -#include -#include -#include -#endif // DigitalIO_h \ No newline at end of file +#include "DigitalPin.h" +#include "I2cConstants.h" +#include "PinIO.h" +#include "SoftI2cMaster.h" +#include "SoftSPI.h" +#endif // DigitalIO_h diff --git a/libraries/MySensors/drivers/DigitalIO/PinIO.cpp b/libraries/MySensors/drivers/DigitalIO/PinIO.cpp index b0367b7..5f1ff52 100644 --- a/libraries/MySensors/drivers/DigitalIO/PinIO.cpp +++ b/libraries/MySensors/drivers/DigitalIO/PinIO.cpp @@ -25,7 +25,7 @@ * @details Two Wire Interface library. * @{ */ -#include +#include "PinIO.h" #include #include //============================================================================== @@ -65,4 +65,4 @@ void PinIO::config(bool mode, bool data) { writeI(data); } } -/** @} */ \ No newline at end of file +/** @} */ diff --git a/libraries/MySensors/drivers/DigitalIO/SoftI2cMaster.cpp b/libraries/MySensors/drivers/DigitalIO/SoftI2cMaster.cpp index a698636..d02b2bb 100644 --- a/libraries/MySensors/drivers/DigitalIO/SoftI2cMaster.cpp +++ b/libraries/MySensors/drivers/DigitalIO/SoftI2cMaster.cpp @@ -25,7 +25,7 @@ * @details Software Two Wire Interface library. * @{ */ -#include +#include "SoftI2cMaster.h" //------------------------------------------------------------------------------ /** * Start an I2C transfer with possible continuation. diff --git a/libraries/MySensors/drivers/DigitalIO/SoftI2cMaster.h b/libraries/MySensors/drivers/DigitalIO/SoftI2cMaster.h index 7bf3c00..33e11fc 100644 --- a/libraries/MySensors/drivers/DigitalIO/SoftI2cMaster.h +++ b/libraries/MySensors/drivers/DigitalIO/SoftI2cMaster.h @@ -33,8 +33,8 @@ #include #endif // ARDUINO #include -#include -#include +#include "DigitalPin.h" +#include "I2cConstants.h" //------------------------------------------------------------------------------ // State codes. diff --git a/libraries/MySensors/drivers/DigitalIO/SoftSPI.h b/libraries/MySensors/drivers/DigitalIO/SoftSPI.h index 036fad6..f51f210 100644 --- a/libraries/MySensors/drivers/DigitalIO/SoftSPI.h +++ b/libraries/MySensors/drivers/DigitalIO/SoftSPI.h @@ -28,7 +28,7 @@ #ifndef SoftSPI_h #define SoftSPI_h -#include +#include "DigitalPin.h" //------------------------------------------------------------------------------ /** Nop for timing. */ #define nop asm volatile ("nop\n\t") diff --git a/libraries/MySensors/drivers/RF24/RF24.cpp b/libraries/MySensors/drivers/RF24/RF24.cpp index 16bff15..d78c875 100644 --- a/libraries/MySensors/drivers/RF24/RF24.cpp +++ b/libraries/MySensors/drivers/RF24/RF24.cpp @@ -19,7 +19,7 @@ void RF24::csn(bool mode) // divider of 4 is the minimum we want. // CLK:BUS 8Mhz:2Mhz, 16Mhz:4Mhz, or 20Mhz:5Mhz #ifdef ARDUINO - #if ( !defined(RF24_TINY) && !defined (__arm__) && !defined (SOFTSPI)) || defined (CORE_TEENSY) + #if ( !defined(RF24_TINY) && !defined (__arm__) && !defined (MY_SOFTSPI)) || defined (CORE_TEENSY) _SPI.setBitOrder(MSBFIRST); _SPI.setDataMode(SPI_MODE0); _SPI.setClockDivider(SPI_CLOCK_DIV2); diff --git a/libraries/MySensors/drivers/RF24/RF24_config.h b/libraries/MySensors/drivers/RF24/RF24_config.h index d8c8016..08a23cc 100644 --- a/libraries/MySensors/drivers/RF24/RF24_config.h +++ b/libraries/MySensors/drivers/RF24/RF24_config.h @@ -26,7 +26,7 @@ //#define SERIAL_DEBUG #define MINIMAL //#define SPI_UART // Requires library from https://github.com/TMRh20/Sketches/tree/master/SPI_UART - //#define SOFTSPI // Requires library from https://github.com/greiman/DigitalIO + //#define MY_SOFTSPI // Requires library from https://github.com/greiman/DigitalIO /**********************/ // Define _BV for non-Arduino platforms and for Arduino DUE diff --git a/libraries/MySensors/examples/EthernetGateway/EthernetGateway.ino b/libraries/MySensors/examples/EthernetGateway/EthernetGateway.ino index c5e10a4..34ee906 100644 --- a/libraries/MySensors/examples/EthernetGateway/EthernetGateway.ino +++ b/libraries/MySensors/examples/EthernetGateway/EthernetGateway.ino @@ -56,15 +56,61 @@ * */ +// Enable debug prints to console +#define MY_DEBUG +// This node has a NR24L01+ radio attached +#define MY_RADIO_NRF24 + + +// W5100 Ethernet in client mode (node opens conmection to controller) +#define MY_GATEWAY_W5100_CLIENT +// W5100 Ethernet in client mode (controller opens conmection to node) +//#define MY_GATEWAY_W5100_SERVER + +// Set default gateway protocol (http://www.mysensors.org/download/serial_api_15) +#define MY_GATEWAY_PROTOCOL_DEFAULT + +// Enable Soft SPI for radio (note different radio wiring required) +// Useful for W5100 which sometimes have hard time co-operate with radio on the same spi bus. +//#define MY_SOFTSPI +//#define MY_SOFT_SPI_SCK_PIN 14 +//#define MY_SOFT_SPI_MISO_PIN 16 +//#define MY_SOFT_SPI_MOSI_PIN 15 + + +// Enable to use UDP +#define MY_USE_UDP + +#define MY_IP_ADDRESS 192,168,178,66 // If this is disabled, DHCP is used to retrieve address +// Renewal period if using DHCP +//#define MY_IP_RENEWAL_INTERVAL 60000 +// The port to keep open on node server mode / or port to contact in client mode +#define MY_PORT 5003 + +// Controller ip address (client mode) +#define MY_CONTROLLER_IP_ADDRESS 192, 168, 178, 254 +// The port you want to contact on controller (client mode) +#define MY_CONTROLLER_PORT 5003 + +// The MAC address can be anything you want but should be unique on your network. +// Newer boards have a MAC address printed on the underside of the PCB, which you can (optionally) use. +// Note that most of the Ardunio examples use "DEAD BEEF FEED" for the MAC address. +#define MY_MAC_ADDRESS 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED + +// Flash leds on rx/tx/err #define MY_WITH_LEDS_BLINKING -#define MY_INCLUSION_MODE_FEATURE -#define MY_INCLUSION_BUTTON_FEATURE - +// Set blinking period #define MY_DEFAULT_LED_BLINK_PERIOD 300 -#define MY_INCLUSION_MODE_DURATION 60 // Number of minutes inclusion mode is enabled -#define MY_INCLUSION_MODE_BUTTON_PIN 3 // Digital pin used for inclusion mode button +// Enable inclusion mode +#define MY_INCLUSION_MODE_FEATURE +// Enable Inclusion mode button on gateway +#define MY_INCLUSION_BUTTON_FEATURE +// Set inclusion mode duration (in seconds) +#define MY_INCLUSION_MODE_DURATION 60 +// Digital pin used for inclusion mode button +#define MY_INCLUSION_MODE_BUTTON_PIN 3 #define MY_DEFAULT_ERR_LED_PIN 7 // Error led pin #define MY_DEFAULT_RX_LED_PIN 8 // Receive led pin @@ -76,53 +122,11 @@ #include -#define MY_IP_PORT 5003 // The port you want to open -#ifndef MY_IP_ADDRESS_DHCP -// Gateway IP address if not using DHCP -IPAddress myIp (192, 168, 178, 66); // Configure your static ip-address here COMPILE ERROR HERE? Use Arduino IDE 1.5.7 or later! -#endif /* IP_ADDRESS_DHCP */ - -// NRFRF24L01 radio driver (set low transmit power by default) -MyTransportNRF24 transport(RADIO_CE_PIN, RADIO_SPI_SS_PIN, RF24_PA_LEVEL_GW); -//MyTransportRFM69 transport; - -// Message signing driver (signer needed if MY_SIGNING_FEATURE is turned on in MyConfig.h) -//MySigningNone signer; -//MySigningAtsha204Soft signer; -//MySigningAtsha204 signer; - -// Hardware profile -MyHwATMega328 hw; - -// Use default controller protocol -MyProtocolDefault protocol; - -// The MAC address can be anything you want but should be unique on your network. -// Newer boards have a MAC address printed on the underside of the PCB, which you can (optionally) use. -// Note that most of the Ardunio examples use "DEAD BEEF FEED" for the MAC address. -byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; // DEAD BEEF FEED - -// Controller IP -IPAddress controllerIP = IPAddress(192, 168, 178, 254); - -// Gateway transport driver -#ifdef IP_ADDRESS_DHCP -MyGatewayTransportEthernet ctrlTransport(protocol, mac, /* gw port */ CONTROLLER_PORT, IP_RENEWAL_INTERVAL, controllerIP, CONTROLLER_PORT); -#else -MyGatewayTransportEthernet ctrlTransport(protocol, mac, myIp, /* gw port */ CONTROLLER_PORT, controllerIP, CONTROLLER_PORT); -#endif - - -// Construct MyGateway library (signer needed if MY_SIGNING_FEATURE is turned on in MyConfig.h) -// To use LEDs blinking, uncomment MY_LEDS_BLINKING_FEATURE in MyConfig.h -MyGateway gw(ctrlTransport, transport, hw /*, signer*/); - void setup() { - gw.begin(NULL); + dataCallback(); } void loop() { - gw.process(); } diff --git a/libraries/MySensors/examples/MotionSensor/MotionSensor.ino b/libraries/MySensors/examples/MotionSensor/MotionSensor.ino index a0d734b..099226f 100644 --- a/libraries/MySensors/examples/MotionSensor/MotionSensor.ino +++ b/libraries/MySensors/examples/MotionSensor/MotionSensor.ino @@ -28,8 +28,14 @@ */ //#define MY_DEBUG +//#define MY_SOFTSPI +//#define MY_OTA_FIRMWARE_FEATURE + +#define MY_RADIO_NRF24 // MY_RADIO_RFM69 + +#include + -#include "MySensor.h" unsigned long SLEEP_TIME = 120000; // Sleep time between reports (in milliseconds) #define DIGITAL_INPUT_SENSOR 3 // The digital input you attached your motion sensor. (Only 2 and 3 generates interrupt!) @@ -43,14 +49,14 @@ MyMessage msg(CHILD_ID, V_TRIPPED); void setup() { - gw.begin(); + dataDallback(); // Send the sketch version information to the gateway and Controller - gw.sendSketchInfo("Motion Sensor", "1.0"); + sendSketchInfo("Motion Sensor", "1.0"); pinMode(DIGITAL_INPUT_SENSOR, INPUT); // sets the motion sensor digital pin as input // Register all sensors to gw (they will be created as child devices) - gw.present(CHILD_ID, S_MOTION); + present(CHILD_ID, S_MOTION); } @@ -60,10 +66,10 @@ void loop() boolean tripped = digitalRead(DIGITAL_INPUT_SENSOR) == HIGH; Serial.println(tripped); - gw.send(msg.set(tripped?"1":"0")); // Send tripped value to gw + send(msg.set(tripped?"1":"0")); // Send tripped value to gw // Sleep until interrupt comes in on motion sensor. Send update every two minute. - gw.sleep(INTERRUPT,CHANGE, SLEEP_TIME); + sleep(INTERRUPT,CHANGE, SLEEP_TIME); }