diff --git a/libraries/MySensors/MyConfig.h b/libraries/MySensors/MyConfig.h index 67e23f6..f8b0108 100644 --- a/libraries/MySensors/MyConfig.h +++ b/libraries/MySensors/MyConfig.h @@ -22,131 +22,342 @@ #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 -#define DEBUG + +/********************************** +* Serial and debug options +***********************************/ + +// 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 MY_DEBUG_VERBOSE flag for verbose debug prints. Requires DEBUG to be enabled. // This will add even more to the size of the final sketch! //#define MY_DEBUG_VERBOSE -// Disable this line, If you are using TX(1), RX(0) as normal I/O pin -#define ENABLED_SERIAL +// Enable this in sketch if you want to use TX(1), RX(0) as normal I/O pin +//#define MY_DISABLED_SERIAL -// Serial output baud rate (for debug prints and serial gateway) -#define BAUD_RATE 115200 +// Turn off debug if serial pins is used for other stuff +#ifdef MY_DISABLED_SERIAL +#undef MY_DEBUG +#endif +// Serial output baud rate (debug prints and serial gateway speed) +#ifndef MY_BAUD_RATE +#define MY_BAUD_RATE 115200 +#endif + + +/********************************** +* Radio selection and node config +***********************************/ + +// Selecting radio transport is optional (for a gateway node). + +//#define MY_RADIO_NRF24 +//#define MY_RADIO_RFM69 + + +// 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 boot-loader. // 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 +#ifndef MY_OTA_FLASH_SS #define MY_OTA_FLASH_SS 8 +#endif + // Flash jdecid +#ifndef MY_OTA_FLASH_JDECID #define MY_OTA_FLASH_JDECID 0x1F65 +#endif + + +/********************************** +* Gateway config +***********************************/ + +// Max buffersize needed for messages coming from controller +#ifndef MY_GATEWAY_MAX_RECEIVE_LENGTH +#define MY_GATEWAY_MAX_RECEIVE_LENGTH 100 +#endif + +// Max buffer size when sending messages +#ifndef MY_GATEWAY_MAX_SEND_LENGTH +#define MY_GATEWAY_MAX_SEND_LENGTH 120 +#endif + +// Max number of parallel clients (sever mode) +#ifndef MY_GATEWAY_MAX_CLIENTS +#define MY_GATEWAY_MAX_CLIENTS 1 +#endif + /********************************** * Information LEDs blinking ***********************************/ // This feature enables LEDs blinking on message receive, transmit -// or if some error occured. This was commonly used only in gateways, +// or if some error occurred. This was commonly used only in gateways, // but now can be used in any sensor node. Also the LEDs can now be // disabled in the gateway. -// #define WITH_LEDS_BLINKING +//#define MY_LEDS_BLINKING_FEATURE -// The following setting allows you to inverse the blinking feature WITH_LEDS_BLINKING -// When WITH_LEDS_BLINKING_INVERSE is enabled LEDSs are normally turned on and switches +// The following setting allows you to inverse the blinking feature MY_LEDS_BLINKING_FEATURE +// When MY_WITH_LEDS_BLINKING_INVERSE is enabled LEDSs are normally turned on and switches // off when blinking -//#define WITH_LEDS_BLINKING_INVERSE +//#define MY_WITH_LEDS_BLINKING_INVERSE // default LEDs blinking period in milliseconds -#define DEFAULT_LED_BLINK_PERIOD 300 +#ifndef MY_DEFAULT_LED_BLINK_PERIOD +#define MY_DEFAULT_LED_BLINK_PERIOD 300 +#endif // The RX LED default pin -#define DEFAULT_RX_LED_PIN 6 +#ifndef MY_DEFAULT_RX_LED_PIN + #if defined(ARDUINO_ARCH_ESP8266) + #define MY_DEFAULT_RX_LED_PIN 8 + #else + #define MY_DEFAULT_RX_LED_PIN 6 + #endif +#endif // The TX LED default pin -#define DEFAULT_TX_LED_PIN 5 +#ifndef MY_DEFAULT_TX_LED_PIN + #if defined(ARDUINO_ARCH_ESP8266) + #define MY_DEFAULT_TX_LED_PIN 9 + #else + #define MY_DEFAULT_TX_LED_PIN 5 + #endif +#endif // The Error LED default pin -#define DEFAULT_ERR_LED_PIN 4 +#ifndef MY_DEFAULT_ERR_LED_PIN + #if defined(ARDUINO_ARCH_ESP8266) + #define MY_DEFAULT_ERR_LED_PIN 7 + #else + #define MY_DEFAULT_ERR_LED_PIN 4 + #endif +#endif +/********************************************** +* Gateway inclusion button/mode configuration +**********************************************/ +// Enabled inclusion mode feature +//#define MY_INCLUSION_MODE_FEATURE + +// Enables inclusion-mode button feature on the gateway device +//#define MY_INCLUSION_BUTTON_FEATURE + +// Disable inclusion mode button if inclusion mode feature is not enabled +#ifndef MY_INCLUSION_MODE_FEATURE +#undef MY_INCLUSION_BUTTON_FEATURE +#endif + +// The default input pin used for the inclusion mode button +#ifndef MY_INCLUSION_MODE_BUTTON_PIN + #if defined(ARDUINO_ARCH_ESP8266) + #define MY_INCLUSION_MODE_BUTTON_PIN 5 + #else + #define MY_INCLUSION_MODE_BUTTON_PIN 3 + #endif +#endif +// Number of seconds (default one minute) inclusion mode should be enabled +#ifndef MY_INCLUSION_MODE_DURATION +#define MY_INCLUSION_MODE_DURATION 60 +#endif /********************************** * Message Signing Settings ***********************************/ -// Disable to completly disable signing functionality in library -//#define MY_SIGNING_FEATURE + +// Enable one of these in sketch to use the signing functionality +//#define MY_SIGNING_ATSHA204 +//#define MY_SIGNING_SOFT // Define a suitable timeout for a signature verification session -// Consider the turnaround from a nonce being generated to a signed message being received +// Consider the turn-around from a nonce being generated to a signed message being received // which might vary, especially in networks with many hops. 5s ought to be enough for anyone. +#ifndef MY_VERIFICATION_TIMEOUT_MS #define MY_VERIFICATION_TIMEOUT_MS 5000 +#endif -// Enable to turn on whitelisting +// Enable to turn on white-listing // When enabled, a signing node will salt the signature with it's unique signature and nodeId. // The verifying node will look up the sender in a local table of trusted nodes and // do the corresponding salting in order to verify the signature. -// For this reason, if whitelisting is enabled on one of the nodes in a sign-verify pair, both -// nodes have to implement whitelisting for this to work. -// Note that a node can still transmit a non-salted message (i.e. have whitelisting disabled) -// to a node that has whitelisting enabled (assuming the receiver does not have a matching entry -// for the sender in it's whitelist) -//#define MY_SECURE_NODE_WHITELISTING +// For this reason, if white listing is enabled on one of the nodes in a sign-verify pair, both +// nodes have to implement white listing for this to work. +// Note that a node can still transmit a non-salted message (i.e. have white listing disabled) +// to a node that has white listing enabled (assuming the receiver does not have a matching entry +// for the sender in it's white list) +//#define MY_SIGNING_NODE_WHITELIST {{.nodeId = GATEWAY_ADDRESS,.serial = {0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01}}} -// MySigningAtsha204 default setting -#define MY_ATSHA204_PIN 17 // A3 - pin where ATSHA204 is attached +// Atsha204 default pin setting +#ifndef MY_SIGNING_ATSHA204_PIN +#define MY_SIGNING_ATSHA204_PIN 17 // A3 - pin where ATSHA204 is attached +#endif -// MySigningAtsha204Soft default settings -#define MY_RANDOMSEED_PIN 7 // A7 - Pin used for random generation (do not connect anything to this) +// Pin used for random generation in soft signing (do not connect anything to this when enabled) +#ifndef MY_SIGNING_SOFT_RANDOMSEED_PIN +#define MY_SIGNING_SOFT_RANDOMSEED_PIN 7 // A7 - +#endif + +// Set the soft_serial value to an arbitrary value for proper security +#ifndef MY_SIGNING_SOFT_SERIAL +#define MY_SIGNING_SOFT_SERIAL 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09 +#endif // Key to use for HMAC calculation in MySigningAtsha204Soft (32 bytes) -#define MY_HMAC_KEY 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +#ifndef MY_SIGNING_SOFT_HMAC_KEY +#define MY_SIGNING_SOFT_HMAC_KEY 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +#endif /********************************** * 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) -// Enable SOFTSPI for NRF24L01 when using the W5100 Ethernet module -//#define SOFTSPI -#ifdef SOFTSPI - // Define the soft SPI pins used for NRF radio - const uint8_t SOFT_SPI_MISO_PIN = 16; - const uint8_t SOFT_SPI_MOSI_PIN = 15; - const uint8_t SOFT_SPI_SCK_PIN = 14; +// Enables RF24 encryption (all nodes and gateway must have this enabled) +//#define MY_RF24_ENABLE_ENCRYPTION + +// Default encryption key. Override in sketch if needed. +#ifndef MY_RF24_ENCRYPTKEY +#define MY_RF24_ENCRYPTKEY 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x10,0x11,0x12,0x13,0x14,0x15,0x16 #endif +// Default pin settings. Override in sketch if needed. +#ifndef MY_RF24_CE_PIN + #if defined(ARDUINO_ARCH_ESP8266) + #define MY_RF24_CE_PIN 4 + #else + #define MY_RF24_CE_PIN 9 + #endif +#endif + +#ifndef MY_RF24_CS_PIN + #if defined(ARDUINO_ARCH_ESP8266) + #define MY_RF24_CS_PIN 15 + #else + #define MY_RF24_CS_PIN 10 + #endif +#endif + +#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 +#define MY_SOFT_SPI_SCK_PIN 14 +#endif +#ifndef MY_SOFT_SPI_MISO_PIN +#define MY_SOFT_SPI_MISO_PIN 16 +#endif +#ifndef MY_SOFT_SPI_MOSI_PIN +#define MY_SOFT_SPI_MOSI_PIN 15 +#endif /********************************** * RFM69 Driver Defaults ***********************************/ -// Default network id. Use the same for all nodes that will talk to each other -#define RFM69_NETWORKID 100 // Default frequency to use. This must match the hardware version of the RFM69 radio (uncomment one): -// #define RFM69_FREQUENCY RF69_433MHZ -#define RFM69_FREQUENCY RF69_868MHZ -//#define FREQUENCY RF69_915MHZ +#ifndef MY_RFM69_FREQUENCY +// #define MY_RFM69_FREQUENCY RF69_433MHZ +#define MY_RFM69_FREQUENCY RF69_868MHZ +//#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 +#endif +#ifndef MY_RF69_IRQ_PIN +#define MY_RF69_IRQ_PIN RF69_IRQ_PIN +#endif +#ifndef MY_RF69_SPI_CS +#define MY_RF69_SPI_CS RF69_SPI_CS +#endif +#ifndef MY_RF69_IRQ_NUM +#define MY_RF69_IRQ_NUM RF69_IRQ_NUM +#endif // Enable this for encryption of packets -//#define RFM69_ENABLE_ENCRYPTION -#define RFM69_ENCRYPTKEY "sampleEncryptKey" //exactly the same 16 characters/bytes on all nodes! +//#define MY_RFM69_ENABLE_ENCRYPTION +#ifndef MY_RFM69_ENCRYPTKEY +#define MY_RFM69_ENCRYPTKEY "sampleEncryptKey" //exactly the same 16 characters/bytes on all nodes! +#endif + +/************************************** +* Ethernet Gateway Transport Defaults +***************************************/ + +// The gateway options available +//#define MY_GATEWAY_W5100 +//#define MY_GATEWAY_ENC28J60 +//#define MY_GATEWAY_ESP8266 + +// The port to open on controller or gateway +#ifndef MY_PORT +#define MY_PORT 5003 +#endif + +// Static ip address of gateway (if this is disabled, DHCP will be used) +//#define MY_IP_ADDRESS 192,168,178,66 + +// Enables UDP mode for Ethernet gateway (W5100) +//#define MY_USE_UDP + +// DHCP, default renewal setting +#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 + +// Controller ip-address, if this is defined, gateway will act as a client trying to contact controller on MY_PORT. +// If MY_CONTROLLER_IP_ADDRESS is left un-defined, gateway acts as server allowing incoming connections. +//#define MY_CONTROLLER_IP_ADDRESS 192, 168, 178, 254 #endif diff --git a/libraries/MySensors/MyHw.h b/libraries/MySensors/MyHw.h deleted file mode 100644 index 74ceb13..0000000 --- a/libraries/MySensors/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 DEBUG - virtual void debugPrint(bool isGW, const char *fmt, ... ) = 0; - #endif - -}; - -#endif diff --git a/libraries/MySensors/MyHwESP8266.h b/libraries/MySensors/MyHwESP8266.h deleted file mode 100644 index e281421..0000000 --- a/libraries/MySensors/MyHwESP8266.h +++ /dev/null @@ -1,86 +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. - */ - -#ifdef ARDUINO_ARCH_ESP8266 - -#ifndef MyHwESP8266_h -#define MyHwESP8266_h - -#include "MyHw.h" -#include "MyConfig.h" -#include "MyMessage.h" - - -#ifdef __cplusplus -#include -#include -#endif - -// 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() - -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); - -enum period_t -{ - SLEEP_15Ms, - SLEEP_30MS, - SLEEP_60MS, - SLEEP_120MS, - SLEEP_250MS, - SLEEP_500MS, - SLEEP_1S, - SLEEP_2S, - SLEEP_4S, - SLEEP_8S, - 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); -#ifdef DEBUG - void debugPrint(bool isGW, const char *fmt, ... ); -#endif -}; -#endif - -#endif // #ifdef ARDUINO_ARCH_ESP8266 diff --git a/libraries/MySensors/MySensor.cpp b/libraries/MySensors/MySensor.cpp deleted file mode 100644 index 2a5169d..0000000 --- a/libraries/MySensors/MySensor.cpp +++ /dev/null @@ -1,887 +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 "MySensor.h" - -#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))) -#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) { - msg.sender = sender; - msg.destination = destination; - msg.sensor = sensor; - msg.type = type; - mSetCommand(msg,command); - mSetRequestAck(msg,enableAck); - mSetAck(msg,false); - 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 -#ifdef WITH_LEDS_BLINKING - , uint8_t _rx, uint8_t _tx, uint8_t _er, unsigned long _blink_period -#endif - ) - : - radio(_radio), -#ifdef MY_SIGNING_FEATURE - signer(_signer), -#endif -#ifdef WITH_LEDS_BLINKING - pinRx(_rx), pinTx(_tx), pinEr(_er), ledBlinkPeriod(_blink_period), -#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 WITH_LEDS_BLINKING -void MySensor::handleLedsBlinking() { - // 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(pinRx, LED_ON); - } - else if(!countRx) { - // switching off - hw_digitalWrite(pinRx, LED_OFF); - } - if(countRx != 255) - --countRx; - - if(countTx && countTx != 255) { - // switch led on - hw_digitalWrite(pinTx, LED_ON); - } - else if(!countTx) { - // switching off - hw_digitalWrite(pinTx, LED_OFF); - } - if(countTx != 255) - --countTx; - - if(countErr && countErr != 255) { - // switch led on - hw_digitalWrite(pinEr, LED_ON); - } - else if(!countErr) { - // switching off - hw_digitalWrite(pinEr, 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) { - #ifdef ENABLED_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 WITH_LEDS_BLINKING - // Setup led pins - pinMode(pinRx, OUTPUT); - pinMode(pinTx, OUTPUT); - pinMode(pinEr, OUTPUT); - - // Set initial state of leds - hw_digitalWrite(pinRx, LED_OFF); - hw_digitalWrite(pinTx, LED_OFF); - hw_digitalWrite(pinEr, 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 WITH_LEDS_BLINKING - 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 WITH_LEDS_BLINKING - 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 WITH_LEDS_BLINKING - errBlink(1); -#endif - return false; - } - if (!signOk) { - debug(PSTR("sign fail\n")); -#ifdef WITH_LEDS_BLINKING - 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 WITH_LEDS_BLINKING - 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 WITH_LEDS_BLINKING - 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 WITH_LEDS_BLINKING - 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 WITH_LEDS_BLINKING - 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 WITH_LEDS_BLINKING - 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 ((!isGateway || DO_SIGN(msg.sender)) && 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 WITH_LEDS_BLINKING - errBlink(1); -#endif - return false; - } - else if (!signer.verifyMsg(msg)) { - debug(PSTR("verify fail\n")); -#ifdef WITH_LEDS_BLINKING - 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 WITH_LEDS_BLINKING - 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) { - bool isMetric; - - if (type == I_REBOOT) { - // Requires MySensors or other bootloader with watchdogs enabled - hw_reboot(); - } else if (type == I_ID_RESPONSE) { - if (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 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()); - } - } - 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',':',(uint8_t)(fwsize >> 8),(uint8_t)(fwsize & 0xff),':'}; - 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; -} - -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 -} - diff --git a/libraries/MySensors/MySensor.h b/libraries/MySensors/MySensor.h index 52bb45b..04f2611 100644 --- a/libraries/MySensors/MySensor.h +++ b/libraries/MySensors/MySensor.h @@ -16,390 +16,198 @@ * 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 -#include "Version.h" // Auto generated by bot #include "MyConfig.h" -#include "MyHw.h" -#include "MyTransport.h" -#include "MyTransportNRF24.h" -#include "MyParser.h" -#ifdef MY_SIGNING_FEATURE -#include "MySigning.h" -#include "MySigningNone.h" -#endif -#include "MyMessage.h" -#ifdef MY_OTA_FIRMWARE_FEATURE -#include "utility/SPIFlash.h" -#endif -#include -#include +#include "core/MySensorCore.h" +// Detect node type +#if defined(MY_GATEWAY_SERIAL) || defined(MY_GATEWAY_W5100) || defined(MY_GATEWAY_ENC28J60) || defined(ARDUINO_ARCH_ESP8266) || defined(MY_GATEWAY_MQTT_CLIENT) + #define MY_GATEWAY_FEATURE + #define MY_IS_GATEWAY (true) + #define MY_NODE_TYPE "gateway" +#elif defined(MY_REPEATER_FEATURE) + #define MY_NODE_TYPE "repeater" +#else + #define MY_NODE_TYPE "sensor" +#endif -// Set the hardware driver to use (initialized by MySensor-class) -//#if defined __AVR_ATmega328P__ +// Enable radio "feature" if one of the radio types was enabled +#if defined(MY_RADIO_NRF24) || defined(MY_RADIO_RFM69) + #define MY_RADIO_FEATURE +#endif + +// HARDWARE #if defined(ARDUINO_ARCH_ESP8266) -#include "MyHwESP8266.h" -typedef MyHwESP8266 MyHwDriver; + // Remove PSTR macros from debug prints + #undef PSTR + #define PSTR(x) (x) + //#undef F + //#define F(x) (x) + #include "core/MyHwESP8266.cpp" + // For ESP8266, we always enable gateway feature + #define MY_GATEWAY_ESP8266 #elif defined(ARDUINO_ARCH_AVR) -#include "MyHwATMega328.h" -typedef MyHwATMega328 MyHwDriver; + #include "core/MyHwATMega328.cpp" #endif -//#endif - -#ifdef DEBUG -#define debug(x,...) hw.debugPrint(isGateway, x, ##__VA_ARGS__) +// LEDS +#if defined(MY_LEDS_BLINKING_FEATURE) + #include "core/MyLeds.cpp" #else -#define debug(x,...) + #include "core/MyLeds.h" #endif -#ifdef WITH_LEDS_BLINKING_INVERSE -#define LED_ON 0x1 -#define LED_OFF 0x0 +// INCLUSION MODE +#if defined(MY_INCLUSION_MODE_FEATURE) + #include "core/MyInclusionMode.cpp" +#endif + + +// SIGNING +#if defined(MY_SIGNING_ATSHA204) || defined(MY_SIGNING_SOFT) + // SIGNING COMMON FUNCTIONS + #include "core/MySigning.cpp" + #define MY_SIGNING_FEATURE + #if defined(MY_SIGNING_ATSHA204) && defined(MY_SIGNING_SOFT) + #error Only one signing engine can be activated + #endif + + #if defined(MY_SIGNING_ATSHA204) + #include "core/MySigningAtsha204.cpp" + #include "drivers/ATSHA204/ATSHA204.cpp" + #elif defined(MY_SIGNING_SOFT) + #include "core/MySigningAtsha204Soft.cpp" + #include "drivers/ATSHA204/sha256.cpp" + #endif +#endif + + + +// GATEWAY - TRANSPORT +#if defined(MY_GATEWAY_MQTT_CLIENT) + // GATEWAY - COMMON FUNCTIONS + // We only support MQTT Client using W5100 and ESP8266 at the moment + #if !defined(MY_CONTROLLER_IP_ADDRESS) + #error You must specify MY_CONTROLLER_IP_ADDRESS (MQTT broker address) + #endif + #if !defined(MY_MQTT_TOPIC_PREFIX) + #error You must specify a topic prefix MY_MQTT_TOPIC_PREFIX for this MQTT client + #endif + #if !defined(MY_MQTT_CLIENT_ID) + #error You must define a unique MY_MQTT_CLIENT_ID for this MQTT client + #endif + + #include "drivers/pubsubclient/src/PubSubClient.cpp" + #include "core/MyGatewayTransport.cpp" + #include "core/MyGatewayTransportMQTTClient.cpp" +#elif defined(MY_GATEWAY_FEATURE) + // GATEWAY - COMMON FUNCTIONS + #include "core/MyGatewayTransport.cpp" + + // We currently only support one protocol at the moment, enable it. + #include "core/MyProtocolMySensors.cpp" + + // GATEWAY - CONFIGURATION + #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_CONTROLLER_IP_ADDRESS) + #define MY_GATEWAY_CLIENT_MODE + #endif + #if !defined(MY_PORT) + #error You must define MY_PORT (controller or gatway port to open) + #endif + #if defined(MY_GATEWAY_ESP8266) + // GATEWAY - ESP8266 + #include "core/MyGatewayTransportEthernet.cpp" + #elif defined(MY_GATEWAY_W5100) + // GATEWAY - W5100 + #include "core/MyGatewayTransportEthernet.cpp" + #elif defined(MY_GATEWAY_ENC28J60) + // GATEWAY - ENC28J60 + #if defined(MY_USE_UDP) + #error UDP mode is not available for ENC28J60 + #endif + #include "core/MyGatewayTransportEthernet.cpp" + #elif defined(MY_GATEWAY_SERIAL) + // GATEWAY - SERIAL + #include "core/MyGatewayTransportSerial.cpp" + #endif +#endif + + +// RADIO +#if defined(MY_RADIO_NRF24) || defined(MY_RADIO_RFM69) + // SOFTSPI + #ifdef MY_SOFTSPI + #if defined(ARDUINO_ARCH_ESP8266) + #error Soft SPI is not available on ESP8266 + #endif + #include "drivers/AVR/DigitalIO/SoftI2cMaster.cpp" + #include "drivers/AVR/DigitalIO/PinIO.cpp" + #endif + + // FLASH + #ifdef MY_OTA_FIRMWARE_FEATURE + #include "drivers/SPIFlash/SPIFlash.cpp" + #endif + #include "core/MyTransport.cpp" + #if defined(MY_RADIO_NRF24) && defined(MY_RADIO_RFM69) + #error Only one radio driver can be activated + #endif + #if defined(MY_RADIO_NRF24) + #if defined(MY_RF24_ENABLE_ENCRYPTION) + #include "drivers/AES/AES.cpp" + #endif + #include "drivers/RF24/RF24.cpp" + #include "core/MyTransportNRF24.cpp" + #elif defined(MY_RADIO_RFM69) + #include "drivers/RFM69/RFM69.cpp" + #include "core/MyTransportRFM69.cpp" + #endif +#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_SIGNING_NODE_WHITELISTING + #undef MY_SIGNING_FEATURE +#endif + +#if !defined(MY_GATEWAY_FEATURE) + #undef MY_INCLUSION_MODE_FEATURE + #undef MY_INCLUSION_BUTTON_FEATURE +#endif + +#if !defined(MY_GATEWAY_FEATURE) && !defined(MY_RADIO_FEATURE) + #error No radio or gateway feature activated. This means nowhere to send messages! Pretty pointless. +#endif + + +#include "core/MyMessage.cpp" +#include "core/MySensorCore.cpp" + +extern void setup(); +// Optional sketch functions called by MySensors library +void receive(const MyMessage &message) __attribute__((weak)); +void receiveTime(unsigned long) __attribute__((weak)); +void presentation() __attribute__((weak)); +extern "C" void setup() __attribute__((weak)); +extern "C" void loop() __attribute__((weak)); +extern "C" void loop2() __attribute__((weak)); + + +#include + +#if defined(MY_GATEWAY_ESP8266) + #include "core/MyMainESP8266.cpp" #else -#define LED_ON 0x0 -#define LED_OFF 0x1 + #include "core/MyMainDefault.cpp" #endif -// EEPROM start address for mysensors library data -#define EEPROM_START 0 -// EEPROM location of node id -#define EEPROM_NODE_ID_ADDRESS EEPROM_START -// EEPROM location of parent id -#define EEPROM_PARENT_NODE_ID_ADDRESS (EEPROM_START+1) -// EEPROM location of distance to gateway -#define EEPROM_DISTANCE_ADDRESS (EEPROM_PARENT_NODE_ID_ADDRESS+1) -#define EEPROM_ROUTES_ADDRESS (EEPROM_DISTANCE_ADDRESS+1) // Where to start storing routing information in EEPROM. Will allocate 256 bytes. -#define EEPROM_CONTROLLER_CONFIG_ADDRESS (EEPROM_ROUTES_ADDRESS+256) // Location of controller sent configuration (we allow one payload of config data from controller) -#define EEPROM_FIRMWARE_TYPE_ADDRESS (EEPROM_CONTROLLER_CONFIG_ADDRESS+24) -#define EEPROM_FIRMWARE_VERSION_ADDRESS (EEPROM_FIRMWARE_TYPE_ADDRESS+2) -#define EEPROM_FIRMWARE_BLOCKS_ADDRESS (EEPROM_FIRMWARE_VERSION_ADDRESS+2) -#define EEPROM_FIRMWARE_CRC_ADDRESS (EEPROM_FIRMWARE_BLOCKS_ADDRESS+2) -#define EEPROM_SIGNING_REQUIREMENT_TABLE_ADDRESS (EEPROM_FIRMWARE_CRC_ADDRESS+2) -#define EEPROM_LOCAL_CONFIG_ADDRESS (EEPROM_SIGNING_REQUIREMENT_TABLE_ADDRESS+32) // First free address for sketch static configuration - -// Search for a new parent node after this many transmission failures -#define SEARCH_FAILURES 5 - - -struct NodeConfig -{ - uint8_t nodeId; // Current node id - uint8_t parentNodeId; // Where this node sends its messages - uint8_t distance; // This nodes distance to sensor net gateway (number of hops) -}; - -struct ControllerConfig { - uint8_t isMetric; -}; - - -// Size of each firmware block -#define FIRMWARE_BLOCK_SIZE 16 -// Number of times a firmware block should be requested before giving up -#define FIRMWARE_MAX_REQUESTS 5 -// Number of times to request a fw block before giving up -#define MY_OTA_RETRY 5 -// Number of millisecons before re-request a fw block -#define MY_OTA_RETRY_DELAY 500 -// Start offset for firmware in flash (DualOptiboot wants to keeps a signature first) -#define FIRMWARE_START_OFFSET 10 -// Bootloader version -#define MY_OTA_BOOTLOADER_MAJOR_VERSION 3 -#define MY_OTA_BOOTLOADER_MINOR_VERSION 0 -#define MY_OTA_BOOTLOADER_VERSION (MY_OTA_BOOTLOADER_MINOR_VERSION * 256 + MY_OTA_BOOTLOADER_MAJOR_VERSION) - - -// FW config structure, stored in eeprom -typedef struct { - uint16_t type; - uint16_t version; - uint16_t blocks; - uint16_t crc; -} __attribute__((packed)) NodeFirmwareConfig; - -typedef struct { - uint16_t type; - uint16_t version; - uint16_t blocks; - uint16_t crc; - uint16_t BLVersion; -} __attribute__((packed)) RequestFirmwareConfig; - -typedef struct { - uint16_t type; - uint16_t version; - uint16_t block; -} __attribute__((packed)) RequestFWBlock; - -typedef struct { - uint16_t type; - uint16_t version; - uint16_t block; - uint8_t data[FIRMWARE_BLOCK_SIZE]; -} __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 -#ifdef WITH_LEDS_BLINKING - , uint8_t _rx=DEFAULT_RX_LED_PIN, - uint8_t _tx=DEFAULT_TX_LED_PIN, - uint8_t _er=DEFAULT_ERR_LED_PIN, - unsigned long _blink_period=DEFAULT_LED_BLINK_PERIOD -#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); - - boolean sendRoute(MyMessage &message); - - /** - * 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); - - /** - * 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); - - /** - * 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); - - /** - * 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)); - - - /** - * 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(); - - /** - * Returns the most recent node configuration received from controller - */ - ControllerConfig getConfig(); - - /** - * 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); - - /** - * 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); - - - - /** - * 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); - -#ifdef WITH_LEDS_BLINKING - /** - * Blink with LEDs - * @param cnt how many blink cycles to keep the LED on. Default cycle is 300ms - */ - void rxBlink(uint8_t cnt); - void txBlink(uint8_t cnt); - void errBlink(uint8_t cnt); -#endif - - protected: - NodeConfig nc; // Essential settings for node to work - ControllerConfig cc; // Configuration coming from controller - - bool repeaterMode; - bool autoFindParent; - bool isGateway; - - MyMessage msg; // Buffer for incoming messages. - MyMessage tmpMsg ; // Buffer for temporary messages (acks and nonces among others). - -#ifdef WITH_LEDS_BLINKING - uint8_t pinRx; // Rx led pin - uint8_t pinTx; // Tx led pin - uint8_t pinEr; // Err led pin - - // 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 ledBlinkPeriod; - 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 -#ifdef MY_OTA_FIRMWARE_FEATURE - NodeFirmwareConfig fc; - bool fwUpdateOngoing; - unsigned long fwLastRequestTime; - uint16_t fwBlock; - uint8_t fwRetry; - SPIFlash flash; -#endif - MyHw& hw; - - boolean sendWrite(uint8_t dest, MyMessage &message); - - private: -#ifdef 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(); -#endif - - - void requestNodeId(); - void setupNode(); - void findParentNode(); - uint8_t crc8Message(MyMessage &message); -}; -#endif - #endif diff --git a/libraries/MySensors/MySigning.h b/libraries/MySensors/MySigning.h deleted file mode 100644 index e65d546..0000000 --- a/libraries/MySensors/MySigning.h +++ /dev/null @@ -1,87 +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. - * - ******************************* - * - * DESCRIPTION - * Signing support created by Patrick "Anticimex" Fallberg - * The MySigning driver provides a generic API for various signing backends to offer - * signing of MySensors messages. - * - */ -#ifndef MySigning_h -#define MySigning_h - -#include -#include "MyMessage.h" - -class MySigning -{ -public: - // MySigning constructor. - // Different parameters would be needed depending on signing backend (e.g. pins connected etc.). - // Keeping these parameters as #define's in MyConfig to streamline the driver interface. - // @param requestSignatures Set this to true if you want destination node to sign all messages sent to this node. Default is not to require signing. - MySigning(bool requestSignatures=false); - - // Returns the current requestSignature setting - bool requestSignatures(); - - // Stores signing identifier and a new nonce in provided message for signing operations. - // All space in message payload buffer is used for signing identifier and nonce. - // Returns false if subsystem is busy processing an ongoing signing operation. - // If successful, this marks the start of a signing operation at the receiving side so - // implementation is expected to do any necessary initializations within this call. - virtual bool getNonce(MyMessage &msg) = 0; - - // Check timeout of verification session. - // Nonce will be purged if it takes too long for a signed message to be sent to the receiver. - // This function should be called on regular intervals. - virtual bool checkTimer(void) = 0; - - // Get nonce from provided message and store for signing operations. - // Returns false if subsystem is busy processing an ongoing signing operation. - // Returns false if signing identifier found in message is not supported by the used backend. - // If successful, this marks the start of a signing operation at the sending side so - // implementation is expected to do any necessary initializations within this call. - virtual bool putNonce(MyMessage &msg) = 0; - - // Signs provided message. All remaining space in message payload buffer is used for signing - // identifier and signature. - // Nonce used for signature calculation is the one generated previously using getNonce(). - // Nonce will be cleared when this function is called to prevent re-use of nonce. - // Returns false if subsystem is busy processing an ongoing signing operation. - // Returns false if not two bytes or more of free payload space is left in provided message. - // This ends a signing operation at the sending side so implementation is expected to do any - // deinitializations and enter a power saving state within this call. - virtual bool signMsg(MyMessage &msg) = 0; - - // Verifies signature in provided message. - // Nonce used for verification is the one previously set using putNonce(). - // Nonce will be cleared when this function is called to prevent re-use of nonce. - // Returns false if subsystem is busy processing an ongoing signing operation. - // Returns false if signing identifier found in message is not supported by the used backend. - // This ends a signing operation at the receiving side so implementation is expected to do any - // deinitializations and enter a power saving state within this call. - virtual bool verifyMsg(MyMessage &msg) = 0; - - public: - bool _requestSignatures; -}; - -#endif diff --git a/libraries/MySensors/MySigningAtsha204.h b/libraries/MySensors/MySigningAtsha204.h deleted file mode 100644 index 188a4aa..0000000 --- a/libraries/MySensors/MySigningAtsha204.h +++ /dev/null @@ -1,75 +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. - * - ******************************* - * - * DESCRIPTION - * Signing support created by Patrick "Anticimex" Fallberg - * ATSHA204 signing backend. The Atmel ATSHA204 offers true random number generation and - * HMAC-SHA256 authentication with a readout-protected key. - * - */ - -#ifndef MySigningAtsha204_h -#define MySigningAtsha204_h - -#include "MyConfig.h" -#include "MySigning.h" -#include "utility/ATSHA204.h" -#include - -#ifdef MY_SECURE_NODE_WHITELISTING -typedef struct { - uint8_t nodeId; - uint8_t serial[SHA204_SERIAL_SZ]; -} whitelist_entry_t; -#endif - -// The ATSHA204 is capable of generating proper random numbers for nonce -// and can calculate HMAC-SHA256 signatures. This is enterprise- -// level of security and ought to implement the signing needs for anybody. -class MySigningAtsha204 : public MySigning -{ -public: - MySigningAtsha204(bool requestSignatures=true, -#ifdef MY_SECURE_NODE_WHITELISTING - uint8_t nof_whitelist_entries=0, const whitelist_entry_t* the_whitelist=NULL, -#endif - uint8_t atshaPin=MY_ATSHA204_PIN); - bool getNonce(MyMessage &msg); - bool checkTimer(void); - bool putNonce(MyMessage &msg); - bool signMsg(MyMessage &msg); - bool verifyMsg(MyMessage &msg); -private: - ATSHA204Class atsha204; - unsigned long timestamp; - bool verification_ongoing; - uint8_t current_nonce[NONCE_NUMIN_SIZE_PASSTHROUGH+SHA204_SERIAL_SZ+1]; - uint8_t temp_message[SHA_MSG_SIZE]; - uint8_t rx_buffer[SHA204_RSP_SIZE_MAX]; - uint8_t tx_buffer[SHA204_CMD_SIZE_MAX]; - void calculateSignature(MyMessage &msg); - uint8_t* sha256(const uint8_t* data, size_t sz); -#ifdef MY_SECURE_NODE_WHITELISTING - uint8_t whitlist_sz; - const whitelist_entry_t* whitelist; -#endif -}; - -#endif diff --git a/libraries/MySensors/MySigningAtsha204Soft.h b/libraries/MySensors/MySigningAtsha204Soft.h deleted file mode 100644 index cd65d2c..0000000 --- a/libraries/MySensors/MySigningAtsha204Soft.h +++ /dev/null @@ -1,85 +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. - * - ******************************* - * - * DESCRIPTION - * Signing support created by Patrick "Anticimex" Fallberg - * ATSHA204 emulated signing backend. The emulated ATSHA204 implementation offers pseudo random - * number generation and HMAC-SHA256 authentication compatible with a "physical" ATSHA204. - * NOTE: Key is stored in clear text in the Arduino firmware. Therefore, the use of this back-end - * could compromise the key used in the signed message infrastructure if device is lost and its memory - * dumped. - * - */ - -#ifndef MySigningHmac256Soft_h -#define MySigningHmac256Soft_h - -#include "MyConfig.h" -#include "MySigning.h" -#include "utility/ATSHA204.h" -#include "utility/sha256.h" -#include - -#ifdef MY_SECURE_NODE_WHITELISTING -typedef struct { - uint8_t nodeId; - uint8_t serial[SHA204_SERIAL_SZ]; -} whitelist_entry_t; -#endif - -// This implementation is the pure software variant of the ATSHA204. -// It is designed to work fully compliant with nodes using ATSHA204 in the network -// and therefore uses the same signing identifier as ATSHA204. -// Because it is completly software based, the quality of the generated random numbers -// is weaker though. Random numbers are generated using the Arduino library and seed -// is sampled from an analog pin. This pin should unconnected in the hardware. -// The pin is selected using MY_RANDOMSEED_PIN in MyConfig.h. -class MySigningAtsha204Soft : public MySigning -{ -public: - MySigningAtsha204Soft(bool requestSignatures=true, -#ifdef MY_SECURE_NODE_WHITELISTING - uint8_t nof_whitelist_entries=0, const whitelist_entry_t* the_whitelist=NULL, - const uint8_t* the_serial=NULL, //SHA204_SERIAL_SZ sized buffer expected if provided -#endif - uint8_t randomseedPin=MY_RANDOMSEED_PIN); - bool getNonce(MyMessage &msg); - bool checkTimer(void); - bool putNonce(MyMessage &msg); - bool signMsg(MyMessage &msg); - bool verifyMsg(MyMessage &msg); -private: - Sha256Class Sha256; - unsigned long timestamp; - bool verification_ongoing; - uint8_t current_nonce[NONCE_NUMIN_SIZE_PASSTHROUGH]; - uint8_t temp_message[32]; - static uint8_t hmacKey[32]; - uint8_t rndPin; - uint8_t hmac[32]; - void calculateSignature(MyMessage &msg); -#ifdef MY_SECURE_NODE_WHITELISTING - uint8_t whitlist_sz; - const whitelist_entry_t* whitelist; - const uint8_t* node_serial_info; -#endif -}; - -#endif diff --git a/libraries/MySensors/MySigningNone.cpp b/libraries/MySensors/MySigningNone.cpp deleted file mode 100644 index d9aa5ab..0000000 --- a/libraries/MySensors/MySigningNone.cpp +++ /dev/null @@ -1,95 +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. - * - ******************************* - * - * DESCRIPTION - * Signing support created by Patrick "Anticimex" Fallberg - * Disabled signing backend. Does not provide any security measures. - * - */ - - -#include "MyConfig.h" -#include "MySigning.h" -#include "MySigningNone.h" - -#define SIGNING_IDENTIFIER (0) - -// Uncomment this to get some useful serial debug info (Serial.print and Serial.println expected) -//#define DEBUG_SIGNING - -#ifdef DEBUG_SIGNING -#define DEBUG_SIGNING_PRINTLN(args) Serial.println(args) -#else -#define DEBUG_SIGNING_PRINTLN(args) -#endif - -#ifdef DEBUG_SIGNING -static void DEBUG_NONE_PRINTBUF(const __FlashStringHelper* str, uint8_t* buf, uint8_t sz) -{ - int i; - Serial.println(str); - for (int i=0; i MAX_PAYLOAD-2) { - DEBUG_SIGNING_PRINTLN(F("MTOL")); // Message too large for signature to fit - return false; - } - mSetSigned(msg, 1); // make sure signing flag is set before signature is calculated - msg.data[mGetLength(msg)] = SIGNING_IDENTIFIER; // Add signing identifier - return true; -} - -bool MySigningNone::verifyMsg(MyMessage &msg) { - if (msg.data[mGetLength(msg)] != SIGNING_IDENTIFIER) - return false; - else - return true; -} diff --git a/libraries/MySensors/MySigningNone.h b/libraries/MySensors/MySigningNone.h deleted file mode 100644 index 4b27103..0000000 --- a/libraries/MySensors/MySigningNone.h +++ /dev/null @@ -1,51 +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. - * - ******************************* - * - * DESCRIPTION - * Signing support created by Patrick "Anticimex" Fallberg - * Disabled signing backend. Does not provide any security measures. - * - */ - - -#ifndef MySigningNone_h -#define MySigningNone_h - -#include "MyConfig.h" -#include "MySigning.h" -#include - -// The "none" signing driver that can be used for nodes -// not requiring signing -// It does check SIGNING_IDENTIFIER byte to avoid illegal mixing of signing back-ends in -// the network (as seen by this node) and it does verify proper execution order on the API. -// The "none" driver rejects all other back-ends. -class MySigningNone : public MySigning -{ -public: - MySigningNone(); - bool getNonce(MyMessage &msg); - bool checkTimer(void); - bool putNonce(MyMessage &msg); - bool signMsg(MyMessage &msg); - bool verifyMsg(MyMessage &msg); -}; - -#endif diff --git a/libraries/MySensors/MyTransport.cpp b/libraries/MySensors/MyTransport.cpp deleted file mode 100644 index 05627de..0000000 --- a/libraries/MySensors/MyTransport.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 "MyTransport.h" - -MyTransport::MyTransport() { -} diff --git a/libraries/MySensors/MyTransport.h b/libraries/MySensors/MyTransport.h deleted file mode 100644 index aab12f3..0000000 --- a/libraries/MySensors/MyTransport.h +++ /dev/null @@ -1,65 +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 MyTransport_h -#define MyTransport_h - -#include - -#define AUTO 0xFF // 0-254. Id 255 is reserved for auto initialization of nodeId. -#define NODE_SENSOR_ID 0xFF // Node child id is always created for when a node - -// This is the nodeId for sensor net gateway receiver sketch (where all sensors should send their data). -#define GATEWAY_ADDRESS ((uint8_t)0) -#define BROADCAST_ADDRESS ((uint8_t)0xFF) - -class MyTransport -{ -public: - // MyTransport constructor - // different parameters would be needed depending on radio module (e.g. pins connected etc.) - // keeping these parameters as #define's in MyConfig to streamline the driver interface - MyTransport(); - // init() - // different parameters would be needed depending on radio module (e.g. channel, power, crc, etc.) - // keeping these parameters as #define's in MyConfig to streamline the driver interface - virtual bool init() = 0; - // setAddress(address) - // set the node address and start listening to that address (and the broadcast address) - virtual void setAddress(uint8_t address) = 0; - // getAddress() - // returns the current address from the radio driver - virtual uint8_t getAddress() = 0; - // send(to, data, len) - // reliable transmission of the data with given length (in bytes) to the destination address - // returns true if successfully submitted - virtual bool send(uint8_t to, const void* data, uint8_t len) = 0; - // available(to) - // returns true if a new packet arrived in the rx buffer - // populates "to" parameter with the address the packet was sent to (either own address or broadcast) - virtual bool available(uint8_t *to) = 0; - // receive(data) - // returns length of received packet - // populates "data" parameter with received data - virtual uint8_t receive(void* data) = 0; - // powers down the radio - virtual void powerDown() = 0; -}; - -#endif diff --git a/libraries/MySensors/MyTransportNRF24.cpp b/libraries/MySensors/MyTransportNRF24.cpp deleted file mode 100644 index a01e8b3..0000000 --- a/libraries/MySensors/MyTransportNRF24.cpp +++ /dev/null @@ -1,95 +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 "MyTransport.h" -#include "MyTransportNRF24.h" - -MyTransportNRF24::MyTransportNRF24(uint8_t ce, uint8_t cs, uint8_t paLevel, uint8_t channel, rf24_datarate_e datarate) - : - MyTransport(), - rf24(ce, cs), - _paLevel(paLevel), - _channel(channel), - _datarate(datarate) -{ -} - -bool MyTransportNRF24::init() { - // Start up the radio library - rf24.begin(); - - if (!rf24.isPVariant()) { - return false; - } - rf24.setAutoAck(1); - rf24.setAutoAck(BROADCAST_PIPE,false); // Turn off auto ack for broadcast - rf24.enableAckPayload(); - rf24.setChannel(_channel); - rf24.setPALevel(_paLevel); - rf24.setDataRate(_datarate); - rf24.setRetries(5,15); - rf24.setCRCLength(RF24_CRC_16); - rf24.enableDynamicPayloads(); - - // All nodes listen to broadcast pipe (for FIND_PARENT_RESPONSE messages) - rf24.openReadingPipe(BROADCAST_PIPE, TO_ADDR(BROADCAST_ADDRESS)); - return true; -} - -void MyTransportNRF24::setAddress(uint8_t address) { - _address = address; - rf24.openReadingPipe(WRITE_PIPE, TO_ADDR(address)); - rf24.openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(address)); - rf24.startListening(); -} - -uint8_t MyTransportNRF24::getAddress() { - return _address; -} - -bool MyTransportNRF24::send(uint8_t to, const void* data, uint8_t len) { - // Make sure radio has powered up - rf24.powerUp(); - rf24.stopListening(); - rf24.openWritingPipe(TO_ADDR(to)); - bool ok = rf24.write(data, len, to == BROADCAST_ADDRESS); - rf24.startListening(); - return ok; -} - -bool MyTransportNRF24::available(uint8_t *to) { - uint8_t pipe = 255; - boolean avail = rf24.available(&pipe); - (void)avail; //until somebody makes use of 'avail' - if (pipe == CURRENT_NODE_PIPE) - *to = _address; - else if (pipe == BROADCAST_PIPE) - *to = BROADCAST_ADDRESS; - return (rf24.available() && pipe < 6); -} - -uint8_t MyTransportNRF24::receive(void* data) { - uint8_t len = rf24.getDynamicPayloadSize(); - rf24.read(data, len); - return len; -} - -void MyTransportNRF24::powerDown() { - rf24.powerDown(); -} diff --git a/libraries/MySensors/core/MyGatewayTransport.cpp b/libraries/MySensors/core/MyGatewayTransport.cpp new file mode 100644 index 0000000..3134d7c --- /dev/null +++ b/libraries/MySensors/core/MyGatewayTransport.cpp @@ -0,0 +1,62 @@ +/** + * 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 Tomas Hozza + * Copyright (C) 2015 Tomas Hozza + * 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" + +extern boolean transportSendRoute(MyMessage &message); + +inline void gatewayTransportProcess() { + 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 + inclusionModeSet(atoi(_msg.data) == 1); + #endif + } else { + _processInternalMessages(); + } + } else { + // Call incoming message callback if available + if (receive) { + receive(_msg); + } + } + } else { + #if defined(MY_RADIO_FEATURE) + transportSendRoute(_msg); + #endif + } + } +} diff --git a/libraries/MySensors/MyParser.cpp b/libraries/MySensors/core/MyGatewayTransport.h similarity index 51% rename from libraries/MySensors/MyParser.cpp rename to libraries/MySensors/core/MyGatewayTransport.h index 2011d16..e971bd1 100644 --- a/libraries/MySensors/MyParser.cpp +++ b/libraries/MySensors/core/MyGatewayTransport.h @@ -5,8 +5,8 @@ * 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 + * Created by Tomas Hozza + * Copyright (C) 2015 Tomas Hozza * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors * * Documentation: http://www.mysensors.org @@ -17,7 +17,39 @@ * version 2 as published by the Free Software Foundation. */ -#include "MyParser.h" +#ifndef MyGatewayTransport_h +#define MyGatewayTransport_h -MyParser::MyParser() { -} +#include "MyConfig.h" +#include "MyMessage.h" +#include "MyProtocol.h" +#include "MySensorCore.h" + +// Common gateway functions + +void gatewayTransportProcess(); + + +// Gateway "interface" functions + +/** + * initialize the driver + */ +bool gatewayTransportInit(); + +/** + * Send message to controller + */ +bool gatewayTransportSend(MyMessage &message); + +/* + * Check if a new message is available from controller + */ +bool gatewayTransportAvailable(); + +/* + * Pick up last message received from controller + */ +MyMessage& gatewayTransportReceive(); + +#endif /* MyGatewayTransportEthernet_h */ diff --git a/libraries/MySensors/core/MyGatewayTransportEthernet.cpp b/libraries/MySensors/core/MyGatewayTransportEthernet.cpp new file mode 100644 index 0000000..2ca86d2 --- /dev/null +++ b/libraries/MySensors/core/MyGatewayTransportEthernet.cpp @@ -0,0 +1,335 @@ +/** + * 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 Tomas Hozza + * Copyright (C) 2015 Tomas Hozza + * 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" + +#if defined(MY_CONTROLLER_IP_ADDRESS) + IPAddress _ethernetControllerIP(MY_CONTROLLER_IP_ADDRESS); +#endif + +#if defined(MY_IP_ADDRESS) + IPAddress _ethernetGatewayIP(MY_IP_ADDRESS); +#endif +byte _ethernetGatewayMAC[] = { MY_MAC_ADDRESS }; +uint16_t _ethernetGatewayPort = MY_PORT; +MyMessage _ethernetMsg; + +#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) + +// gatewayTransportSend(buildGw(_msg, I_GATEWAY_READY).set("Gateway startup complete.")); + +typedef struct +{ + char string[MY_GATEWAY_MAX_RECEIVE_LENGTH]; + uint8_t idx; +} inputBuffer; + +#if defined(MY_GATEWAY_ESP8266) + // Some re-defines to make code more readable below + #define EthernetServer WiFiServer + #define EthernetClient WiFiClient + #define EthernetUDP WiFiUDP + + #if defined(MY_IP_ADDRESS) + IPAddress gateway(MY_IP_GATEWAY_ADDRESS); + IPAddress subnet(MY_IP_SUBNET_ADDRESS); + #endif + static bool clientsConnected[MY_GATEWAY_MAX_CLIENTS]; +#endif + +#if defined(MY_USE_UDP) + EthernetUDP _ethernetServer; +#else + EthernetServer _ethernetServer(_ethernetGatewayPort); +#endif + + +static EthernetClient clients[MY_GATEWAY_MAX_CLIENTS]; +static inputBuffer inputString[MY_GATEWAY_MAX_CLIENTS]; + + +#ifndef MY_IP_ADDRESS + void gatewayTransportRenewIP(); +#endif + +// On W5100 boards with SPI_EN exposed we can use the real SPI bus together with radio +// (if we enable it during usage) +#ifdef MY_W5100_SPI_EN + void _w5100_spi_en(boolean enable) + { + if (enable) + { + // Pull up pin + pinMode(MY_W5100_SPI_EN, INPUT); + digitalWrite(MY_W5100_SPI_EN, HIGH); + } + else + { + // Ground pin + pinMode(MY_W5100_SPI_EN, OUTPUT); + digitalWrite(MY_W5100_SPI_EN, LOW); + } + } +#else + #define _w5100_spi_en(x) +#endif + + + +bool gatewayTransportInit() { + _w5100_spi_en(true); + #if defined(MY_GATEWAY_ESP8266) + (void)WiFi.begin(MY_ESP8266_SSID, MY_ESP8266_PASSWORD); + #ifdef MY_IP_ADDRESS + WiFi.config(_ethernetGatewayIP, gateway, subnet); + #endif + while (WiFi.status() != WL_CONNECTED) + { + delay(500); + Serial.print("."); + yield(); + } + Serial.print(F("IP: ")); + Serial.println(WiFi.localIP()); + + #else + #ifdef MY_IP_ADDRESS + Ethernet.begin(_ethernetGatewayMAC, _ethernetGatewayIP); + Serial.print(F("IP: ")); + Serial.println(Ethernet.localIP()); + #else + // Get IP address from DHCP + Ethernet.begin(_ethernetGatewayMAC); + Serial.print(F("IP: ")); + Serial.println(Ethernet.localIP()); + #endif /* IP_ADDRESS_DHCP */ + // give the Ethernet interface a second to initialize + // TODO: use HW delay + delay(1000); + #endif + + #ifdef MY_USE_UDP + _ethernetServer.begin(_ethernetGatewayPort); + #else + // we have to use pointers due to the constructor of EthernetServer + _ethernetServer.begin(); + #if defined(MY_GATEWAY_ESP8266) + _ethernetServer.setNoDelay(true); + #endif + #endif /* USE_UDP */ + _w5100_spi_en(false); + return true; +} + +bool gatewayTransportSend(MyMessage &message) +{ + bool ret = true; + char *_ethernetMsg = protocolFormat(message); + + _w5100_spi_en(true); + #if defined(MY_CONTROLLER_IP_ADDRESS) + #if defined(MY_USE_UDP) + _ethernetServer.beginPacket(_ethernetControllerIP, MY_PORT); + _ethernetServer.write(_ethernetMsg, strlen(_ethernetMsg)); + // returns 1 if the packet was sent successfully + ret = _ethernetServer.endPacket(); + #else + EthernetClient client; + if (client.connect(_ethernetControllerIP, MY_PORT)) { + client.write(_ethernetMsg, strlen(_ethernetMsg)); + client.stop(); + } + else { + // connecting to the server failed! + ret = false; + } + #endif + #else + // Send message to connected clients + #if defined(MY_GATEWAY_ESP8266) + for (uint8_t i = 0; i < ARRAY_SIZE(clients); i++) + { + if (clients[i] && clients[i].connected()) + { + clients[i].write((uint8_t*)_ethernetMsg, strlen(_ethernetMsg)); + } + } + #else + _ethernetServer.write(_ethernetMsg); + #endif + #endif + _w5100_spi_en(false); + return ret; + +} + +bool _readFromClient(uint8_t i) { + while (clients[i].connected() && clients[i].available()) { + char inChar = clients[i].read(); + if (inputString[i].idx < MY_GATEWAY_MAX_RECEIVE_LENGTH - 1) { + // if newline then command is complete + if (inChar == '\n') { + // a command was issued by the client + // we will now try to send it to the actuator + // echo the string to the serial port + debug(PSTR("Client %d: %s\n"), i, inputString[i].string); + + // clear the string: + inputString[i].idx = 0; + + bool ret = protocolParse(_ethernetMsg, inputString[i].string); + inputString[i].string[inputString[i].idx] = 0; + if (ret) { + return ret; + } + + } else { + // add it to the inputString: + inputString[i].string[inputString[i].idx++] = inChar; + } + } else { + // Incoming message too long. Throw away + debug(PSTR("Client %d: Message too long\n"), i); + inputString[i].idx = 0; + // Finished with this client's message. Next loop() we'll see if there's more to read. + break; + } + } + return false; +} + +bool gatewayTransportAvailable() +{ + _w5100_spi_en(true); + #if !defined(MY_IP_ADDRESS) && defined(MY_GATEWAY_W5100) + // renew IP address using DHCP + gatewayTransportRenewIP(); + #endif + + #ifdef MY_USE_UDP + + int packet_size = _ethernetServer.parsePacket(); + + if (packet_size) { + //debug(PSTR("UDP packet available. Size:%d\n"), packet_size); + _ethernetServer.read(inputString[0].string, MY_GATEWAY_MAX_RECEIVE_LENGTH); + _w5100_spi_en(false); + debug(PSTR("UDP packet received: %s\n"), inputString[0].string); + return protocolParse(_ethernetMsg, inputString[0].string); + } + #else + #if defined(MY_GATEWAY_ESP8266) + // ESP8266: Go over list of clients and stop any that are no longer connected. + // If the server has a new client connection it will be assigned to a free slot. + bool allSlotsOccupied = true; + for (uint8_t i = 0; i < ARRAY_SIZE(clients); i++) { + if (!clients[i].connected()) { + if (clientsConnected[i]) { + debug(PSTR("Client %d disconnected\n"), i); + clients[i].stop(); + } + //check if there are any new clients + if (_ethernetServer.hasClient()) { + clients[i] = _ethernetServer.available(); + inputString[i].idx = 0; + debug(PSTR("Client %d connected\n"), i); + _w5100_spi_en(false); + gatewayTransportSend(buildGw(_msg, I_GATEWAY_READY).set("Gateway startup complete.")); + _w5100_spi_en(true); + } + } + bool connected = clients[i].connected(); + clientsConnected[i] = connected; + allSlotsOccupied &= connected; + } + if (allSlotsOccupied && _ethernetServer.hasClient()) { + //no free/disconnected spot so reject + debug(PSTR("No free slot available\n")); + EthernetClient c = _ethernetServer.available(); + c.stop(); + } + // Loop over clients connect and read available data + for (uint8_t i = 0; i < ARRAY_SIZE(clients); i++) { + if (_readFromClient(i)) { + _w5100_spi_en(false); + return true; + } + } + #else + // W5100/ENC module does not have hasClient-method. We can only serve one client at the time. + EthernetClient newclient = _ethernetServer.available(); + // if a new client connects make sure to dispose any previous existing sockets + if (newclient) { + if (clients[0] != newclient) { + clients[0].stop(); + clients[0] = newclient; + debug(PSTR("Client connected\n")); + _w5100_spi_en(false); + gatewayTransportSend(buildGw(_msg, I_GATEWAY_READY).set("Gateway startup complete.")); + } + } + if (clients[0]) { + if (!clients[0].connected()) { + debug(PSTR("Client disconnected\n")); + clients[0].stop(); + } else { + if (_readFromClient(0)) { + _w5100_spi_en(false); + return true; + } + } + } + #endif + #endif + _w5100_spi_en(false); + return false; +} + +MyMessage& gatewayTransportReceive() +{ + // Return the last parsed message + return _ethernetMsg; +} + + +#if !defined(MY_IP_ADDRESS) && !defined(MY_GATEWAY_ESP8266) +void gatewayTransportRenewIP() +{ + /* renew/rebind IP address + 0 - nothing happened + 1 - renew failed + 2 - renew success + 3 - rebinf failed + 4 - rebind success + */ + static unsigned long next_time = hwMillis() + MY_IP_RENEWAL_INTERVAL; + unsigned long now = hwMillis(); + + // http://playground.arduino.cc/Code/TimingRollover + if ((long)(now - next_time) < 0) + return; + if (Ethernet.maintain() & ~(0x06)) { + debug(PSTR("IP was not renewed correctly\n")); + /* Error occured -> IP was not renewed */ + return; + } + _w5100_spi_en(false); + next_time = now + MY_IP_RENEWAL_INTERVAL; +} +#endif /* IP_ADDRESS_DHCP */ diff --git a/libraries/MySensors/core/MyGatewayTransportMQTTClient.cpp b/libraries/MySensors/core/MyGatewayTransportMQTTClient.cpp new file mode 100644 index 0000000..b948b96 --- /dev/null +++ b/libraries/MySensors/core/MyGatewayTransportMQTTClient.cpp @@ -0,0 +1,184 @@ +/** + * 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 "MyConfig.h" +#include "MyProtocol.h" +#include "MyGatewayTransport.h" +#include "MyMessage.h" + +// Topic structure: MY_MQTT_TOPIC_PREFIX/NODE-ID/SENSOR-ID/CMD-TYPE/ACK-FLAG/SUB-TYPE + + + +IPAddress _brokerIp(MY_CONTROLLER_IP_ADDRESS); + +#if defined(MY_GATEWAY_ESP8266) + #define EthernetClient WiFiClient + IPAddress _gatewayIp(MY_IP_GATEWAY_ADDRESS); + IPAddress _subnetIp(MY_IP_SUBNET_ADDRESS); +#else + byte _clientMAC[] = { MY_MAC_ADDRESS }; +#endif + +#if defined(MY_IP_ADDRESS) + IPAddress _clientIp(MY_IP_ADDRESS); +#endif + +EthernetClient _ethClient; +PubSubClient _client(_ethClient); +bool _available = false; +char _convBuffer[MAX_PAYLOAD*2+1]; +char _fmtBuffer[MY_GATEWAY_MAX_SEND_LENGTH]; +MyMessage _mqttMsg; + + + +bool gatewayTransportSend(MyMessage &message) { + if (!_client.connected()) + return false; + snprintf_P(_fmtBuffer, MY_GATEWAY_MAX_SEND_LENGTH, PSTR(MY_MQTT_TOPIC_PREFIX "/%d/%d/%d/%d/%d"), message.sender, message.sensor, mGetCommand(message), mGetAck(message), message.type); + debug(PSTR("Sending message on topic: %s\n"), _fmtBuffer); + return _client.publish(_fmtBuffer, message.getString(_convBuffer)); +} + + + +void incomingMQTT(char* topic, byte* payload, + unsigned int length) +{ + debug(PSTR("Message arrived on topic: %s\n"), topic); + char *str, *p; + uint8_t i = 0; + for (str = strtok_r(topic, "/", &p); str && i < 5; + str = strtok_r(NULL, "/", &p)) { + switch (i) { + case 0: { + // Topic prefix + if (strcmp_P(str, MY_MQTT_TOPIC_PREFIX) != 0) { + // Message not for us or malformed! + return; + } + break; + } + case 1: { + // Node id + _mqttMsg.destination = atoi(str); + break; + } + case 2: { + // Sensor id + _mqttMsg.sensor = atoi(str); + break; + } + case 3: { + // Command type + mSetCommand(_mqttMsg, atoi(str)); + break; + } + case 4: { + // Ack flag + mSetRequestAck(_mqttMsg, atoi(str)?1:0); + break; + } + case 5: { + // Sub type + _mqttMsg.type = atoi(str); + // Add payload + char* ca; + ca = (char *) payload; + ca += length; + *ca = '\0'; + _mqttMsg.set((const char*) payload); + _available = true; + } + } + i++; + } +} + + +bool reconnectMQTT() { + debug(PSTR("Attempting MQTT connection...\n")); + // Attempt to connect + if (_client.connect(MY_MQTT_CLIENT_ID + #if defined(MY_MQTT_USER) && defined(MY_MQTT_PASSWORD) + , MY_MQTT_USER, MY_MQTT_PASSWORD + #endif + )) { + debug(PSTR("MQTT connected\n")); + // Once connected, publish an announcement... + //_client.publish("outTopic","hello world"); + // ... and resubscribe + _client.subscribe(MY_MQTT_TOPIC_PREFIX "/+/+/+/+/+"); + return true; + } + return false; +} + +bool gatewayTransportInit() { + _client.setServer(_brokerIp, MY_PORT); + _client.setCallback(incomingMQTT); + + #if defined(MY_GATEWAY_ESP8266) + (void)WiFi.begin(MY_ESP8266_SSID, MY_ESP8266_PASSWORD); + #ifdef MY_IP_ADDRESS + WiFi.config(_clientIp, _gatewayIp, _subnetIp); + #endif + while (WiFi.status() != WL_CONNECTED) + { + delay(500); + Serial.print("."); + yield(); + } + Serial.print(F("IP: ")); + Serial.println(WiFi.localIP()); + #else + #ifdef MY_IP_ADDRESS + Ethernet.begin(_clientMAC, _clientIp); + Serial.print(F("IP: ")); + Serial.println(Ethernet.localIP()); + #else + // Get IP address from DHCP + Ethernet.begin(_clientMAC); + Serial.print(F("IP: ")); + Serial.println(Ethernet.localIP()); + #endif /* IP_ADDRESS_DHCP */ + // give the Ethernet interface a second to initialize + // TODO: use HW delay + wait(1000); + #endif + return true; +} + + +bool gatewayTransportAvailable() { + if (!_client.connected()) { + reconnectMQTT(); + return false; + } + _client.loop(); + return _available; +} + +MyMessage & gatewayTransportReceive() { + // Return the last parsed message + _available = false; + return _mqttMsg; +} diff --git a/libraries/MySensors/core/MyGatewayTransportSerial.cpp b/libraries/MySensors/core/MyGatewayTransportSerial.cpp new file mode 100644 index 0000000..b292f7b --- /dev/null +++ b/libraries/MySensors/core/MyGatewayTransportSerial.cpp @@ -0,0 +1,78 @@ +/** + * 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 "MyConfig.h" +#include "MyProtocol.h" +#include "MyGatewayTransport.h" +#include "MyMessage.h" +#include "MyProtocol.h" + + +char _serialInputString[MY_GATEWAY_MAX_RECEIVE_LENGTH]; // A buffer for incoming commands from serial interface +int _serialInputPos; +MyMessage _serialMsg; + + +bool gatewayTransportSend(MyMessage &message) { + Serial.print(protocolFormat(message)); + // Serial print is always successful + return true; +} + +bool gatewayTransportInit() { + gatewayTransportSend(buildGw(_msg, I_GATEWAY_READY).set("Gateway startup complete.")); + return true; +} + + +bool gatewayTransportAvailable() { + bool available = false; + + while (Serial.available()) { + // get the new byte: + char inChar = (char) Serial.read(); + // if the incoming character is a newline, set a flag + // so the main loop can do something about it: + if (_serialInputPos < MY_GATEWAY_MAX_RECEIVE_LENGTH - 1 && !available) { + if (inChar == '\n') { + _serialInputString[_serialInputPos] = 0; + available = true; + } else { + // add it to the inputString: + _serialInputString[_serialInputPos] = inChar; + _serialInputPos++; + } + } else { + // Incoming message too long. Throw away + _serialInputPos = 0; + } + } + if (available) { + // Parse message and return parse result + available = protocolParse(_serialMsg, _serialInputString); + _serialInputPos = 0; + } + return available; +} + +MyMessage & gatewayTransportReceive() { + // Return the last parsed message + return _serialMsg; +} diff --git a/libraries/MySensors/MyTransportNRF24.h b/libraries/MySensors/core/MyHw.h similarity index 50% rename from libraries/MySensors/MyTransportNRF24.h rename to libraries/MySensors/core/MyHw.h index 19d15e1..c4387de 100644 --- a/libraries/MySensors/MyTransportNRF24.h +++ b/libraries/MySensors/core/MyHw.h @@ -17,38 +17,37 @@ * version 2 as published by the Free Software Foundation. */ -#ifndef MyTransportNRF24_h -#define MyTransportNRF24_h +#ifndef MyHw_h +#define MyHw_h #include "MyConfig.h" -#include "MyTransport.h" -#include -#include "utility/RF24.h" -#include "utility/RF24_config.h" +#include "MyMessage.h" +#include "MyHw.h" -#define TO_ADDR(x) (RF24_BASE_RADIO_ID + x) - -#define WRITE_PIPE ((uint8_t)0) -#define CURRENT_NODE_PIPE ((uint8_t)1) -#define BROADCAST_PIPE ((uint8_t)2) - -class MyTransportNRF24 : public MyTransport -{ -public: - MyTransportNRF24(uint8_t ce=RF24_CE_PIN, uint8_t cs=RF24_CS_PIN, uint8_t paLevel=RF24_PA_LEVEL, uint8_t channel=RF24_CHANNEL, rf24_datarate_e datarate=RF24_DATARATE); - bool init(); - void setAddress(uint8_t address); - uint8_t getAddress(); - bool send(uint8_t to, const void* data, uint8_t len); - bool available(uint8_t *to); - uint8_t receive(void* data); - void powerDown(); -private: - RF24 rf24; - uint8_t _address; - uint8_t _paLevel; - uint8_t _channel; - rf24_datarate_e _datarate; -}; +#ifdef __cplusplus +#include #endif + +// Implement these as functions or macros +/* +#define hwDigitalWrite(__pin, __value) +#define hwInit() Serial.begin(BAUD_RATE) +#define hwWatchdogReset() wdt_reset() +#define hwReboot() wdt_enable(WDTO_15MS); while (1) +#define hwMillis() millis() + +void hwReadConfigBlock(void* buf, void* adr, size_t length); +void hwWriteConfigBlock(void* buf, void* adr, size_t length); +void hwWriteConfig(int adr, uint8_t value); +uint8_t hwReadConfig(int adr); +*/ + +void 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 hwDebugPrint(const char *fmt, ... ); +#endif + +#endif // #ifdef ARDUINO_ARCH_ESP8266 diff --git a/libraries/MySensors/MyHwATMega328.cpp b/libraries/MySensors/core/MyHwATMega328.cpp similarity index 58% rename from libraries/MySensors/MyHwATMega328.cpp rename to libraries/MySensors/core/MyHwATMega328.cpp index 7d67563..38e16fe 100644 --- a/libraries/MySensors/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,63 +77,63 @@ 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) - #ifdef ENABLED_SERIAL + #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; } } else { - #ifdef ENABLED_SERIAL + #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; } } else { - #ifdef ENABLED_SERIAL + #ifndef MY_DISABLED_SERIAL Serial.flush(); #endif - powerDown(SLEEP_FOREVER); + hwPowerDown(SLEEP_FOREVER); } detachInterrupt(interrupt1); detachInterrupt(interrupt2); @@ -190,25 +148,25 @@ inline uint8_t MyHwATMega328::sleep(uint8_t interrupt1, uint8_t mode1, uint8_t i -#ifdef DEBUG -void MyHwATMega328::debugPrint(bool isGW, const char *fmt, ... ) { +#ifdef MY_DEBUG +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/MyHwATMega328.h b/libraries/MySensors/core/MyHwATMega328.h similarity index 58% rename from libraries/MySensors/MyHwATMega328.h rename to libraries/MySensors/core/MyHwATMega328.h index e18b72e..65d0190 100644 --- a/libraries/MySensors/MyHwATMega328.h +++ b/libraries/MySensors/core/MyHwATMega328.h @@ -20,8 +20,6 @@ #ifndef MyHwATMega328_h #define MyHwATMega328_h -#ifdef ARDUINO_ARCH_AVR - #include "MyHw.h" #include "MyConfig.h" #include "MyMessage.h" @@ -36,14 +34,13 @@ #ifdef __cplusplus #include -#include #endif #if defined __AVR_ATmega328P__ #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" \ @@ -60,22 +57,22 @@ do { \ // 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 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))) @@ -94,28 +91,6 @@ enum period_t SLEEP_FOREVER }; -class MyHwATMega328 : public MyHw -{ -public: - MyHwATMega328(); +void hwInternalSleep(unsigned long ms); -/* 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); -#ifdef DEBUG - void debugPrint(bool isGW, const char *fmt, ... ); #endif -private: - void internalSleep(unsigned long ms); -}; -#endif -#endif // #ifdef ARDUINO_ARCH_AVR diff --git a/libraries/MySensors/MyHwESP8266.cpp b/libraries/MySensors/core/MyHwESP8266.cpp similarity index 57% rename from libraries/MySensors/MyHwESP8266.cpp rename to libraries/MySensors/core/MyHwESP8266.cpp index 9c85f62..5e91a75 100644 --- a/libraries/MySensors/MyHwESP8266.cpp +++ b/libraries/MySensors/core/MyHwESP8266.cpp @@ -17,11 +17,8 @@ * version 2 as published by the Free Software Foundation. */ -#ifdef ARDUINO_ARCH_ESP8266 - -#include "MyHw.h" #include "MyHwESP8266.h" -#include + /* int8_t pinIntTrigger = 0; @@ -43,7 +40,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,9 +50,9 @@ 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(); + hwInitConfigBlock(); uint8_t* dst = static_cast(buf); int offs = reinterpret_cast(adr); while (length-- > 0) @@ -64,9 +61,9 @@ 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(); + hwInitConfigBlock(); uint8_t* src = static_cast(buf); int offs = reinterpret_cast(adr); while (length-- > 0) @@ -76,103 +73,58 @@ 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); + hwReadConfigBlock(&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); + uint8_t curr = hwReadConfig(adr); if (curr != value) { - hw_writeConfigBlock(&value, reinterpret_cast(adr), 1); + hwWriteConfigBlock(&value, reinterpret_cast(adr), 1); } } -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 DEBUG -void MyHwESP8266::debugPrint(bool isGW, const char *fmt, ... ) { +#ifdef MY_DEBUG +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(); @@ -180,5 +132,3 @@ void MyHwESP8266::debugPrint(bool isGW, const char *fmt, ... ) { //Serial.write(freeRam()); } #endif - -#endif // #ifdef ARDUINO_ARCH_ESP8266 diff --git a/libraries/MySensors/MyHw.cpp b/libraries/MySensors/core/MyHwESP8266.h similarity index 52% rename from libraries/MySensors/MyHw.cpp rename to libraries/MySensors/core/MyHwESP8266.h index 3016a6b..202000f 100644 --- a/libraries/MySensors/MyHw.cpp +++ b/libraries/MySensors/core/MyHwESP8266.h @@ -16,8 +16,37 @@ * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. */ +#ifndef MyHwESP8266_h +#define MyHwESP8266_h #include "MyHw.h" +#include "MyConfig.h" -MyHw::MyHw() { -} +#include "MyMessage.h" + + +#ifdef __cplusplus +#include +//#include +#endif + + +#define min(a,b) ((a)<(b)?(a):(b)) +#define max(a,b) ((a)>(b)?(a):(b)) + + +// Define these as macros to save valuable space + +#define hwDigitalWrite(__pin, __value) (digitalWrite(__pin, __value)) +#define hwInit() Serial.begin(MY_BAUD_RATE); Serial.setDebugOutput(true) +#define hwWatchdogReset() wdt_reset() +#define hwReboot() wdt_enable(WDTO_15MS); while (1) +#define hwMillis() millis() + +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); + + +#endif // #ifdef ARDUINO_ARCH_ESP8266 diff --git a/libraries/MySensors/core/MyInclusionMode.cpp b/libraries/MySensors/core/MyInclusionMode.cpp new file mode 100644 index 0000000..604983a --- /dev/null +++ b/libraries/MySensors/core/MyInclusionMode.cpp @@ -0,0 +1,59 @@ +/** + * 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 "MyInclusionMode.h" + +unsigned long _inclusionStartTime; +bool _inclusionMode; + +inline void inclusionInit() { + _inclusionMode = false; + #if 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 + +} + + +void inclusionModeSet(boolean newMode) { + if (newMode != inclusionMode) { + _inclusionMode = newMode; + // Send back mode change to controller + gatewayTransportSend(buildGw(_msg, I_INCLUSION_MODE).set(_inclusionMode?1:0)); + if (_inclusionMode) { + _inclusionStartTime = hwMillis(); + } + } +} + +inline void inclusionProcess() { + #ifdef MY_INCLUSION_BUTTON_FEATURE + if (!inclusionMode && digitalRead(MY_INCLUSION_MODE_BUTTON_PIN) == LOW) { + // Start inclusion mode + inclusionModeSet(true); + } + #endif + + if (_inclusionMode && hwMillis()-_inclusionStartTime>MY_INCLUSION_MODE_DURATION*1000L) { + // inclusionTimeInMinutes minute(s) has passed.. stop inclusion mode + inclusionModeSet(false); + } +} diff --git a/libraries/MySensors/MyParserSerial.h b/libraries/MySensors/core/MyInclusionMode.h similarity index 75% rename from libraries/MySensors/MyParserSerial.h rename to libraries/MySensors/core/MyInclusionMode.h index 80d315d..03ac12b 100644 --- a/libraries/MySensors/MyParserSerial.h +++ b/libraries/MySensors/core/MyInclusionMode.h @@ -17,18 +17,19 @@ * version 2 as published by the Free Software Foundation. */ -#ifndef MyParserSerial_h -#define MyParserSerial_h +#ifndef MyInclusionMode_h +#define MyInclusionMode_h + +#include "MySensorCore.h" + +unsigned long inclusionStartTime; +bool inclusionMode; + +extern bool gatewayTransportSend(MyMessage &message); + +void inclusionInit(); +void inclusionModeSet(boolean newMode); +void inclusionProcess(); -#include "MyConfig.h" -#include "MyParser.h" -class MyParserSerial : public MyParser -{ -public: - MyParserSerial(); - bool parse(MyMessage &message, char *inputString); -private: - uint8_t h2i(char c); -}; #endif diff --git a/libraries/MySensors/core/MyLeds.cpp b/libraries/MySensors/core/MyLeds.cpp new file mode 100644 index 0000000..10ce2f5 --- /dev/null +++ b/libraries/MySensors/core/MyLeds.cpp @@ -0,0 +1,99 @@ +/** + * 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 "MyLeds.h" + +// 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; + +inline void ledsInit() { + // 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; +} + +inline void ledsProcess() { + // 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() + MY_DEFAULT_LED_BLINK_PERIOD; + + // 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 ledsBlinkRx(uint8_t cnt) { + if(_countRx == 255) { _countRx = cnt; } +} + +void ledsBlinkTx(uint8_t cnt) { + if(_countTx == 255) { _countTx = cnt; } +} + +void ledsBlinkErr(uint8_t cnt) { + if(_countErr == 255) { _countErr = cnt; } +} + diff --git a/libraries/MySensors/MyTransportRFM69.h b/libraries/MySensors/core/MyLeds.h similarity index 53% rename from libraries/MySensors/MyTransportRFM69.h rename to libraries/MySensors/core/MyLeds.h index afb7643..18e138d 100644 --- a/libraries/MySensors/MyTransportRFM69.h +++ b/libraries/MySensors/core/MyLeds.h @@ -17,32 +17,38 @@ * version 2 as published by the Free Software Foundation. */ -#ifndef MyTransportRF69_h -#define MyTransportRF69_h - -#include "MyConfig.h" -#include "MyTransport.h" -#include -#include "utility/RFM69.h" -#include +#ifndef MyLeds_h +#define MyLeds_h -class MyTransportRFM69 : public MyTransport -{ -public: - MyTransportRFM69(uint8_t freqBand=RFM69_FREQUENCY, uint8_t networkId=RFM69_NETWORKID, uint8_t slaveSelectPin=RF69_SPI_CS, uint8_t interruptPin=RF69_IRQ_PIN, bool isRFM69HW=false, uint8_t interruptNum=RF69_IRQ_NUM); - bool init(); - void setAddress(uint8_t address); - uint8_t getAddress(); - bool send(uint8_t to, const void* data, uint8_t len); - bool available(uint8_t *to); - uint8_t receive(void* data); - void powerDown(); -private: - RFM69 radio; - uint8_t _address; - uint8_t _freqBand; - uint8_t _networkId; -}; +#ifdef MY_WITH_LEDS_BLINKING_INVERSE +#define LED_ON 0x1 +#define LED_OFF 0x0 +#else +#define LED_ON 0x0 +#define LED_OFF 0x1 +#endif + +#ifdef MY_LEDS_BLINKING_FEATURE + #define ledBlinkTx(x,...) ledsBlinkTx(x) + #define ledBlinkRx(x,...) ledsBlinkRx(x) + #define ledBlinkErr(x,...) ledsBlinkErr(x) + + /** + * Blink with LEDs + * @param cnt how many blink cycles to keep the LED on. Default cycle is 300ms + */ + void ledsInit(); + void ledsBlinkRx(uint8_t cnt); + void ledsBlinkTx(uint8_t cnt); + void ledsBlinkErr(uint8_t cnt); + void ledsProcess(); // do the actual blinking + +#else + // Remove led functions if feature is disabled + #define ledBlinkTx(x,...) + #define ledBlinkRx(x,...) + #define ledBlinkErr(x,...) +#endif #endif diff --git a/libraries/MySensors/core/MyMainDefault.cpp b/libraries/MySensors/core/MyMainDefault.cpp new file mode 100644 index 0000000..f4f0ee8 --- /dev/null +++ b/libraries/MySensors/core/MyMainDefault.cpp @@ -0,0 +1,16 @@ +// Initialize library and handle sketch functions like we want to +int main(void) { + init(); + #if defined(USBCON) + USBDevice.attach(); + #endif + _begin(); // Startup MySensors library + if (setup) setup(); // Call sketch setup + + for(;;) { + _process(); // Process incoming data + if (loop) loop(); // Call sketch loop + if (serialEventRun) serialEventRun(); + } + return 0; +} diff --git a/libraries/MySensors/core/MyMainESP8266.cpp b/libraries/MySensors/core/MyMainESP8266.cpp new file mode 100644 index 0000000..158ca74 --- /dev/null +++ b/libraries/MySensors/core/MyMainESP8266.cpp @@ -0,0 +1,134 @@ + +extern "C" { +#include "ets_sys.h" +#include "os_type.h" +#include "osapi.h" +#include "mem.h" +#include "user_interface.h" +#include "cont.h" +} + +#define LOOP_TASK_PRIORITY 0 +#define LOOP_QUEUE_SIZE 1 + +#define OPTIMISTIC_YIELD_TIME_US 16000 + +struct rst_info resetInfo; + +int atexit(void (*func)()) { + return 0; +} + +extern "C" void ets_update_cpu_frequency(int freqmhz); +void initVariant() __attribute__((weak)); +void initVariant() { +} + + + +void preloop_update_frequency() __attribute__((weak)); +void preloop_update_frequency() { +#if defined(F_CPU) && (F_CPU == 160000000L) + REG_SET_BIT(0x3ff00014, BIT(0)); + ets_update_cpu_frequency(160); +#endif +} + +extern void (*__init_array_start)(void); +extern void (*__init_array_end)(void); + +cont_t g_cont __attribute__ ((aligned (16))); +static os_event_t g_loop_queue[LOOP_QUEUE_SIZE]; + +static uint32_t g_micros_at_task_start; + + +extern "C" void abort() { + do { + *((int*)0) = 0; + } while(true); +} + +extern "C" void esp_yield() { + if (cont_can_yield(&g_cont)) { + cont_yield(&g_cont); + } +} + +extern "C" void esp_schedule() { + system_os_post(LOOP_TASK_PRIORITY, 0, 0); +} + +extern "C" void __yield() { + if (cont_can_yield(&g_cont)) { + esp_schedule(); + esp_yield(); + } + else { + abort(); + } +} + +extern "C" void yield(void) __attribute__ ((weak, alias("__yield"))); + +extern "C" void optimistic_yield(uint32_t interval_us) { + if (cont_can_yield(&g_cont) && + (system_get_time() - g_micros_at_task_start) > interval_us) + { + yield(); + } +} + +static void loop_wrapper() { + static bool setup_done = false; + if(!setup_done) { + _begin(); // Startup MySensors library + setup(); + setup_done = true; + } + preloop_update_frequency(); + _process(); // Process incoming data + loop(); + esp_schedule(); +} + +static void loop_task(os_event_t *events) { + g_micros_at_task_start = system_get_time(); + cont_run(&g_cont, &loop_wrapper); + if(cont_check(&g_cont) != 0) { + ets_printf("\r\nsketch stack overflow detected\r\n"); + abort(); + } +} + +static void do_global_ctors(void) { + void (**p)(void); + for(p = &__init_array_start; p != &__init_array_end; ++p) + (*p)(); +} + +void init_done() { + system_set_os_print(1); + do_global_ctors(); + esp_schedule(); +} + + +extern "C" void user_init(void) { + struct rst_info *rtc_info_ptr = system_get_rst_info(); + memcpy((void *) &resetInfo, (void *) rtc_info_ptr, sizeof(resetInfo)); + + uart_div_modify(0, UART_CLK_FREQ / (115200)); + + init(); + + initVariant(); + + cont_init(&g_cont); + + system_os_task(loop_task, + LOOP_TASK_PRIORITY, g_loop_queue, + LOOP_QUEUE_SIZE); + + system_init_done_cb(&init_done); +} diff --git a/libraries/MySensors/MyMessage.cpp b/libraries/MySensors/core/MyMessage.cpp similarity index 100% rename from libraries/MySensors/MyMessage.cpp rename to libraries/MySensors/core/MyMessage.cpp diff --git a/libraries/MySensors/MyMessage.h b/libraries/MySensors/core/MyMessage.h similarity index 99% rename from libraries/MySensors/MyMessage.h rename to libraries/MySensors/core/MyMessage.h index 5a9fc43..ec94e3a 100644 --- a/libraries/MySensors/MyMessage.h +++ b/libraries/MySensors/core/MyMessage.h @@ -151,7 +151,7 @@ typedef enum { I_INCLUSION_MODE, I_CONFIG, I_FIND_PARENT, I_FIND_PARENT_RESPONSE, I_LOG_MESSAGE, I_CHILDREN, I_SKETCH_NAME, I_SKETCH_VERSION, I_REBOOT, I_GATEWAY_READY, I_REQUEST_SIGNING, I_GET_NONCE, I_GET_NONCE_RESPONSE, - I_HEARTBEAT + I_HEARTBEAT, I_PRESENTATION } mysensor_internal; diff --git a/libraries/MySensors/MyParser.h b/libraries/MySensors/core/MyProtocol.h similarity index 71% rename from libraries/MySensors/MyParser.h rename to libraries/MySensors/core/MyProtocol.h index 39e2975..324759a 100644 --- a/libraries/MySensors/MyParser.h +++ b/libraries/MySensors/core/MyProtocol.h @@ -17,19 +17,19 @@ * version 2 as published by the Free Software Foundation. */ -#ifndef MyParser_h -#define MyParser_h +#ifndef MyProtocol_h +#define MyProtocol_h +#include "MyConfig.h" #include "MyMessage.h" -class MyParser -{ -public: - // MyParser constructor - MyParser(); - // parse(message, inputString) - // parse a string into a message element - // returns true if successfully parsed the input string - virtual bool parse(MyMessage &message, char *inputString) = 0; -}; + +// parse(message, inputString) +// parse a string into a message element +// returns true if successfully parsed the input string +bool protocolParse(MyMessage &message, char *inputString); + +// Format MyMessage to the protocol represenataion +char *protocolFormat(MyMessage &message); + #endif diff --git a/libraries/MySensors/MyParserSerial.cpp b/libraries/MySensors/core/MyProtocolMySensors.cpp similarity index 75% rename from libraries/MySensors/MyParserSerial.cpp rename to libraries/MySensors/core/MyProtocolMySensors.cpp index e2ee52a..f9ffe12 100644 --- a/libraries/MySensors/MyParserSerial.cpp +++ b/libraries/MySensors/core/MyProtocolMySensors.cpp @@ -17,13 +17,16 @@ * version 2 as published by the Free Software Foundation. */ -#include "MyParser.h" -#include "MyParserSerial.h" +#include "MyConfig.h" #include "MyTransport.h" +#include "MyProtocol.h" -MyParserSerial::MyParserSerial() : MyParser() {} +uint8_t protocolH2i(char c); -bool MyParserSerial::parse(MyMessage &message, char *inputString) { +char _fmtBuffer[MY_GATEWAY_MAX_SEND_LENGTH]; +char _convBuffer[MAX_PAYLOAD*2+1]; + +bool protocolParse(MyMessage &message, char *inputString) { char *str, *p, *value=NULL; uint8_t bvalue[MAX_PAYLOAD]; uint8_t blen = 0; @@ -58,22 +61,25 @@ bool MyParserSerial::parse(MyMessage &message, char *inputString) { blen = 0; uint8_t val; while (*str) { - val = h2i(*str++) << 4; - val += h2i(*str++); + val = protocolH2i(*str++) << 4; + val += protocolH2i(*str++); bvalue[blen] = val; blen++; } } else { value = str; - // Remove ending carriage return character (if it exists) + // Remove trailing carriage return and newline character (if it exists) uint8_t lastCharacter = strlen(value)-1; if (value[lastCharacter] == '\r') value[lastCharacter] = 0; + if (value[lastCharacter] == '\n') + value[lastCharacter] = 0; } break; } i++; } + //debug(PSTR("Received %d"), i); // Check for invalid input if (i < 5) return false; @@ -89,7 +95,12 @@ bool MyParserSerial::parse(MyMessage &message, char *inputString) { return true; } -uint8_t MyParserSerial::h2i(char c) { +char * protocolFormat(MyMessage &message) { + snprintf_P(_fmtBuffer, MY_GATEWAY_MAX_SEND_LENGTH, PSTR("%d;%d;%d;%d;%d;%s\n"), message.sender, message.sensor, mGetCommand(message), mGetAck(message), message.type, message.getString(_convBuffer)); + return _fmtBuffer; +} + +uint8_t protocolH2i(char c) { uint8_t i = 0; if (c <= '9') i += c - '0'; @@ -99,3 +110,5 @@ uint8_t MyParserSerial::h2i(char c) { i += c - 'A' + 10; return i; } + + diff --git a/libraries/MySensors/core/MySensorCore.cpp b/libraries/MySensors/core/MySensorCore.cpp new file mode 100644 index 0000000..a67383b --- /dev/null +++ b/libraries/MySensors/core/MySensorCore.cpp @@ -0,0 +1,351 @@ +/** + * 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 "MySensorCore.h" + +ControllerConfig _cc; // Configuration coming from controller +NodeConfig _nc; // Essential settings for node to work +MyMessage _msg; // Buffer for incoming messages. +MyMessage _msgTmp; // Buffer for temporary messages (acks and nonces among others). + +#ifdef MY_DEBUG + char _convBuf[MAX_PAYLOAD*2+1]; +#endif + +uint16_t _heartbeat; +void (*_timeCallback)(unsigned long); // Callback for requested time messages + + +void _process() { + hwWatchdogReset(); + + + #if defined (MY_LEDS_BLINKING_FEATURE) + ledsProcess(); + #endif + + #if defined(MY_INCLUSION_MODE_FEATURE) + inclusionProcess(); + #endif + + #if defined(MY_GATEWAY_FEATURE) + gatewayTransportProcess(); + #endif + + #if defined(MY_RADIO_FEATURE) + transportProcess(); + #endif +} + +#if defined(MY_RADIO_FEATURE) +static inline bool isValidParent( const uint8_t parent ) { + return parent != AUTO; +} +#endif + +void _begin() { + #if !defined(MY_DISABLED_SERIAL) + hwInit(); + #endif + + debug(PSTR("Starting...\n")); + #if defined(MY_RADIO_FEATURE) + _failedTransmissions = 0; + + // Setup radio + if (!transportInit()) { + debug(PSTR("Radio init fail\n")); + // Nothing more we can do + while(1) { + #if defined(MY_GATEWAY_ESP8266) + yield(); + #endif + }; + } + #endif + + #if defined(MY_GATEWAY_FEATURE) + #if defined(MY_INCLUSION_BUTTON_FEATURE) + inclusionInit(); + #endif + + // initialize the transport driver + if (!gatewayTransportInit()) { + debug(PSTR("Transport driver init fail\n")); + // Nothing more we can do + while(1) { + #if defined(MY_GATEWAY_ESP8266) + yield(); + #endif + } + } + + #endif + + + #if defined(MY_SIGNING_FEATURE) + // Read out the signing requirements from EEPROM + hwReadConfigBlock((void*)_doSign, (void*)EEPROM_SIGNING_REQUIREMENT_TABLE_ADDRESS, sizeof(_doSign)); + #endif + + #if defined(MY_LEDS_BLINKING_FEATURE) + ledsInit(); + #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; + } + + #if defined(MY_GATEWAY_FEATURE) + // Set configuration for gateway + _nc.parentNodeId = GATEWAY_ADDRESS; + _nc.distance = 0; + _nc.nodeId = GATEWAY_ADDRESS; + #elif defined(MY_RADIO_FEATURE) + // 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 + + _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. + transportFindParentNode(); + } + + 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 + transportRequestNodeId(); + } + #endif + + #if defined(MY_RADIO_FEATURE) + transportPresentNode(); + #endif + if (presentation) + presentation(); + debug(PSTR("%s started, id=%d, parent=%d, distance=%d\n"), MY_NODE_TYPE, _nc.nodeId, _nc.parentNodeId, _nc.distance); +} + + + + +uint8_t getNodeId() { + return _nc.nodeId; +} + +ControllerConfig getConfig() { + return _cc; +} + + +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. + ledBlinkTx(1); + return gatewayTransportSend(message); + } + #endif + #if defined(MY_RADIO_FEATURE) + return transportSendRoute(message); + #else + return false; + #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() { + _sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_TIME, false).set("")); +} + +// Message delivered through _msg +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_PRESENTATION) { + // Re-send node presentation to controller + #if defined(MY_RADIO_FEATURE) + transportPresentNode(); + #endif + if (presentation) + presentation(); + } else if (type == I_HEARTBEAT) { + if (!mGetAck(_msg)) { + // Send heartbeat ack message back to sender (with the same payload) + _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); + } + } else if (type == I_TIME && receiveTime) { + // Deliver time to callback + receiveTime(_msg.getULong()); + } + #if defined(MY_REPEATER_FEATURE) + if (type == I_CHILDREN && _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 + transportFindParentNode(); + _sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_CHILDREN,false).set("")); + } + #endif +} + + +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); +} + + +void wait(unsigned long ms) { + unsigned long enter = hwMillis(); + while (hwMillis() - enter < ms) { + _process(); + yield(); + } +} + +void sleep(unsigned long ms) { + #if defined(MY_OTA_FIRMWARE_FEATURE) + if (_fwUpdateOngoing) { + // Do not sleep node while fw update is ongoing + _process(); + return; + } else { + #endif + #if defined(MY_RADIO_FEATURE) + transportPowerDown(); + #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 + return false; + } else { + #endif + #if defined(MY_RADIO_FEATURE) + transportPowerDown(); + #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 + return -1; + } else { + #endif + #if defined(MY_RADIO_FEATURE) + transportPowerDown(); + #endif + return hwSleep(interrupt1, mode1, interrupt2, mode2, ms) ; + #if defined(MY_OTA_FIRMWARE_FEATURE) + } + #endif +} + + + + diff --git a/libraries/MySensors/core/MySensorCore.h b/libraries/MySensors/core/MySensorCore.h new file mode 100644 index 0000000..30edd42 --- /dev/null +++ b/libraries/MySensors/core/MySensorCore.h @@ -0,0 +1,257 @@ +/** + * 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 MySensorCore_h +#define MySensorCore_h + +#include "Version.h" // Auto generated by bot +#include "MyConfig.h" +#ifdef MY_SIGNING_FEATURE +#include "MySigning.h" +#include "MySigningNone.h" +#endif +#include "MyMessage.h" +#ifdef MY_OTA_FIRMWARE_FEATURE +#include "drivers/SPIFlash/SPIFlash.h" +#endif + +#include +#include + +#ifdef MY_DEBUG +#define debug(x,...) hwDebugPrint(x, ##__VA_ARGS__) +#else +#define debug(x,...) +#endif + +// EEPROM start address for mysensors library data +#define EEPROM_START 0 +// EEPROM location of node id +#define EEPROM_NODE_ID_ADDRESS EEPROM_START +// EEPROM location of parent id +#define EEPROM_PARENT_NODE_ID_ADDRESS (EEPROM_START+1) +// EEPROM location of distance to gateway +#define EEPROM_DISTANCE_ADDRESS (EEPROM_PARENT_NODE_ID_ADDRESS+1) +#define EEPROM_ROUTES_ADDRESS (EEPROM_DISTANCE_ADDRESS+1) // Where to start storing routing information in EEPROM. Will allocate 256 bytes. +#define EEPROM_CONTROLLER_CONFIG_ADDRESS (EEPROM_ROUTES_ADDRESS+256) // Location of controller sent configuration (we allow one payload of config data from controller) +#define EEPROM_FIRMWARE_TYPE_ADDRESS (EEPROM_CONTROLLER_CONFIG_ADDRESS+24) +#define EEPROM_FIRMWARE_VERSION_ADDRESS (EEPROM_FIRMWARE_TYPE_ADDRESS+2) +#define EEPROM_FIRMWARE_BLOCKS_ADDRESS (EEPROM_FIRMWARE_VERSION_ADDRESS+2) +#define EEPROM_FIRMWARE_CRC_ADDRESS (EEPROM_FIRMWARE_BLOCKS_ADDRESS+2) +#define EEPROM_SIGNING_REQUIREMENT_TABLE_ADDRESS (EEPROM_FIRMWARE_CRC_ADDRESS+2) +#define EEPROM_LOCAL_CONFIG_ADDRESS (EEPROM_SIGNING_REQUIREMENT_TABLE_ADDRESS+32) // First free address for sketch static configuration + +// This is the nodeId for sensor net gateway receiver sketch (where all sensors should send their data). +#define GATEWAY_ADDRESS ((uint8_t)0) +// Node child is always created/presented when a node is started +#define NODE_SENSOR_ID 0xFF + + +struct NodeConfig +{ + uint8_t nodeId; // Current node id + uint8_t parentNodeId; // Where this node sends its messages + uint8_t distance; // This nodes distance to sensor net gateway (number of hops) +}; + +struct ControllerConfig { + uint8_t isMetric; +}; + + + +/** + * Return this nodes id. + */ +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); + + +/** + * 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); + +/** + * 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); + +/** +* 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); + +/** + * Requests time from controller. Answer will be delivered to receiveTime function in sketch. + * + */ +void requestTime(); + + + +/** + * Returns the most recent node configuration received from controller + */ +ControllerConfig getConfig(); + +/** + * 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); + +/** + * 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); + +/** + * 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(); + +boolean _sendRoute(MyMessage &message); + +extern NodeConfig _nc; +extern MyMessage _msg; // Buffer for incoming messages. +extern MyMessage _msgTmp; // Buffer for temporary messages (acks and nonces among others). +#ifdef MY_DEBUG + extern char _convBuf[MAX_PAYLOAD*2+1]; +#endif +void receive(const MyMessage &message) __attribute__((weak)); +void receiveTime(unsigned long) __attribute__((weak)); +void presentation() __attribute__((weak)); + +// 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) { + msg.sender = sender; + msg.destination = destination; + msg.sensor = sensor; + msg.type = type; + mSetCommand(msg,command); + mSetRequestAck(msg,enableAck); + mSetAck(msg,false); + return msg; +} + +static inline MyMessage& buildGw(MyMessage &msg, uint8_t type) { + msg.sender = GATEWAY_ADDRESS; + msg.destination = GATEWAY_ADDRESS; + msg.sensor = 0; + msg.type = type; + mSetCommand(msg, C_INTERNAL); + mSetRequestAck(msg, false); + mSetAck(msg, false); + return msg; +} + + +#endif diff --git a/libraries/MySensors/MySigning.cpp b/libraries/MySensors/core/MySigning.cpp similarity index 84% rename from libraries/MySensors/MySigning.cpp rename to libraries/MySensors/core/MySigning.cpp index 03c3ee3..e6350c8 100644 --- a/libraries/MySensors/MySigning.cpp +++ b/libraries/MySensors/core/MySigning.cpp @@ -19,10 +19,7 @@ #include "MySigning.h" -MySigning::MySigning(bool requestSignatures) : _requestSignatures(requestSignatures) { -} +uint16_t _doSign[16]; // Bitfield indicating which sensors require signed communication +MyMessage _msgSign; // Buffer for message to sign. -bool MySigning::requestSignatures() { - return _requestSignatures; -} diff --git a/libraries/MySensors/core/MySigning.h b/libraries/MySensors/core/MySigning.h new file mode 100644 index 0000000..5027160 --- /dev/null +++ b/libraries/MySensors/core/MySigning.h @@ -0,0 +1,86 @@ +/** + * 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. + * + ******************************* + * + * DESCRIPTION + * Signing support created by Patrick "Anticimex" Fallberg + * The MySigning driver provides a generic API for various signing backends to offer + * signing of MySensors messages. + * + */ +#ifndef MySigning_h +#define MySigning_h + +#include "MyConfig.h" +#include +#include "MyMessage.h" +#include "drivers/ATSHA204/ATSHA204.h" + +#ifdef MY_SIGNING_NODE_WHITELISTING +typedef struct { + uint8_t nodeId; + uint8_t serial[SHA204_SERIAL_SZ]; +} whitelist_entry_t; +#endif + +// 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 NUM_OF(x) (sizeof(x)/sizeof(x[0])) + +// Stores signing identifier and a new nonce in provided message for signing operations. +// All space in message payload buffer is used for signing identifier and nonce. +// Returns false if subsystem is busy processing an ongoing signing operation. +// If successful, this marks the start of a signing operation at the receiving side so +// implementation is expected to do any necessary initializations within this call. +bool signerGetNonce(MyMessage &msg); + +// Check timeout of verification session. +// Nonce will be purged if it takes too long for a signed message to be sent to the receiver. +// This function should be called on regular intervals. +bool signerCheckTimer(void); + +// Get nonce from provided message and store for signing operations. +// Returns false if subsystem is busy processing an ongoing signing operation. +// Returns false if signing identifier found in message is not supported by the used backend. +// If successful, this marks the start of a signing operation at the sending side so +// implementation is expected to do any necessary initializations within this call. +bool signerPutNonce(MyMessage &msg); + +// Signs provided message. All remaining space in message payload buffer is used for signing +// identifier and signature. +// Nonce used for signature calculation is the one generated previously using getNonce(). +// Nonce will be cleared when this function is called to prevent re-use of nonce. +// Returns false if subsystem is busy processing an ongoing signing operation. +// Returns false if not two bytes or more of free payload space is left in provided message. +// This ends a signing operation at the sending side so implementation is expected to do any +// deinitializations and enter a power saving state within this call. +bool signerSignMsg(MyMessage &msg); + +// Verifies signature in provided message. +// Nonce used for verification is the one previously set using putNonce(). +// Nonce will be cleared when this function is called to prevent re-use of nonce. +// Returns false if subsystem is busy processing an ongoing signing operation. +// Returns false if signing identifier found in message is not supported by the used backend. +// This ends a signing operation at the receiving side so implementation is expected to do any +// deinitializations and enter a power saving state within this call. +bool signerVerifyMsg(MyMessage &msg); + +#endif diff --git a/libraries/MySensors/MySigningAtsha204.cpp b/libraries/MySensors/core/MySigningAtsha204.cpp similarity index 53% rename from libraries/MySensors/MySigningAtsha204.cpp rename to libraries/MySensors/core/MySigningAtsha204.cpp index 06d1b45..6bc735c 100644 --- a/libraries/MySensors/MySigningAtsha204.cpp +++ b/libraries/MySensors/core/MySigningAtsha204.cpp @@ -27,13 +27,28 @@ #if defined(ARDUINO_ARCH_AVR) #include "MySigning.h" -#include "MySigningAtsha204.h" #define SIGNING_IDENTIFIER (1) // Uncomment this to get some useful serial debug info (Serial.print and Serial.println expected) //#define DEBUG_SIGNING +ATSHA204Class atsha204(MY_SIGNING_ATSHA204_PIN); +unsigned long _signing_timestamp; +bool _signing_verification_ongoing = false; +uint8_t _signing_current_nonce[NONCE_NUMIN_SIZE_PASSTHROUGH+SHA204_SERIAL_SZ+1]; +uint8_t _signing_temp_message[SHA_MSG_SIZE]; +uint8_t _singning_rx_buffer[SHA204_RSP_SIZE_MAX]; +uint8_t _singning_tx_buffer[SHA204_CMD_SIZE_MAX]; + +#ifdef MY_SIGNING_NODE_WHITELISTING + const whitelist_entry_t _signing_whitelist[] = MY_SIGNING_NODE_WHITELISTING; +#endif + +void signerCalculateSignature(MyMessage &msg); +uint8_t* signerSha256(const uint8_t* data, size_t sz); + + #ifdef DEBUG_SIGNING #define DEBUG_SIGNING_PRINTLN(args) Serial.println(args) #else @@ -59,79 +74,63 @@ static void DEBUG_SIGNING_PRINTBUF(const __FlashStringHelper* str, uint8_t* buf, #endif -MySigningAtsha204::MySigningAtsha204(bool requestSignatures, -#ifdef MY_SECURE_NODE_WHITELISTING - uint8_t nof_whitelist_entries, const whitelist_entry_t* the_whitelist, -#endif - uint8_t atshaPin) - : - MySigning(requestSignatures), - atsha204(atshaPin), -#ifdef MY_SECURE_NODE_WHITELISTING - whitelist(the_whitelist), - whitlist_sz(nof_whitelist_entries), -#endif - verification_ongoing(false) -{ -} - -bool MySigningAtsha204::getNonce(MyMessage &msg) { +bool signerGetNonce(MyMessage &msg) { // Generate random number for use as nonce // We used a basic whitening technique that takes the first byte of a new random value and builds up a 32-byte random value // This 32-byte random value is then hashed (SHA256) to produce the resulting nonce for (int i = 0; i < 32; i++) { if (atsha204.sha204m_execute(SHA204_RANDOM, RANDOM_NO_SEED_UPDATE, 0, 0, NULL, - RANDOM_COUNT, tx_buffer, RANDOM_RSP_SIZE, rx_buffer) != SHA204_SUCCESS) { + RANDOM_COUNT, _singning_tx_buffer, RANDOM_RSP_SIZE, _singning_rx_buffer) != SHA204_SUCCESS) { DEBUG_SIGNING_PRINTLN(F("FTGN")); // FTGN = Failed to generate nonce return false; } - current_nonce[i] = rx_buffer[SHA204_BUFFER_POS_DATA]; + _signing_current_nonce[i] = _singning_rx_buffer[SHA204_BUFFER_POS_DATA]; } - memcpy(current_nonce, sha256(current_nonce, 32), MAX_PAYLOAD); + memcpy(_signing_current_nonce, signerSha256(_signing_current_nonce, 32), MAX_PAYLOAD); // We set the part of the 32-byte nonce that does not fit into a message to 0xAA - memset(¤t_nonce[MAX_PAYLOAD], 0xAA, sizeof(current_nonce)-MAX_PAYLOAD); + memset(&_signing_current_nonce[MAX_PAYLOAD], 0xAA, sizeof(_signing_current_nonce)-MAX_PAYLOAD); // Replace the first byte in the nonce with our signing identifier - current_nonce[0] = SIGNING_IDENTIFIER; + _signing_current_nonce[0] = SIGNING_IDENTIFIER; // Transfer the first part of the nonce to the message - msg.set(current_nonce, MAX_PAYLOAD); - verification_ongoing = true; - timestamp = millis(); // Set timestamp to determine when to purge nonce + msg.set(_signing_current_nonce, MAX_PAYLOAD); + _signing_verification_ongoing = true; + _signing_timestamp = millis(); // Set timestamp to determine when to purge nonce // Be a little fancy to handle turnover (prolong the time allowed to timeout after turnover) // Note that if message is "too" quick, and arrives before turnover, it will be rejected // but this is consider such a rare case that it is accepted and rejects are 'safe' - if (timestamp + MY_VERIFICATION_TIMEOUT_MS < millis()) timestamp = 0; + if (_signing_timestamp + MY_VERIFICATION_TIMEOUT_MS < millis()) _signing_timestamp = 0; return true; } -bool MySigningAtsha204::checkTimer() { - if (verification_ongoing) { - if (millis() < timestamp || millis() > timestamp + MY_VERIFICATION_TIMEOUT_MS) { +bool signerCheckTimer() { + if (_signing_verification_ongoing) { + if (millis() < _signing_timestamp || millis() > _signing_timestamp + MY_VERIFICATION_TIMEOUT_MS) { DEBUG_SIGNING_PRINTLN(F("VT")); // VT = Verification timeout // Purge nonce - memset(current_nonce, 0x00, NONCE_NUMIN_SIZE_PASSTHROUGH); - verification_ongoing = false; + memset(_signing_current_nonce, 0x00, NONCE_NUMIN_SIZE_PASSTHROUGH); + _signing_verification_ongoing = false; return false; } } return true; } -bool MySigningAtsha204::putNonce(MyMessage &msg) { +bool signerPutNonce(MyMessage &msg) { if (((uint8_t*)msg.getCustom())[0] != SIGNING_IDENTIFIER) { DEBUG_SIGNING_PRINTLN(F("ISI")); // ISI = Incorrect signing identifier return false; } - memcpy(current_nonce, (uint8_t*)msg.getCustom(), MAX_PAYLOAD); + memcpy(_signing_current_nonce, (uint8_t*)msg.getCustom(), MAX_PAYLOAD); // We set the part of the 32-byte nonce that does not fit into a message to 0xAA - memset(¤t_nonce[MAX_PAYLOAD], 0xAA, sizeof(current_nonce)-MAX_PAYLOAD); + memset(&_signing_current_nonce[MAX_PAYLOAD], 0xAA, sizeof(_signing_current_nonce)-MAX_PAYLOAD); return true; } -bool MySigningAtsha204::signMsg(MyMessage &msg) { +bool signerSignMsg(MyMessage &msg) { // If we cannot fit any signature in the message, refuse to sign it if (mGetLength(msg) > MAX_PAYLOAD-2) { DEBUG_SIGNING_PRINTLN(F("MTOL")); // Message too large for signature to fit @@ -140,37 +139,37 @@ bool MySigningAtsha204::signMsg(MyMessage &msg) { // Calculate signature of message mSetSigned(msg, 1); // make sure signing flag is set before signature is calculated - calculateSignature(msg); + signerCalculateSignature(msg); -#ifdef MY_SECURE_NODE_WHITELISTING +#ifdef MY_SIGNING_NODE_WHITELISTING // Salt the signature with the senders nodeId and the unique serial of the ATSHA device - memcpy(current_nonce, &rx_buffer[SHA204_BUFFER_POS_DATA], 32); // We can reuse the nonce buffer now since it is no longer needed - current_nonce[32] = msg.sender; - atsha204.getSerialNumber(¤t_nonce[33]); - (void)sha256(current_nonce, 32+1+SHA204_SERIAL_SZ); // we can 'void' sha256 because the hash is already put in the correct place + memcpy(_signing_current_nonce, &_singning_rx_buffer[SHA204_BUFFER_POS_DATA], 32); // We can reuse the nonce buffer now since it is no longer needed + _signing_current_nonce[32] = msg.sender; + atsha204.getSerialNumber(&_signing_current_nonce[33]); + (void)signerSha256(_signing_current_nonce, 32+1+SHA204_SERIAL_SZ); // we can 'void' sha256 because the hash is already put in the correct place DEBUG_SIGNING_PRINTLN(F("SWS")); // SWS = Signature whitelist salted #endif // Overwrite the first byte in the signature with the signing identifier - rx_buffer[SHA204_BUFFER_POS_DATA] = SIGNING_IDENTIFIER; + _singning_rx_buffer[SHA204_BUFFER_POS_DATA] = SIGNING_IDENTIFIER; // Transfer as much signature data as the remaining space in the message permits - memcpy(&msg.data[mGetLength(msg)], &rx_buffer[SHA204_BUFFER_POS_DATA], MAX_PAYLOAD-mGetLength(msg)); + memcpy(&msg.data[mGetLength(msg)], &_singning_rx_buffer[SHA204_BUFFER_POS_DATA], MAX_PAYLOAD-mGetLength(msg)); return true; } -bool MySigningAtsha204::verifyMsg(MyMessage &msg) { - if (!verification_ongoing) { +bool signerVerifyMsg(MyMessage &msg) { + if (!_signing_verification_ongoing) { DEBUG_SIGNING_PRINTLN(F("NAVS")); // NAVS = No active verification session return false; } else { // Make sure we have not expired - if (!checkTimer()) { + if (!signerCheckTimer()) { return false; } - verification_ongoing = false; + _signing_verification_ongoing = false; if (msg.data[mGetLength(msg)] != SIGNING_IDENTIFIER) { DEBUG_SIGNING_PRINTLN(F("ISI")); // ISI = Incorrect signing identifier @@ -178,29 +177,29 @@ bool MySigningAtsha204::verifyMsg(MyMessage &msg) { } DEBUG_SIGNING_PRINTBUF(F("SIM:"), (uint8_t*)&msg.data[mGetLength(msg)], MAX_PAYLOAD-mGetLength(msg)); // SIM = Signature in message - calculateSignature(msg); // Get signature of message + signerCalculateSignature(msg); // Get signature of message -#ifdef MY_SECURE_NODE_WHITELISTING +#ifdef MY_SIGNING_NODE_WHITELISTING // Look up the senders nodeId in our whitelist and salt the signature with that data - for (int j=0; j < whitlist_sz; j++) { - if (whitelist[j].nodeId == msg.sender) { + for (int j=0; j < NUM_OF(_signing_whitelist); j++) { + if (_signing_whitelist[j].nodeId == msg.sender) { DEBUG_SIGNING_PRINTLN(F("SIW")); // SIW = Sender found in whitelist - memcpy(current_nonce, &rx_buffer[SHA204_BUFFER_POS_DATA], 32); // We can reuse the nonce buffer now since it is no longer needed - current_nonce[32] = msg.sender; - memcpy(¤t_nonce[33], whitelist[j].serial, SHA204_SERIAL_SZ); - (void)sha256(current_nonce, 32+1+SHA204_SERIAL_SZ); // we can 'void' sha256 because the hash is already put in the correct place + memcpy(_signing_current_nonce, &_singning_rx_buffer[SHA204_BUFFER_POS_DATA], 32); // We can reuse the nonce buffer now since it is no longer needed + _signing_current_nonce[32] = msg.sender; + memcpy(&_signing_current_nonce[33], _signing_whitelist[j].serial, SHA204_SERIAL_SZ); + (void)signerSha256(_signing_current_nonce, 32+1+SHA204_SERIAL_SZ); // we can 'void' sha256 because the hash is already put in the correct place break; } } #endif // Overwrite the first byte in the signature with the signing identifier - rx_buffer[SHA204_BUFFER_POS_DATA] = SIGNING_IDENTIFIER; + _singning_rx_buffer[SHA204_BUFFER_POS_DATA] = SIGNING_IDENTIFIER; // Compare the caluclated signature with the provided signature - if (memcmp(&msg.data[mGetLength(msg)], &rx_buffer[SHA204_BUFFER_POS_DATA], MAX_PAYLOAD-mGetLength(msg))) { - DEBUG_SIGNING_PRINTBUF(F("SNOK:"), &rx_buffer[SHA204_BUFFER_POS_DATA], MAX_PAYLOAD-mGetLength(msg)); // SNOK = Signature bad -#ifdef MY_SECURE_NODE_WHITELISTING + if (memcmp(&msg.data[mGetLength(msg)], &_singning_rx_buffer[SHA204_BUFFER_POS_DATA], MAX_PAYLOAD-mGetLength(msg))) { + DEBUG_SIGNING_PRINTBUF(F("SNOK:"), &_singning_rx_buffer[SHA204_BUFFER_POS_DATA], MAX_PAYLOAD-mGetLength(msg)); // SNOK = Signature bad +#ifdef MY_SIGNING_NODE_WHITELISTING DEBUG_SIGNING_PRINTLN(F("W?")); // W? = Is the sender whitelisted? #endif return false; @@ -211,60 +210,60 @@ bool MySigningAtsha204::verifyMsg(MyMessage &msg) { } } -// Helper to calculate signature of msg (returned in rx_buffer[SHA204_BUFFER_POS_DATA]) -void MySigningAtsha204::calculateSignature(MyMessage &msg) { - memset(temp_message, 0, 32); - memcpy(temp_message, (uint8_t*)&msg.data[1-HEADER_SIZE], MAX_MESSAGE_LENGTH-1-(MAX_PAYLOAD-mGetLength(msg))); +// Helper to calculate signature of msg (returned in _singning_rx_buffer[SHA204_BUFFER_POS_DATA]) +void signerCalculateSignature(MyMessage &msg) { + memset(_signing_temp_message, 0, 32); + memcpy(_signing_temp_message, (uint8_t*)&msg.data[1-HEADER_SIZE], MAX_MESSAGE_LENGTH-1-(MAX_PAYLOAD-mGetLength(msg))); // Program the data to sign into the ATSHA204 DEBUG_SIGNING_PRINTBUF(F("MSG:"), (uint8_t*)&msg.data[1-HEADER_SIZE], MAX_MESSAGE_LENGTH-1-(MAX_PAYLOAD-mGetLength(msg))); // MSG = Message to sign - DEBUG_SIGNING_PRINTBUF(F("CNC:"), current_nonce, 32); // CNC = Current nonce - (void)atsha204.sha204m_execute(SHA204_WRITE, SHA204_ZONE_DATA | SHA204_ZONE_COUNT_FLAG, 8 << 3, 32, temp_message, - WRITE_COUNT_LONG, tx_buffer, WRITE_RSP_SIZE, rx_buffer); + DEBUG_SIGNING_PRINTBUF(F("CNC:"), _signing_current_nonce, 32); // CNC = Current nonce + (void)atsha204.sha204m_execute(SHA204_WRITE, SHA204_ZONE_DATA | SHA204_ZONE_COUNT_FLAG, 8 << 3, 32, _signing_temp_message, + WRITE_COUNT_LONG, _singning_tx_buffer, WRITE_RSP_SIZE, _singning_rx_buffer); // Program the nonce to use for the signature (has to be done just before GENDIG due to chip limitations) - (void)atsha204.sha204m_execute(SHA204_NONCE, NONCE_MODE_PASSTHROUGH, 0, NONCE_NUMIN_SIZE_PASSTHROUGH, current_nonce, - NONCE_COUNT_LONG, tx_buffer, NONCE_RSP_SIZE_SHORT, rx_buffer); + (void)atsha204.sha204m_execute(SHA204_NONCE, NONCE_MODE_PASSTHROUGH, 0, NONCE_NUMIN_SIZE_PASSTHROUGH, _signing_current_nonce, + NONCE_COUNT_LONG, _singning_tx_buffer, NONCE_RSP_SIZE_SHORT, _singning_rx_buffer); // Purge nonce when used - memset(current_nonce, 0x00, NONCE_NUMIN_SIZE_PASSTHROUGH); + memset(_signing_current_nonce, 0x00, NONCE_NUMIN_SIZE_PASSTHROUGH); // Generate digest of data and nonce (void)atsha204.sha204m_execute(SHA204_GENDIG, GENDIG_ZONE_DATA, 8, 0, NULL, - GENDIG_COUNT_DATA, tx_buffer, GENDIG_RSP_SIZE, rx_buffer); + GENDIG_COUNT_DATA, _singning_tx_buffer, GENDIG_RSP_SIZE, _singning_rx_buffer); // Calculate HMAC of message+nonce digest and secret key (void)atsha204.sha204m_execute(SHA204_HMAC, HMAC_MODE_SOURCE_FLAG_MATCH, 0, 0, NULL, - HMAC_COUNT, tx_buffer, HMAC_RSP_SIZE, rx_buffer); + HMAC_COUNT, _singning_tx_buffer, HMAC_RSP_SIZE, _singning_rx_buffer); // Put device back to sleep atsha204.sha204c_sleep(); - DEBUG_SIGNING_PRINTBUF(F("HMAC:"), &rx_buffer[SHA204_BUFFER_POS_DATA], 32); + DEBUG_SIGNING_PRINTBUF(F("HMAC:"), &_singning_rx_buffer[SHA204_BUFFER_POS_DATA], 32); } // Helper to calculate a generic SHA256 digest of provided buffer (only supports one block) -// The pointer to the hash is returned, but the hash is also stored in rx_buffer[SHA204_BUFFER_POS_DATA]) -uint8_t* MySigningAtsha204::sha256(const uint8_t* data, size_t sz) { +// The pointer to the hash is returned, but the hash is also stored in _singning_rx_buffer[SHA204_BUFFER_POS_DATA]) +uint8_t* signerSha256(const uint8_t* data, size_t sz) { // Initiate SHA256 calculator (void)atsha204.sha204m_execute(SHA204_SHA, SHA_INIT, 0, 0, NULL, - SHA_COUNT_SHORT, tx_buffer, SHA_RSP_SIZE_SHORT, rx_buffer); + SHA_COUNT_SHORT, _singning_tx_buffer, SHA_RSP_SIZE_SHORT, _singning_rx_buffer); // Calculate a hash - memset(temp_message, 0x00, SHA_MSG_SIZE); - memcpy(temp_message, data, sz); - temp_message[sz] = 0x80; + memset(_signing_temp_message, 0x00, SHA_MSG_SIZE); + memcpy(_signing_temp_message, data, sz); + _signing_temp_message[sz] = 0x80; // Write length data to the last bytes - temp_message[SHA_MSG_SIZE-2] = (sz >> 5); - temp_message[SHA_MSG_SIZE-1] = (sz << 3); - DEBUG_SIGNING_PRINTBUF(F("DTH:"), temp_message, SHA_MSG_SIZE); // DTH = Data to hash - (void)atsha204.sha204m_execute(SHA204_SHA, SHA_CALC, 0, SHA_MSG_SIZE, temp_message, - SHA_COUNT_LONG, tx_buffer, SHA_RSP_SIZE_LONG, rx_buffer); + _signing_temp_message[SHA_MSG_SIZE-2] = (sz >> 5); + _signing_temp_message[SHA_MSG_SIZE-1] = (sz << 3); + DEBUG_SIGNING_PRINTBUF(F("DTH:"), _signing_temp_message, SHA_MSG_SIZE); // DTH = Data to hash + (void)atsha204.sha204m_execute(SHA204_SHA, SHA_CALC, 0, SHA_MSG_SIZE, _signing_temp_message, + SHA_COUNT_LONG, _singning_tx_buffer, SHA_RSP_SIZE_LONG, _singning_rx_buffer); // Put device back to sleep atsha204.sha204c_sleep(); - DEBUG_SIGNING_PRINTBUF(F("SHA:"), &rx_buffer[SHA204_BUFFER_POS_DATA], 32); - return &rx_buffer[SHA204_BUFFER_POS_DATA]; + DEBUG_SIGNING_PRINTBUF(F("SHA:"), &_singning_rx_buffer[SHA204_BUFFER_POS_DATA], 32); + return &_singning_rx_buffer[SHA204_BUFFER_POS_DATA]; } #endif // #if defined(ARDUINO_ARCH_AVR) diff --git a/libraries/MySensors/MySigningAtsha204Soft.cpp b/libraries/MySensors/core/MySigningAtsha204Soft.cpp similarity index 54% rename from libraries/MySensors/MySigningAtsha204Soft.cpp rename to libraries/MySensors/core/MySigningAtsha204Soft.cpp index 971602c..140124f 100644 --- a/libraries/MySensors/MySigningAtsha204Soft.cpp +++ b/libraries/MySensors/core/MySigningAtsha204Soft.cpp @@ -28,13 +28,27 @@ * */ -#include "MyConfig.h" #include "MySigning.h" -#include "MySigningAtsha204Soft.h" -#include "utility/sha256.h" +#include "drivers/ATSHA204/sha256.h" #define SIGNING_IDENTIFIER (1) + +Sha256Class _signing_sha256; +unsigned long _signing_timestamp; +bool _signing_verification_ongoing = false; +uint8_t _signing_current_nonce[NONCE_NUMIN_SIZE_PASSTHROUGH]; +uint8_t _signing_temp_message[32]; +static uint8_t _signing_hmac_key[32] = { MY_SIGNING_SOFT_HMAC_KEY }; +uint8_t _signing_hmac[32]; + +#ifdef MY_SIGNING_NODE_WHITELISTING + const uint8_t _signing_node_serial_info[SHA204_SERIAL_SZ] = {MY_SIGNING_SOFT_SERIAL}; + const whitelist_entry_t _signing_whitelist[] = MY_SIGNING_NODE_WHITELISTING; +#endif + +void signerCalculateSignature(MyMessage &msg); + // Uncomment this to get some useful serial debug info (Serial.print and Serial.println expected) //#define DEBUG_SIGNING @@ -63,81 +77,60 @@ static void DEBUG_SIGNING_PRINTBUF(const __FlashStringHelper* str, uint8_t* buf, #define DEBUG_SIGNING_PRINTBUF(str, buf, sz) #endif -// Initialize hmacKey from MyConfig.h (codebender didn't like static initialization in constructor) -uint8_t MySigningAtsha204Soft::hmacKey[32] = { MY_HMAC_KEY }; -MySigningAtsha204Soft::MySigningAtsha204Soft(bool requestSignatures, -#ifdef MY_SECURE_NODE_WHITELISTING - uint8_t nof_whitelist_entries, const whitelist_entry_t* the_whitelist, - const uint8_t* the_serial, -#endif - uint8_t randomseedPin) - : - MySigning(requestSignatures), -#ifdef MY_SECURE_NODE_WHITELISTING - whitlist_sz(nof_whitelist_entries), - whitelist(the_whitelist), - node_serial_info(the_serial), -#endif - Sha256(), - verification_ongoing(false), - rndPin(randomseedPin) -{ -} - -bool MySigningAtsha204Soft::getNonce(MyMessage &msg) { +bool signerGetNonce(MyMessage &msg) { // Set randomseed - randomSeed(analogRead(rndPin)); + randomSeed(analogRead(MY_SIGNING_SOFT_RANDOMSEED_PIN)); // We used a basic whitening technique that takes the first byte of a new random value and builds up a 32-byte random value // This 32-byte random value is then hashed (SHA256) to produce the resulting nonce - Sha256.init(); + _signing_sha256.init(); for (int i = 0; i < 32; i++) { - Sha256.write(random(255)); + _signing_sha256.write(random(255)); } - memcpy(current_nonce, Sha256.result(), MAX_PAYLOAD); + memcpy(_signing_current_nonce, _signing_sha256.result(), MAX_PAYLOAD); // We set the part of the 32-byte nonce that does not fit into a message to 0xAA - memset(¤t_nonce[MAX_PAYLOAD], 0xAA, sizeof(current_nonce)-MAX_PAYLOAD); + memset(&_signing_current_nonce[MAX_PAYLOAD], 0xAA, sizeof(_signing_current_nonce)-MAX_PAYLOAD); // Replace the first byte in the nonce with our signing identifier - current_nonce[0] = SIGNING_IDENTIFIER; + _signing_current_nonce[0] = SIGNING_IDENTIFIER; // Transfer the first part of the nonce to the message - msg.set(current_nonce, MAX_PAYLOAD); - verification_ongoing = true; - timestamp = millis(); // Set timestamp to determine when to purge nonce + msg.set(_signing_current_nonce, MAX_PAYLOAD); + _signing_verification_ongoing = true; + _signing_timestamp = millis(); // Set timestamp to determine when to purge nonce // Be a little fancy to handle turnover (prolong the time allowed to timeout after turnover) // Note that if message is "too" quick, and arrives before turnover, it will be rejected // but this is consider such a rare case that it is accepted and rejects are 'safe' - if (timestamp + MY_VERIFICATION_TIMEOUT_MS < millis()) timestamp = 0; + if (_signing_timestamp + MY_VERIFICATION_TIMEOUT_MS < millis()) _signing_timestamp = 0; return true; } -bool MySigningAtsha204Soft::checkTimer() { - if (verification_ongoing) { - if (millis() < timestamp || millis() > timestamp + MY_VERIFICATION_TIMEOUT_MS) { +bool signerCheckTimer() { + if (_signing_verification_ongoing) { + if (millis() < _signing_timestamp || millis() > _signing_timestamp + MY_VERIFICATION_TIMEOUT_MS) { DEBUG_SIGNING_PRINTLN(F("VT")); // VT = Verification timeout // Purge nonce - memset(current_nonce, 0xAA, 32); - verification_ongoing = false; + memset(_signing_current_nonce, 0xAA, 32); + _signing_verification_ongoing = false; return false; } } return true; } -bool MySigningAtsha204Soft::putNonce(MyMessage &msg) { +bool signerPutNonce(MyMessage &msg) { if (((uint8_t*)msg.getCustom())[0] != SIGNING_IDENTIFIER) { DEBUG_SIGNING_PRINTLN(F("ISI")); // ISI = Incorrect signing identifier return false; } - memcpy(current_nonce, (uint8_t*)msg.getCustom(), MAX_PAYLOAD); + memcpy(_signing_current_nonce, (uint8_t*)msg.getCustom(), MAX_PAYLOAD); return true; } -bool MySigningAtsha204Soft::signMsg(MyMessage &msg) { +bool signerSignMsg(MyMessage &msg) { // If we cannot fit any signature in the message, refuse to sign it if (mGetLength(msg) > MAX_PAYLOAD-2) { DEBUG_SIGNING_PRINTLN(F("MTOL")); // Message too large for signature to fit @@ -146,38 +139,38 @@ bool MySigningAtsha204Soft::signMsg(MyMessage &msg) { // Calculate signature of message mSetSigned(msg, 1); // make sure signing flag is set before signature is calculated - calculateSignature(msg); + signerCalculateSignature(msg); -#ifdef MY_SECURE_NODE_WHITELISTING +#ifdef MY_SIGNING_NODE_WHITELISTING // Salt the signature with the senders nodeId and the (hopefully) unique serial The Creator has provided - Sha256.init(); - for (int i=0; i<32; i++) Sha256.write(hmac[i]); - Sha256.write(msg.sender); - for (int i=0; i + * 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 "MyTransport.h" + +bool _autoFindParent; +uint8_t _failedTransmissions; + +#ifdef MY_SIGNING_FEATURE + bool _signingNonceStatus; +#endif + +#ifdef MY_OTA_FIRMWARE_FEATURE + SPIFlash _flash(MY_OTA_FLASH_SS, MY_OTA_FLASH_JDECID); + NodeFirmwareConfig _fc; + bool _fwUpdateOngoing; + unsigned long _fwLastRequestTime; + uint16_t _fwBlock; + uint8_t _fwRetry; +#endif + + +static inline bool isValidDistance( const uint8_t distance ) { + return distance != DISTANCE_INVALID; +} + + +inline void transportProcess() { + uint8_t to = 0; + if (!transportAvailable(&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; + ledBlinkErr(1); + 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; + } + + #ifdef MY_SIGNING_FEATURE + (void)signerCheckTimer(); // Manage signing timeout + #endif + + uint8_t len = transportReceive((uint8_t *)&_msg); + (void)len; //until somebody makes use of 'len' + ledBlinkRx(1); + + 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; + + #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) + #ifdef MY_SIGNING_REQUEST_SIGNATURES + + if ((!MY_IS_GATEWAY || DO_SIGN(sender)) && + destination == nc.nodeId && + mGetLength(_msg) && + !mGetAck(_msg) && + (mGetCommand(_msg) != C_INTERNAL || + (type != I_GET_NONCE_RESPONSE && type != I_GET_NONCE && type != I_REQUEST_SIGNING && + type != I_ID_REQUEST && type != I_ID_RESPONSE && + type != I_FIND_PARENT && type != I_FIND_PARENT_RESPONSE))) { + if (!mGetSigned(_msg)) { + // Got unsigned message that should have been signed + debug(PSTR("no sign\n")); + ledBlinkErr(1); + return; + } + else if (!signerVerifyMsg(_msg)) { + debug(PSTR("verify fail\n")); + ledBlinkErr(1); + return; // This signed message has been tampered with! + } + } + #endif + #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"), + sender, _msg.last, destination, _msg.sensor, mGetCommand(_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")); + ledBlinkErr(1); + return; + } + + if (destination == _nc.nodeId) { + // This message is addressed to this node + + #if defined(MY_REPEATER_FEATURE) + if (_msg.last != _nc.parentNodeId) { + // Message is from one of the child nodes. Add it to routing table. + hwWriteConfig(EEPROM_ROUTES_ADDRESS+sender, _msg.last); + } + #endif + + // 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 = 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 = 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 (signerGetNonce(_msg)) { + _sendRoute(build(_msg, _nc.nodeId, sender, NODE_SENSOR_ID, C_INTERNAL, I_GET_NONCE_RESPONSE, false)); + } + return; // 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(sender); + } else { + // We received an indicator that the sender does not require us to sign all messages we send to it + CLEAR_SIGN(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 defined (MY_GATEWAY_FEATURE) + #ifdef MY_SIGNING_REQUEST_SIGNATURES + if (DO_SIGN(sender)) + _sendRoute(build(_msg, _nc.nodeId, sender, NODE_SENSOR_ID, C_INTERNAL, I_REQUEST_SIGNING, false).set(true)); + else + _sendRoute(build(_msg, _nc.nodeId, sender, NODE_SENSOR_ID, C_INTERNAL, I_REQUEST_SIGNING, false).set(false)); + #else + _sendRoute(build(_msg, _nc.nodeId, sender, NODE_SENSOR_ID, C_INTERNAL, I_REQUEST_SIGNING, false).set(false)); + #endif + #endif + return; // Signing request is an internal MySensor protocol message, no need to inform caller about this + } else if (type == I_GET_NONCE_RESPONSE) { + // Proceed with signing if nonce has been received + if (signerPutNonce(_msg) && signerSignMsg(_msgSign)) { + _signingNonceStatus = SIGN_OK; + } + 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... + } + transportPresentNode(); + // 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 ; + } + 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 (transportIsValidFirmware()) { + 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',':',(uint8_t)(fwsize >> 8),(uint8_t)(fwsize & 0xff),':'}; + _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 + #if defined(MY_GATEWAY_FEATURE) + // Hand over message to controller + gatewayTransportSend(_msg); + #endif + // Call incoming message callback if available + if (receive) { + receive(_msg); + } + return; + } + #if defined(MY_REPEATER_FEATURE) + if (_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) + transportFindParentNode(); + + 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); + transportSendWrite(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 +} + +#ifdef MY_OTA_FIRMWARE_FEATURE +// do a crc16 on the whole received firmware +bool transportIsValidFirmware() { + // 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 + + +boolean transportSendWrite(uint8_t to, MyMessage &message) { + + mSetVersion(message, PROTOCOL_VERSION); + uint8_t length = mGetSigned(message) ? MAX_MESSAGE_LENGTH : mGetLength(message); + message.last = _nc.nodeId; + ledBlinkTx(1); + + bool ok = transportSend(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; +} + + +boolean transportSendRoute(MyMessage &message) { + #if defined(MY_REPEATER_FEATURE) + uint8_t last = message.last; + #endif + bool ok; + + // If we still don't have any parent id, re-request and skip this message. + if (_nc.parentNodeId == AUTO) { + transportFindParentNode(); + ledBlinkErr(1); + return false; + } + + // If we still don't have any node id, re-request and skip this message. + if (_nc.nodeId == AUTO) { + transportRequestNodeId(); + ledBlinkErr(1); + return false; + } + + mSetVersion(message, PROTOCOL_VERSION); + + #ifdef MY_SIGNING_FEATURE + uint8_t type = message.type; + // 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 || + (type != I_GET_NONCE && type != I_GET_NONCE_RESPONSE && type != I_REQUEST_SIGNING && + type != I_ID_REQUEST && type != I_ID_RESPONSE && + type != I_FIND_PARENT && type != I_FIND_PARENT_RESPONSE))) { + // 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; + } + // 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 && _signingNonceStatus==SIGN_WAITING_FOR_NONCE) { + _process(); + } + if (hwMillis() - enter > MY_VERIFICATION_TIMEOUT_MS) { + debug(PSTR("nonce tmo\n")); + ledBlinkErr(1); + return false; + } + if (_signingNonceStatus == SIGN_OK) { + message = _msgSign; // Write the signed message back + } else { + debug(PSTR("sign fail\n")); + ledBlinkErr(1); + 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 !defined(MY_REPEATER_FEATURE) + + // None repeating node... We can only send to our parent + ok = transportSendWrite(_nc.parentNodeId, message); + #else + uint8_t sender = message.sender; + uint8_t dest = message.destination; + if (dest == GATEWAY_ADDRESS) { + // Store this address in routing table (if repeater) + hwWriteConfig(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 = transportSendWrite(_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 transportSendWrite(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 transportSendWrite(BROADCAST_ADDRESS, message); + } + #if defined (MY_GATEWAY_FEATURE) + // Destination isn't in our routing table and isn't a broadcast address + // Nothing to do here + debug(PSTR("Destination %d unknown\n"), dest); + 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 = transportSendWrite(_nc.parentNodeId, message); + + // Add this child to our "routing table" if it not already exist + hwWriteConfig(EEPROM_ROUTES_ADDRESS+sender, last); + + #endif + } + #endif + + if (!ok) { + // Failure when sending to parent node. The parent node might be down and we + // need to find another route to gateway. + ledBlinkErr(1); + _failedTransmissions++; + if (_autoFindParent && _failedTransmissions > SEARCH_FAILURES) { + transportFindParentNode(); + } + } else { + _failedTransmissions = 0; + } + + return ok; +} + + +void transportRequestNodeId() { + debug(PSTR("req id\n")); + transportSetAddress(_nc.nodeId); + build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_ID_REQUEST, false).set(""); + transportSendWrite(_nc.parentNodeId, _msg); + wait(2000); +} + +void transportPresentNode() { + // Open reading pipe for messages directed to this node (set write pipe to same) + transportSetAddress(_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 + #if defined(MY_SIGNING_REQUEST_SIGNATURES) + _sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_REQUEST_SIGNING, false).set(true)); + // If we do require signing, wait for the gateway to tell us how it prefer us to transmit our messages + wait(2000); + #else + _sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_REQUEST_SIGNING, false).set(false)); + #endif + #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 +} + +void transportFindParentNode() { + 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 + transportSendWrite(BROADCAST_ADDRESS, _msg); + + // Wait for ping response. + wait(2000); + findingParentNode = false; +} diff --git a/libraries/MySensors/core/MyTransport.h b/libraries/MySensors/core/MyTransport.h new file mode 100644 index 0000000..18be525 --- /dev/null +++ b/libraries/MySensors/core/MyTransport.h @@ -0,0 +1,109 @@ +/** + * 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 MyTransport_h +#define MyTransport_h + +#include +#include "MySensorCore.h" + +// Size of each firmware block +#define FIRMWARE_BLOCK_SIZE 16 +// Number of times a firmware block should be requested before giving up +#define FIRMWARE_MAX_REQUESTS 5 +// Number of times to request a fw block before giving up +#define MY_OTA_RETRY 5 +// Number of millisecons before re-request a fw block +#define MY_OTA_RETRY_DELAY 500 +// Start offset for firmware in flash (DualOptiboot wants to keeps a signature first) +#define FIRMWARE_START_OFFSET 10 +// Bootloader version +#define MY_OTA_BOOTLOADER_MAJOR_VERSION 3 +#define MY_OTA_BOOTLOADER_MINOR_VERSION 0 +#define MY_OTA_BOOTLOADER_VERSION (MY_OTA_BOOTLOADER_MINOR_VERSION * 256 + MY_OTA_BOOTLOADER_MAJOR_VERSION) + +// Search for a new parent node after this many transmission failures +#define SEARCH_FAILURES 5 + +// Status when waiting for signing nonce in process +enum { SIGN_WAITING_FOR_NONCE = 0, SIGN_OK = 1 }; + + +// FW config structure, stored in eeprom +typedef struct { + uint16_t type; + uint16_t version; + uint16_t blocks; + uint16_t crc; +} __attribute__((packed)) NodeFirmwareConfig; + +typedef struct { + uint16_t type; + uint16_t version; + uint16_t blocks; + uint16_t crc; + uint16_t BLVersion; +} __attribute__((packed)) RequestFirmwareConfig; + +typedef struct { + uint16_t type; + uint16_t version; + uint16_t block; +} __attribute__((packed)) RequestFWBlock; + +typedef struct { + uint16_t type; + uint16_t version; + uint16_t block; + uint8_t data[FIRMWARE_BLOCK_SIZE]; +} __attribute__((packed)) ReplyFWBlock; + + + +#define AUTO 0xFF // 0-254. Id 255 is reserved for auto initialization of nodeId. + +#define BROADCAST_ADDRESS ((uint8_t)0xFF) + +// invalid distance when searching for parent +#define DISTANCE_INVALID (0xFF) + +// Common functions in all radio drivers +#ifdef MY_OTA_FIRMWARE_FEATURE + // do a crc16 on the whole received firmware + bool transportIsValidFirmware(); +#endif + + +void transportProcess(); +void transportRequestNodeId(); +void transportPresentNode(); +void transportFindParentNode(); +boolean transportSendRoute(MyMessage &message); +boolean transportSendWrite(uint8_t to, MyMessage &message); + +// "Interface" functions for radio driver +bool transportInit(); +void transportSetAddress(uint8_t address); +uint8_t transportGetAddress(); +bool transportSend(uint8_t to, const void* data, uint8_t len); +bool transportAvailable(uint8_t *to); +uint8_t transportReceive(void* data); +void transportPowerDown(); + +#endif diff --git a/libraries/MySensors/core/MyTransportNRF24.cpp b/libraries/MySensors/core/MyTransportNRF24.cpp new file mode 100644 index 0000000..a9d0342 --- /dev/null +++ b/libraries/MySensors/core/MyTransportNRF24.cpp @@ -0,0 +1,135 @@ +/** + * 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 "MyConfig.h" +#include "MyTransport.h" +#include +#include "drivers/RF24/RF24.h" +#include "drivers/RF24/RF24_config.h" +#if defined(MY_RF24_ENABLE_ENCRYPTION) +#include "drivers/AES/AES.h" +#endif + +#define TO_ADDR(x) (MY_RF24_BASE_RADIO_ID + x) + +#define WRITE_PIPE ((uint8_t)0) +#define CURRENT_NODE_PIPE ((uint8_t)1) +#define BROADCAST_PIPE ((uint8_t)2) + + +RF24 _rf24(MY_RF24_CE_PIN, MY_RF24_CS_PIN); +uint8_t _address; +uint8_t _paLevel; +#if defined(MY_RF24_ENABLE_ENCRYPTION) + AES _aes; + uint8_t _dataenc[32] = {0}; + uint8_t _psk[] = { MY_RF24_ENCRYPTKEY }; +#endif + +bool transportInit() { + // Start up the radio library + _rf24.begin(); + + if (!_rf24.isPVariant()) { + return false; + } + _rf24.setAutoAck(1); + _rf24.setAutoAck(BROADCAST_PIPE,false); // Turn off auto ack for broadcast + _rf24.enableAckPayload(); + _rf24.setChannel(MY_RF24_CHANNEL); + _rf24.setPALevel(_paLevel); + if (!_rf24.setDataRate(MY_RF24_DATARATE)) { + return false; + } + _rf24.setRetries(5,15); + _rf24.setCRCLength(RF24_CRC_16); + _rf24.enableDynamicPayloads(); + + // All nodes listen to broadcast pipe (for FIND_PARENT_RESPONSE messages) + _rf24.openReadingPipe(BROADCAST_PIPE, TO_ADDR(BROADCAST_ADDRESS)); + + + #if defined(MY_RF24_ENABLE_ENCRYPTION) + _aes.set_key(_psk, 16); //set up AES-key + #endif + + //_rf24.printDetails(); + + return true; +} + +void transportSetAddress(uint8_t address) { + _address = address; + _rf24.openReadingPipe(WRITE_PIPE, TO_ADDR(address)); + _rf24.openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(address)); + _rf24.startListening(); +} + +uint8_t transportGetAddress() { + return _address; +} + +bool transportSend(uint8_t to, const void* data, uint8_t len) { + #if defined(MY_RF24_ENABLE_ENCRYPTION) + memcpy(_dataenc,data,len); // copy input data because it is read-only + + _aes.set_IV(0);//not sure if necessary + len = len > 16 ? 32 : 16; + _aes.cbc_encrypt(_dataenc, _dataenc, len/16); //encrypt + #endif + + // Make sure radio has powered up + + _rf24.powerUp(); + _rf24.stopListening(); + _rf24.openWritingPipe(TO_ADDR(to)); + #if defined(MY_RF24_ENABLE_ENCRYPTION) + bool ok = _rf24.write(_dataenc, len, to == BROADCAST_ADDRESS); + #else + bool ok = _rf24.write(data, len, to == BROADCAST_ADDRESS); + #endif + _rf24.startListening(); + return ok; +} + +bool transportAvailable(uint8_t *to) { + uint8_t pipe = 255; + boolean avail = _rf24.available(&pipe); + + (void)avail; //until somebody makes use of 'avail' + if (pipe == CURRENT_NODE_PIPE) + *to = _address; + else if (pipe == BROADCAST_PIPE) + *to = BROADCAST_ADDRESS; + return (_rf24.available() && pipe < 6); +} + +uint8_t transportReceive(void* data) { + uint8_t len = _rf24.getDynamicPayloadSize(); + _rf24.read(data, len); + #if defined(MY_RF24_ENABLE_ENCRYPTION) + _aes.set_IV(0);//not sure if necessary + _aes.cbc_decrypt((byte*)(data), (byte*)(data), len>16?2:1); // decrypt + #endif + return len; +} + +void transportPowerDown() { + _rf24.powerDown(); +} diff --git a/libraries/MySensors/MyTransportRFM69.cpp b/libraries/MySensors/core/MyTransportRFM69.cpp similarity index 51% rename from libraries/MySensors/MyTransportRFM69.cpp rename to libraries/MySensors/core/MyTransportRFM69.cpp index 757c9a5..b48805d 100644 --- a/libraries/MySensors/MyTransportRFM69.cpp +++ b/libraries/MySensors/core/MyTransportRFM69.cpp @@ -17,57 +17,54 @@ * version 2 as published by the Free Software Foundation. */ +#include "MyConfig.h" #include "MyTransport.h" -#include "MyTransportRFM69.h" +#include +#include "drivers/RFM69/RFM69.h" -MyTransportRFM69::MyTransportRFM69(uint8_t freqBand, uint8_t networkId, uint8_t slaveSelectPin, uint8_t interruptPin, bool isRFM69HW, uint8_t interruptNum) - : - MyTransport(), - radio(slaveSelectPin, interruptPin, isRFM69HW, interruptNum), - _freqBand(freqBand), - _networkId(networkId) -{ -} +RFM69 _radio(MY_RF69_SPI_CS, MY_RF69_IRQ_PIN, MY_IS_RFM69HW, MY_RF69_IRQ_NUM); +uint8_t _address; -bool MyTransportRFM69::init() { + +bool transportInit() { // Start up the radio library (_address will be set later by the MySensors library) - radio.initialize(_freqBand, _address, _networkId); -#ifdef RFM69_ENABLE_ENCRYPTION - radio.encrypt(RFM69_ENCRYPTKEY); + _radio.initialize(MY_RFM69_FREQUENCY, _address, MY_RFM69_NETWORKID); +#ifdef MY_RFM69_ENABLE_ENCRYPTION + _radio.encrypt(RFM69_ENCRYPTKEY); #endif return true; } -void MyTransportRFM69::setAddress(uint8_t address) { +void transportSetAddress(uint8_t address) { _address = address; - radio.setAddress(address); + _radio.setAddress(address); } -uint8_t MyTransportRFM69::getAddress() { +uint8_t transportGetAddress() { return _address; } -bool MyTransportRFM69::send(uint8_t to, const void* data, uint8_t len) { - return radio.sendWithRetry(to,data,len); +bool transportSend(uint8_t to, const void* data, uint8_t len) { + return _radio.sendWithRetry(to,data,len); } -bool MyTransportRFM69::available(uint8_t *to) { - if (radio.TARGETID == BROADCAST_ADDRESS) +bool transportAvailable(uint8_t *to) { + if (_radio.TARGETID == BROADCAST_ADDRESS) *to = BROADCAST_ADDRESS; else *to = _address; - return radio.receiveDone(); + return _radio.receiveDone(); } -uint8_t MyTransportRFM69::receive(void* data) { - memcpy(data,(const void *)radio.DATA, radio.DATALEN); +uint8_t transportReceive(void* data) { + memcpy(data,(const void *)_radio.DATA, _radio.DATALEN); // Send ack back if this message wasn't a broadcast - if (radio.TARGETID != RF69_BROADCAST_ADDR) - radio.ACKRequested(); - radio.sendACK(); - return radio.DATALEN; + if (_radio.TARGETID != RF69_BROADCAST_ADDR) + _radio.ACKRequested(); + _radio.sendACK(); + return _radio.DATALEN; } -void MyTransportRFM69::powerDown() { - radio.sleep(); +void transportPowerDown() { + _radio.sleep(); } diff --git a/libraries/MySensors/Version.h b/libraries/MySensors/core/Version.h similarity index 87% rename from libraries/MySensors/Version.h rename to libraries/MySensors/core/Version.h index c782265..5f64530 100644 --- a/libraries/MySensors/Version.h +++ b/libraries/MySensors/core/Version.h @@ -6,6 +6,6 @@ #ifndef Version_h #define Version_h -#define LIBRARY_VERSION "1.5" +#define LIBRARY_VERSION "1.6" #endif diff --git a/libraries/MySensors/drivers/AES/AES.cpp b/libraries/MySensors/drivers/AES/AES.cpp new file mode 100644 index 0000000..cd5d306 --- /dev/null +++ b/libraries/MySensors/drivers/AES/AES.cpp @@ -0,0 +1,598 @@ +#include "AES.h" + +/* + --------------------------------------------------------------------------- + Copyright (c) 1998-2008, Brian Gladman, Worcester, UK. All rights reserved. + + LICENSE TERMS + + The redistribution and use of this software (with or without changes) + is allowed without the payment of fees or royalties provided that: + + 1. source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + 2. binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation; + + 3. the name of the copyright holder is not used to endorse products + built using this software without specific written permission. + + DISCLAIMER + + This software is provided 'as is' with no explicit or implied warranties + in respect of its properties, including, but not limited to, correctness + and/or fitness for purpose. + --------------------------------------------------------------------------- + Issue 09/09/2006 + + This is an AES implementation that uses only 8-bit byte operations on the + cipher state (there are options to use 32-bit types if available). + + The combination of mix columns and byte substitution used here is based on + that developed by Karl Malbrain. His contribution is acknowledged. + */ + +/* This version derived by Mark Tillotson 2012-01-23, tidied up, slimmed down + and tailored to 8-bit microcontroller abilities and Arduino datatypes. + + The s-box and inverse s-box were retained as tables (0.5kB PROGMEM) but all + the other transformations are coded to save table space. Many efficiency + improvments to the routines mix_sub_columns() and inv_mix_sub_columns() + (mainly common sub-expression elimination). + + Only the routines with precalculated subkey schedule are retained (together + with set_key() - this does however mean each AES object takes 240 bytes of + RAM, alas) + + The CBC routines side-effect the iv argument (so that successive calls work + together correctly). + + All the encryption and decryption routines work with plain == cipher for + in-place encryption, note. + +*/ + + +/* functions for finite field multiplication in the AES Galois field */ + +/* code was modified by george spanos + * 16/12/14 + */ + +// GF(2^8) stuff + +#define WPOLY 0x011B +#define DPOLY 0x008D + +const static byte s_fwd [0x100] PROGMEM = +{ + 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, + 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, + 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, + 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, + 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, + 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, + 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, + 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, + 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, + 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, + 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, + 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, + 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, + 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, + 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, + 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16, +} ; + +const static byte s_inv [0x100] PROGMEM = +{ + 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, + 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, + 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, + 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, + 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, + 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, + 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, + 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, + 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, + 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, + 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, + 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, + 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, + 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, + 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, + 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d, +} ; + +// times 2 in the GF(2^8) +#define f2(x) ((x) & 0x80 ? (x << 1) ^ WPOLY : x << 1) +#define d2(x) (((x) >> 1) ^ ((x) & 1 ? DPOLY : 0)) + +static byte s_box (byte x) +{ + // return fwd_affine (pgm_read_byte (&inv [x])) ; + return pgm_read_byte (& s_fwd [x]) ; +} + +// Inverse Sbox +static byte is_box (byte x) +{ + // return pgm_read_byte (&inv [inv_affine (x)]) ; + return pgm_read_byte (& s_inv [x]) ; +} + + +static void xor_block (byte * d, byte * s) +{ + for (byte i = 0 ; i < N_BLOCK ; i += 4) + { + *d++ ^= *s++ ; // some unrolling + *d++ ^= *s++ ; + *d++ ^= *s++ ; + *d++ ^= *s++ ; + } +} + +static void copy_and_key (byte * d, byte * s, byte * k) +{ + for (byte i = 0 ; i < N_BLOCK ; i += 4) + { + *d++ = *s++ ^ *k++ ; // some unrolling + *d++ = *s++ ^ *k++ ; + *d++ = *s++ ^ *k++ ; + *d++ = *s++ ^ *k++ ; + } +} + +// #define add_round_key(d, k) xor_block (d, k) + +/* SUB ROW PHASE */ + +static void shift_sub_rows (byte st [N_BLOCK]) +{ + st [0] = s_box (st [0]) ; st [4] = s_box (st [4]) ; + st [8] = s_box (st [8]) ; st [12] = s_box (st [12]) ; + + byte tt = st [1] ; + st [1] = s_box (st [5]) ; st [5] = s_box (st [9]) ; + st [9] = s_box (st [13]) ; st [13] = s_box (tt) ; + + tt = st[2] ; st [2] = s_box (st [10]) ; st [10] = s_box (tt) ; + tt = st[6] ; st [6] = s_box (st [14]) ; st [14] = s_box (tt) ; + + tt = st[15] ; + st [15] = s_box (st [11]) ; st [11] = s_box (st [7]) ; + st [7] = s_box (st [3]) ; st [3] = s_box (tt) ; +} + +static void inv_shift_sub_rows (byte st[N_BLOCK]) +{ + st [0] = is_box (st[0]) ; st [4] = is_box (st [4]); + st [8] = is_box (st[8]) ; st [12] = is_box (st [12]); + + byte tt = st[13] ; + st [13] = is_box (st [9]) ; st [9] = is_box (st [5]) ; + st [5] = is_box (st [1]) ; st [1] = is_box (tt) ; + + tt = st [2] ; st [2] = is_box (st [10]) ; st [10] = is_box (tt) ; + tt = st [6] ; st [6] = is_box (st [14]) ; st [14] = is_box (tt) ; + + tt = st [3] ; + st [3] = is_box (st [7]) ; st [7] = is_box (st [11]) ; + st [11] = is_box (st [15]) ; st [15] = is_box (tt) ; +} + +/* SUB COLUMNS PHASE */ + +static void mix_sub_columns (byte dt[N_BLOCK], byte st[N_BLOCK]) +{ + byte j = 5 ; + byte k = 10 ; + byte l = 15 ; + for (byte i = 0 ; i < N_BLOCK ; i += N_COL) + { + byte a = st [i] ; + byte b = st [j] ; j = (j+N_COL) & 15 ; + byte c = st [k] ; k = (k+N_COL) & 15 ; + byte d = st [l] ; l = (l+N_COL) & 15 ; + byte a1 = s_box (a), b1 = s_box (b), c1 = s_box (c), d1 = s_box (d) ; + byte a2 = f2(a1), b2 = f2(b1), c2 = f2(c1), d2 = f2(d1) ; + dt[i] = a2 ^ b2^b1 ^ c1 ^ d1 ; + dt[i+1] = a1 ^ b2 ^ c2^c1 ^ d1 ; + dt[i+2] = a1 ^ b1 ^ c2 ^ d2^d1 ; + dt[i+3] = a2^a1 ^ b1 ^ c1 ^ d2 ; + } +} + +static void inv_mix_sub_columns (byte dt[N_BLOCK], byte st[N_BLOCK]) +{ + for (byte i = 0 ; i < N_BLOCK ; i += N_COL) + { + byte a1 = st [i] ; + byte b1 = st [i+1] ; + byte c1 = st [i+2] ; + byte d1 = st [i+3] ; + byte a2 = f2(a1), b2 = f2(b1), c2 = f2(c1), d2 = f2(d1) ; + byte a4 = f2(a2), b4 = f2(b2), c4 = f2(c2), d4 = f2(d2) ; + byte a8 = f2(a4), b8 = f2(b4), c8 = f2(c4), d8 = f2(d4) ; + byte a9 = a8 ^ a1,b9 = b8 ^ b1,c9 = c8 ^ c1,d9 = d8 ^ d1 ; + byte ac = a8 ^ a4,bc = b8 ^ b4,cc = c8 ^ c4,dc = d8 ^ d4 ; + + dt[i] = is_box (ac^a2 ^ b9^b2 ^ cc^c1 ^ d9) ; + dt[(i+5)&15] = is_box (a9 ^ bc^b2 ^ c9^c2 ^ dc^d1) ; + dt[(i+10)&15] = is_box (ac^a1 ^ b9 ^ cc^c2 ^ d9^d2) ; + dt[(i+15)&15] = is_box (a9^a2 ^ bc^b1 ^ c9 ^ dc^d2) ; + } +} + +/******************************************************************************/ + +AES::AES(){ + byte ar_iv[8] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01 }; + memcpy(iv,ar_iv,8); + memcpy(iv+8,ar_iv,8); + arr_pad[0] = 0x01; + arr_pad[1] = 0x02; + arr_pad[2] = 0x03; + arr_pad[3] = 0x04; + arr_pad[4] = 0x05; + arr_pad[5] = 0x06; + arr_pad[6] = 0x07; + arr_pad[7] = 0x08; + arr_pad[8] = 0x09; + arr_pad[9] = 0x0a; + arr_pad[10] = 0x0b; + arr_pad[11] = 0x0c; + arr_pad[12] = 0x0d; + arr_pad[13] = 0x0e; + arr_pad[14] = 0x0f; +} + +/******************************************************************************/ + +byte AES::set_key (byte key [], int keylen) +{ + byte hi ; + switch (keylen) + { + case 16: + case 128: + keylen = 16; // 10 rounds + round = 10 ; + break; + case 24: + case 192: + keylen = 24; // 12 rounds + round = 12 ; + break; + case 32: + case 256: + keylen = 32; // 14 rounds + round = 14 ; + break; + default: + round = 0; + return AES_FAILURE; + } + hi = (round + 1) << 4 ; + copy_n_bytes (key_sched, key, keylen) ; + byte t[4] ; + byte next = keylen ; + for (byte cc = keylen, rc = 1 ; cc < hi ; cc += N_COL) + { + for (byte i = 0 ; i < N_COL ; i++) + t[i] = key_sched [cc-4+i] ; + if (cc == next) + { + next += keylen ; + byte ttt = t[0] ; + t[0] = s_box (t[1]) ^ rc ; + t[1] = s_box (t[2]) ; + t[2] = s_box (t[3]) ; + t[3] = s_box (ttt) ; + rc = f2 (rc) ; + } + else if (keylen == 32 && (cc & 31) == 16) + { + for (byte i = 0 ; i < 4 ; i++) + t[i] = s_box (t[i]) ; + } + byte tt = cc - keylen ; + for (byte i = 0 ; i < N_COL ; i++) + key_sched [cc + i] = key_sched [tt + i] ^ t[i] ; + } + return AES_SUCCESS ; +} + +/******************************************************************************/ + +void AES::clean () +{ + for (byte i = 0 ; i < KEY_SCHEDULE_BYTES ; i++) + key_sched [i] = 0 ; + round = 0 ; +} + +/******************************************************************************/ + +void AES::copy_n_bytes (byte * d, byte * s, byte nn) +{ + while (nn >= 4) + { + *d++ = *s++ ; // some unrolling + *d++ = *s++ ; + *d++ = *s++ ; + *d++ = *s++ ; + nn -= 4 ; + } + while (nn--) + *d++ = *s++ ; +} + +/******************************************************************************/ + +byte AES::encrypt (byte plain [N_BLOCK], byte cipher [N_BLOCK]) +{ + if (round) + { + byte s1 [N_BLOCK], r ; + copy_and_key (s1, plain, (byte*) (key_sched)) ; + + for (r = 1 ; r < round ; r++) + { + byte s2 [N_BLOCK] ; + mix_sub_columns (s2, s1) ; + copy_and_key (s1, s2, (byte*) (key_sched + r * N_BLOCK)) ; + } + shift_sub_rows (s1) ; + copy_and_key (cipher, s1, (byte*) (key_sched + r * N_BLOCK)) ; + } + else + return AES_FAILURE ; + return AES_SUCCESS ; +} + +/******************************************************************************/ + +byte AES::cbc_encrypt (byte * plain, byte * cipher, int n_block, byte iv [N_BLOCK]) +{ + while (n_block--) + { + xor_block (iv, plain) ; + if (encrypt (iv, iv) != AES_SUCCESS) + return AES_FAILURE ; + copy_n_bytes (cipher, iv, N_BLOCK) ; + plain += N_BLOCK ; + cipher += N_BLOCK ; + } + return AES_SUCCESS ; +} + +/******************************************************************************/ + +byte AES::cbc_encrypt (byte * plain, byte * cipher, int n_block) +{ + while (n_block--) + { + xor_block (iv, plain) ; + if (encrypt (iv, iv) != AES_SUCCESS) + return AES_FAILURE ; + copy_n_bytes (cipher, iv, N_BLOCK) ; + plain += N_BLOCK ; + cipher += N_BLOCK ; + } + return AES_SUCCESS ; +} + +/******************************************************************************/ + +byte AES::decrypt (byte plain [N_BLOCK], byte cipher [N_BLOCK]) +{ + if (round) + { + byte s1 [N_BLOCK] ; + copy_and_key (s1, plain, (byte*) (key_sched + round * N_BLOCK)) ; + inv_shift_sub_rows (s1) ; + + for (byte r = round ; --r ; ) + { + byte s2 [N_BLOCK] ; + copy_and_key (s2, s1, (byte*) (key_sched + r * N_BLOCK)) ; + inv_mix_sub_columns (s1, s2) ; + } + copy_and_key (cipher, s1, (byte*) (key_sched)) ; + } + else + return AES_FAILURE ; + return AES_SUCCESS ; +} + +/******************************************************************************/ + +byte AES::cbc_decrypt (byte * cipher, byte * plain, int n_block, byte iv [N_BLOCK]) +{ + while (n_block--) + { + byte tmp [N_BLOCK] ; + copy_n_bytes (tmp, cipher, N_BLOCK) ; + if (decrypt (cipher, plain) != AES_SUCCESS) + return AES_FAILURE ; + xor_block (plain, iv) ; + copy_n_bytes (iv, tmp, N_BLOCK) ; + plain += N_BLOCK ; + cipher += N_BLOCK; + } + return AES_SUCCESS ; +} + +/******************************************************************************/ + +byte AES::cbc_decrypt (byte * cipher, byte * plain, int n_block) +{ + while (n_block--) + { + byte tmp [N_BLOCK] ; + copy_n_bytes (tmp, cipher, N_BLOCK) ; + if (decrypt (cipher, plain) != AES_SUCCESS) + return AES_FAILURE ; + xor_block (plain, iv) ; + copy_n_bytes (iv, tmp, N_BLOCK) ; + plain += N_BLOCK ; + cipher += N_BLOCK; + } + return AES_SUCCESS ; +} + +/*****************************************************************************/ + +void AES::set_IV(unsigned long long int IVCl){ + memcpy(iv,&IVCl,8); + memcpy(iv+8,&IVCl,8); + IVC = IVCl; +} + +/******************************************************************************/ + +void AES::iv_inc(){ + IVC += 1; + memcpy(iv,&IVC,8); + memcpy(iv+8,&IVC,8); +} + +/******************************************************************************/ + +int AES::get_size(){ + return size; +} + +/******************************************************************************/ + +void AES::set_size(int sizel){ + size = sizel; +} + + +/******************************************************************************/ + +void AES::get_IV(byte *out){ + memcpy(out,&IVC,8); + memcpy(out+8,&IVC,8); +} + +/******************************************************************************/ + +void AES::calc_size_n_pad(int p_size){ + int s_of_p = p_size - 1; + if ( s_of_p % N_BLOCK == 0){ + size = s_of_p; + }else{ + size = s_of_p + (N_BLOCK-(s_of_p % N_BLOCK)); + } + pad = size - s_of_p; +} + +/******************************************************************************/ + +void AES::padPlaintext(void* in,byte* out) +{ + memcpy(out,in,size); + for (int i = size-pad; i < size; i++){; + out[i] = arr_pad[pad - 1]; + } +} + +/******************************************************************************/ + +bool AES::CheckPad(byte* in,int lsize){ + if (in[lsize-1] <= 0x0f){ + int lpad = (int)in[lsize-1]; + for (int i = lsize - 1; i >= lsize-lpad; i--){ + if (arr_pad[lpad - 1] != in[i]){ + return false; + } + } + }else{ + return true; + } +return true; +} + +/******************************************************************************/ + +void AES::printArray(byte output[],bool p_pad) +{ +uint8_t i,j; +uint8_t loops = size/N_BLOCK; +uint8_t outp = N_BLOCK; +for (j = 0; j < loops; j += 1){ + if (p_pad && (j == (loops - 1)) ) { outp = N_BLOCK - pad; } + for (i = 0; i < outp; i++) + { + printf_P(PSTR("%c"),output[j*N_BLOCK + i]); + } +} + printf_P(PSTR("\n")); +} + +/******************************************************************************/ + +void AES::printArray(byte output[],int sizel) +{ + for (int i = 0; i < sizel; i++) + { + printf_P(PSTR("%x"),output[i]); + } + printf_P(PSTR("\n")); +} + + +/******************************************************************************/ + +void AES::do_aes_encrypt(byte *plain,int size_p,byte *cipher,byte *key, int bits, byte ivl [N_BLOCK]){ + calc_size_n_pad(size_p); + byte plain_p[get_size()]; + padPlaintext(plain,plain_p); + int blocks = get_size() / N_BLOCK; + set_key (key, bits) ; + cbc_encrypt (plain_p, cipher, blocks, ivl); +} + +/******************************************************************************/ + +void AES::do_aes_encrypt(byte *plain,int size_p,byte *cipher,byte *key, int bits){ + calc_size_n_pad(size_p); + byte plain_p[get_size()]; + padPlaintext(plain,plain_p); + int blocks = get_size() / N_BLOCK; + set_key (key, bits) ; + cbc_encrypt (plain_p, cipher, blocks); +} + +/******************************************************************************/ + +void AES::do_aes_decrypt(byte *cipher,int size_c,byte *plain,byte *key, int bits, byte ivl [N_BLOCK]){ + set_size(size_c); + int blocks = size_c / N_BLOCK; + set_key (key, bits); + cbc_decrypt (cipher,plain, blocks, ivl); +} + +/******************************************************************************/ + +void AES::do_aes_decrypt(byte *cipher,int size_c,byte *plain,byte *key, int bits){ + set_size(size_c); + int blocks = size_c / N_BLOCK; + set_key (key, bits); + cbc_decrypt (cipher,plain, blocks); +} + + +/******************************************************************************/ + +#if defined(AES_LINUX) +double AES::millis(){ + gettimeofday(&tv, NULL); + return (tv.tv_sec + 0.000001 * tv.tv_usec); +} +#endif diff --git a/libraries/MySensors/drivers/AES/AES.h b/libraries/MySensors/drivers/AES/AES.h new file mode 100644 index 0000000..ec7b8a6 --- /dev/null +++ b/libraries/MySensors/drivers/AES/AES.h @@ -0,0 +1,415 @@ +#ifndef __AES_H__ +#define __AES_H__ + +#include "AES_config.h" +/* + --------------------------------------------------------------------------- + Copyright (c) 1998-2008, Brian Gladman, Worcester, UK. All rights reserved. + + LICENSE TERMS + + The redistribution and use of this software (with or without changes) + is allowed without the payment of fees or royalties provided that: + + 1. source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + 2. binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation; + + 3. the name of the copyright holder is not used to endorse products + built using this software without specific written permission. + + DISCLAIMER + + This software is provided 'as is' with no explicit or implied warranties + in respect of its properties, including, but not limited to, correctness + and/or fitness for purpose. + --------------------------------------------------------------------------- + Issue 09/09/2006 + + This is an AES implementation that uses only 8-bit byte operations on the + cipher state. + */ + + /* code was modified by george spanos + * 16/12/14 + */ + +class AES +{ + public: + +/* The following calls are for a precomputed key schedule + + NOTE: If the length_type used for the key length is an + unsigned 8-bit character, a key length of 256 bits must + be entered as a length in bytes (valid inputs are hence + 128, 192, 16, 24 and 32). +*/ + /** \fn AES() + * \brief AES constructor + * + * This function initialized an instance of AES. + */ + AES(); + + /** Set the cipher key for the pre-keyed version. + * @param key[] pointer to the key string. + * @param keylen Integer that indicates the length of the key. + * @note NOTE: If the length_type used for the key length is an unsigned 8-bit character, + * a key length of 256 bits must be entered as a length in bytes + * (valid inputs are hence 128, 192, 16, 24 and 32). + * + */ + byte set_key (byte key[], int keylen) ; + + /** clean up subkeys after use. + * + */ + void clean () ; // delete key schedule after use + + /** copying and xoring utilities. + * @param *AESt byte pointer of the AEStination array. + * @param *src byte pointer of the source array. + * @param n byte, indicating the sizeof the bytes to be copied. + * @note this is an alternative for memcpy(void *s1,const void *s2, site_t n), + * i have not updated the function in the implementation yet, but it is considered a future plan. + * + */ + void copy_n_bytes (byte * AESt, byte * src, byte n) ; + + /** Encrypt a single block of 16 bytes . + * @param plain[N_BLOCK] Array of the plaintext. + * @param cipher[N_BLOCK] Array of the ciphertext. + * @note The N_BLOCK is defined in AES_config.h as, + * @code #define N_ROW 4 + * #define N_COL 4 + * #define N_BLOCK (N_ROW * N_COL) + * @endcode + * Changed to that will change the Block_size. + * @Return 0 if SUCCESS or -1 if FAILURE + * + */ + byte encrypt (byte plain [N_BLOCK], byte cipher [N_BLOCK]) ; + + /** CBC encrypt a number of blocks (input and return an IV). + * + * @param *plain Pointer, points to the plaintex. + * @param *cipher Pointer, points to the ciphertext that will be created. + * @param n_block integer, indicated the number of blocks to be ciphered. + * @param iv[N_BLOCK] byte Array that holds the IV (initialization vector). + * @Return 0 if SUCCESS or -1 if FAILURE + * + */ + byte cbc_encrypt (byte * plain, byte * cipher, int n_block, byte iv [N_BLOCK]) ; + + /** CBC encrypt a number of blocks (input and return an IV). + * + * @param *plain Pointer, points to the plaintex. + * @param *cipher Pointer, points to the ciphertext that will be created. + * @param n_block integer, indicated the number of blocks to be ciphered. + * @Return 0 if SUCCESS or -1 if FAILURE + * + */ + byte cbc_encrypt (byte * plain, byte * cipher, int n_block) ; + + + /** Decrypt a single block of 16 bytes + * @param cipher[N_BLOCK] Array of the ciphertext. + * @param plain[N_BLOCK] Array of the plaintext. + * @note The N_BLOCK is defined in AES_config.h as, + * @code #define N_ROW 4 + * #define N_COL 4 + * #define N_BLOCK (N_ROW * N_COL) + * @endcode + * Changed to that will change the Block_size. + * @Return 0 if SUCCESS or -1 if FAILURE + * + */ + byte decrypt (byte cipher [N_BLOCK], byte plain [N_BLOCK]) ; + + /** CBC decrypt a number of blocks (input and return an IV) + * + * @param *cipher Pointer, points to the ciphertext that will be created. + * @param *plain Pointer, points to the plaintex. + * @param n_block integer, indicated the number of blocks to be ciphered. + * @param iv[N_BLOCK] byte Array that holds the IV (initialization vector). + * @Return 0 if SUCCESS or -1 if FAILURE + * + */ + byte cbc_decrypt (byte * cipher, byte * plain, int n_block, byte iv [N_BLOCK]) ; + + /** CBC decrypt a number of blocks (input and return an IV) + * + * @param *cipher Pointer, points to the ciphertext that will be created. + * @param *plain Pointer, points to the plaintex. + * @param n_block integer, indicated the number of blocks to be ciphered. + * @Return 0 if SUCCESS or -1 if FAILURE + * + */ + byte cbc_decrypt (byte * cipher, byte * plain, int n_block) ; + + /** Sets IV (initialization vector) and IVC (IV counter). + * This function changes the ivc and iv variables needed for AES. + * + * @param IVCl int or hex value of iv , ex. 0x0000000000000001 + * @note example: + * @code unsigned long long int my_iv = 01234567; @endcode + */ + void set_IV(unsigned long long int IVCl); + + /** increase the iv (initialization vector) and IVC (IV counter) by 1 + * + * This function increased the VI by one step in order to have a different IV each time + * + */ + void iv_inc(); + + /** Getter method for size + * + * This function return the size + * @return an integer, that is the size of the of the padded plaintext, + * thus, the size of the ciphertext. + */ + int get_size(); + + /** Setter method for size + * + * This function sets the size of the plaintext+pad + * + */ + void set_size(int sizel); + + /** Getter method for IV + * + * This function return the IV + * @param out byte pointer that gets the IV. + * @return none, the IV is writed to the out pointer. + */ + void get_IV(byte *out); + + /** Calculates the size of the plaintext and the padding. + * + * Calculates the size of theplaintext with the padding + * and the size of the padding needed. Moreover it stores them in their class variables. + * + * @param p_size the size of the byte array ex sizeof(plaintext) + */ + void calc_size_n_pad(int p_size); + + /** Pads the plaintext + * + * This function pads the plaintext and returns an char array with the + * plaintext and the padding in order for the plaintext to be compatible with + * 16bit size blocks required by AES + * + * @param in the string of the plaintext in a byte array + * @param out The string of the out array. + * @return no return, The padded plaintext is stored in the out pointer. + */ + void padPlaintext(void* in,byte* out); + + /** Check the if the padding is correct. + * + * This functions checks the padding of the plaintext. + * + * @param in the string of the plaintext in a byte array + * @param size the size of the string + * @return true if correct / false if not + */ + bool CheckPad(byte* in,int size); + + /** Prints the array given. + * + * This function prints the given array and pad, + * It is mainlly used for debugging purpuses or to output the string. + * + * @param output[] the string of the text in a byte array + * @param p_pad optional, used to print with out the padding characters + */ + void printArray(byte output[],bool p_pad = true); + + /** Prints the array given. + * + * This function prints the given array in Hexadecimal. + * + * @param output[] the string of the text in a byte array + * @param sizel the size of the array. + */ + void printArray(byte output[],int sizel); + + /** User friendly implementation of AES-CBC encryption. + * + * @param *plain pointer to the plaintext + * @param size_p size of the plaintext + * @param *cipher pointer to the ciphertext + * @param *key pointer to the key that will be used. + * @param bits bits of the encryption/decrpytion + * @param ivl[N_BLOCK] the initialization vector IV that will be used for encryption. + * @note The key will be stored in class variable. + */ + void do_aes_encrypt(byte *plain,int size_p,byte *cipher,byte *key, int bits, byte ivl [N_BLOCK]); + + /** User friendly implementation of AES-CBC encryption. + * + * @param *plain pointer to the plaintext + * @param size_p size of the plaintext + * @param *cipher pointer to the ciphertext + * @param *key pointer to the key that will be used. + * @param bits bits of the encryption/decrpytion + * @note The key will be stored in class variable. + */ + void do_aes_encrypt(byte *plain,int size_p,byte *cipher,byte *key, int bits); + + /** User friendly implementation of AES-CBC decryption. + * + * @param *cipher pointer to the ciphertext + * @param size_c size of the ciphertext + * @param *plain pointer to the plaintext + * @param *key pointer to the key that will be used. + * @param bits bits of the encryption/decrpytion + * @param ivl[N_BLOCK] the initialization vector IV that will be used for decryption. + * @note The key will be stored in class variable. + */ + void do_aes_decrypt(byte *cipher,int size_c,byte *plain,byte *key, int bits, byte ivl [N_BLOCK]); + + /** User friendly implementation of AES-CBC decryption. + * + * @param *cipher pointer to the ciphertext + * @param size_c size of the ciphertext + * @param *plain pointer to the plaintext + * @param *key pointer to the key that will be used. + * @param bits bits of the encryption/decrpytion + * @note The key will be stored in class variable. + */ + void do_aes_decrypt(byte *cipher,int size_c,byte *plain,byte *key, int bits); + + #if defined(AES_LINUX) + /** + * used in linux in order to retrieve the time in milliseconds. + * + * @return returns the milliseconds in a double format. + */ + double millis(); + #endif + private: + int round ;/**< holds the number of rounds to be used. */ + byte key_sched [KEY_SCHEDULE_BYTES] ;/**< holds the pre-computed key for the encryption/decrpytion. */ + unsigned long long int IVC;/**< holds the initialization vector counter in numerical format. */ + byte iv[16];/**< holds the initialization vector that will be used in the cipher. */ + int pad;/**< holds the size of the padding. */ + int size;/**< hold the size of the plaintext to be ciphered */ + #if defined(AES_LINUX) + timeval tv;/**< holds the time value on linux */ + byte arr_pad[15];/**< holds the hexadecimal padding values on linux */ + #else + byte arr_pad[15];// = { 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f };/**< holds the hexadecimal padding values */ + #endif +} ; + + +#endif + +/** + * @example aes.ino + * For Arduino
+ * Updated: spaniakos 2015
+ * + * This is an example of how to use AES in CBC mode easily. + * The text and keys can be either in HEX or String format.
+ */ + + /** + * @example aes.cpp + * For Rasberry pi
+ * Updated: spaniakos 2015
+ * + * This is an example of how to use AES in CBC mode easily. + * The text and keys can be either in HEX or String format.
+ */ + + /** + * @example test_vectors.ino + * For Arduino
+ * Updated: spaniakos 2015
+ * + * This is an example of monte carlo test vectors, in order to justify the effectiveness of the algorithm.
+ * plus is a classical approach to the AES encryption library with out the easy to use add-on modifications. + */ + + /** + * @example test_vectors.cpp + * For Rasberry pi
+ * Updated: spaniakos 2015
+ * + * This is an example of monte carlo test vectors, in order to justify the effectiveness of the algorithm.
+ * plus is a classical approach to the AES encryption library with out the easy to use add-on modifications. + */ + +/** + * @mainpage AES library for Arduino and Raspberry pi. + * + * @section Goals design Goals + * + * This library is AESigned to be... + * @li Fast and efficient. + * @li Able to effectively encrypt and decrypt any size of string. + * @li Able to encrypt and decrypt using AES + * @li Able to encrypt and decrypt using AES-CBC + * @li Easy for the user to use in his programs. + * + * @section Acknowledgements Acknowledgements + * This is an AES library for the Arduino, based on tzikis's AES library, which you can find here:.
+ * Tzikis library was based on scottmac`s library, which you can find here:
+ * + * @section Installation Installation + *

Arduino

+ * Create a folder named _AES_ in the _libraries_ folder inside your Arduino sketch folder. If the + * libraries folder doesn't exist, create it. Then copy everything inside. (re)launch the Arduino IDE.
+ * You're done. Time for a mojito + * + *

Raspberry pi

+ * install

+ * + * sudo make install
+ * cd examples_Rpi
+ * make

+ * + * What to do after changes to the library

+ * sudo make clean
+ * sudo make install
+ * cd examples_Rpi
+ * make clean
+ * make

+ * What to do after changes to a sketch

+ * cd examples_Rpi
+ * make

+ * or
+ * make clean
+ * make


+ * How to start a sketch

+ * cd examples_Rpi
+ * sudo ./

+ * + * @section News News + * + * If issues are discovered with the documentation, please report them here + * @section Useful Useful References + * + * Please refer to: + * + * @li AES Class Documentation + * @li Download + * @li Source Code + * @li All spaniakos Documentation Main Page + * + * @section Board_Support Board Support + * + * Most standard Arduino based boards are supported: + * - Arduino + * - Intel Galileo support + * - Raspberry Pi Support + * + * - The library has not been tested to other boards, but it should suppport ATMega 328 based boards,Mega Boards,Arduino Due,ATTiny board + */ diff --git a/libraries/MySensors/drivers/AES/AES_config.h b/libraries/MySensors/drivers/AES/AES_config.h new file mode 100644 index 0000000..242c841 --- /dev/null +++ b/libraries/MySensors/drivers/AES/AES_config.h @@ -0,0 +1,44 @@ +/* code was modified by george spanos + * 16/12/14 + */ + +#ifndef __AES_CONFIG_H__ +#define __AES_CONFIG_H__ + +#if (defined(__linux) || defined(linux)) && !defined(__ARDUINO_X86__) + + #define AES_LINUX + + #include + #include + #include + #include + #include + #include +#else + #include +#endif + +#include +#include + +#if defined(__ARDUINO_X86__) || (defined (__linux) || defined (linux)) + #undef PROGMEM + #define PROGMEM __attribute__(( section(".progmem.data") )) + #define pgm_read_byte(p) (*(p)) + typedef unsigned char byte; + #define printf_P printf + #define PSTR(x) (x) +#else + #include +#endif + +#define N_ROW 4 +#define N_COL 4 +#define N_BLOCK (N_ROW * N_COL) +#define N_MAX_ROUNDS 14 +#define KEY_SCHEDULE_BYTES ((N_MAX_ROUNDS + 1) * N_BLOCK) +#define AES_SUCCESS (0) +#define AES_FAILURE (-1) + +#endif diff --git a/libraries/MySensors/drivers/AES/Doxyfile b/libraries/MySensors/drivers/AES/Doxyfile new file mode 100644 index 0000000..79cf3b7 --- /dev/null +++ b/libraries/MySensors/drivers/AES/Doxyfile @@ -0,0 +1,2372 @@ +# Doxyfile 1.8.6 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all text +# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv +# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv +# for the list of possible encodings. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = "AES Encryption Library for Arduino and Raspberry Pi" + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = "Spaniakos - AES Encryption Library for Arduino and Raspberry Pi" + +# With the PROJECT_LOGO tag one can specify an logo or icon that is included in +# the documentation. The maximum height of the logo should not exceed 55 pixels +# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo +# to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. + +OUTPUT_DIRECTORY = "../Documentations/AES" + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub- +# directories (in 2 levels) under the output directory of each output format and +# will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. +# The default value is: NO. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, +# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), +# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, +# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, +# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, +# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, +# Ukrainian and Vietnamese. +# The default value is: English. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. +# The default value is: YES. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. + +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# doxygen will generate a detailed section even if there is only a brief +# description. +# The default value is: NO. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. +# The default value is: NO. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. + +FULL_PATH_NAMES = YES + +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. + +JAVADOC_AUTOBRIEF = NO + +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a +# new page for each member. If set to NO, the documentation of a member will be +# part of the file/class/namespace that contains it. +# The default value is: NO. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:\n" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". You can put \n's in the value part of an alias to insert +# newlines. + +ALIASES = + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding "class=itcl::class" +# will allow you to use the command class in the itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, Javascript, +# C#, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL. For instance to make +# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C +# (default is Fortran), use: inc=Fortran f=C. +# +# Note For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. + +EXTENSION_MAPPING = ino=c \ + pde=c + +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See http://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by by putting a % sign in front of the word +# or globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. +# The default value is: NO. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. + +TYPEDEF_HIDES_STRUCT = NO + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will +# be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIVATE = YES + +# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = YES + +# If the EXTRACT_STATIC tag is set to YES all static members of a file will be +# included in the documentation. +# The default value is: NO. + +EXTRACT_STATIC = YES + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO only methods in the interface are +# included. +# The default value is: NO. + +EXTRACT_LOCAL_METHODS = YES + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO these classes will be included in the various overviews. This option has +# no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# (class|struct|union) declarations. If set to NO these declarations will be +# included in the documentation. +# The default value is: NO. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file +# names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. +# The default value is: system dependent. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES the +# scope will be hidden. +# The default value is: NO. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. + +SHOW_INCLUDE_FILES = YES + + +SHOW_GROUPED_MEMB_INC = NO + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO the members will appear in declaration order. +# The default value is: YES. + +SORT_MEMBER_DOCS = NO + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. + +SORT_BY_SCOPE_NAME = YES + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the +# todo list. This list is created by putting \todo commands in the +# documentation. +# The default value is: YES. + +GENERATE_TODOLIST = NO + +# The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the +# test list. This list is created by putting \test commands in the +# documentation. +# The default value is: YES. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if ... \endif and \cond +# ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES the list +# will mention the files that were used to generate the documentation. +# The default value is: YES. + +SHOW_USED_FILES = NO + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. + +SHOW_FILES = NO + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. + +SHOW_NAMESPACES = NO + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. Do not use file names with spaces, bibtex cannot handle them. See +# also \cite for info how to create references. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. + +WARNINGS = YES + +# If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. + +WARN_IF_UNDOCUMENTED = YES + +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some parameters +# in a documented function, or documenting parameters that don't exist or using +# markup commands wrongly. +# The default value is: YES. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO doxygen will only warn about wrong or incomplete parameter +# documentation, but not about the absence of documentation. +# The default value is: NO. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# The default value is: $file:$line: $text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. +# Note: If this tag is empty the current directory is searched. + +INPUT = ./ + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: http://www.gnu.org/software/libiconv) for the list of +# possible encodings. +# The default value is: UTF-8. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank the +# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii, +# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, +# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, +# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, +# *.qsf, *.as and *.js. + +FILE_PATTERNS = *.c \ + *.cc \ + *.cxx \ + *.cpp \ + *.c++ \ + *.java \ + *.ii \ + *.ixx \ + *.ipp \ + *.i++ \ + *.inl \ + *.idl \ + *.ddl \ + *.odl \ + *.h \ + *.hh \ + *.hxx \ + *.hpp \ + *.h++ \ + *.cs \ + *.d \ + *.php \ + *.php4 \ + *.php5 \ + *.phtml \ + *.inc \ + *.m \ + *.markdown \ + *.md \ + *.mm \ + *.dox \ + *.py \ + *.f90 \ + *.f \ + *.for \ + *.tcl \ + *.vhd \ + *.vhdl \ + *.ucf \ + *.qsf \ + *.as \ + *.js + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories use the pattern */test/* + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). + +EXAMPLE_PATH = examples \ + examples_RPi \ + examples_Rpi + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. + +EXAMPLE_RECURSIVE = YES + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# +# +# where is the value of the INPUT_FILTER tag, and is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER ) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = + +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# function all documented functions referencing it will be listed. +# The default value is: NO. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES, then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = YES + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see http://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the config file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. + +VERBATIM_HEADERS = YES + +# If the CLANG_ASSISTED_PARSING tag is set to YES, then doxygen will use the +# clang parser (see: http://clang.llvm.org/) for more acurate parsing at the +# cost of reduced performance. This can be particularly helpful with template +# rich C++ code for which doxygen's built-in parser lacks the necessary type +# information. +# Note: The availability of this option depends on whether or not doxygen was +# compiled with the --with-libclang option. +# The default value is: NO. + +CLANG_ASSISTED_PARSING = NO + +# If clang assisted parsing is enabled you can provide the compiler with command +# line options that you would normally use when invoking the compiler. Note that +# the include paths will already be set by doxygen for the files and directories +# specified with INPUT and INCLUDE_PATH. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_OPTIONS = + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. + +ALPHABETICAL_INDEX = YES + +# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in +# which the alphabetical index list will be split. +# Minimum value: 1, maximum value: 20, default value: 5. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +COLS_IN_ALPHA_INDEX = 10 + +# In case all classes in a project start with a common prefix, all classes will +# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag +# can be used to specify a prefix (or a list of prefixes) that should be ignored +# while generating the index headers. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES doxygen will generate HTML output +# The default value is: YES. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user- +# defined cascading style sheet that is included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefor more robust against future updates. +# Doxygen will copy the style sheet file to the output directory. For an example +# see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = doxygen-custom.css + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the stylesheet and background images according to +# this color. Hue is specified as an angle on a colorwheel, see +# http://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use grayscales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting this +# to NO can help when comparing the output of multiple runs. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_TIMESTAMP = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = YES + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: http://developer.apple.com/tools/xcode/), introduced with +# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a +# Makefile in the HTML output directory. Running make will produce the docset in +# that directory and running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_DOCSET = NO + +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on +# Windows. +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_HTMLHELP = NO + +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be +# written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_FILE = + +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler ( hhc.exe). If non-empty +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +HHC_LOCATION = + +# The GENERATE_CHI flag controls if a separate .chi index file is generated ( +# YES) or that it should be included in the master .chm file ( NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +GENERATE_CHI = NO + +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_INDEX_ENCODING = + +# The BINARY_TOC flag controls whether a binary table of contents is generated ( +# YES) or a normal table of contents ( NO) in the .chm file. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- +# folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_VIRTUAL_FOLDER = doc + +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_SECT_FILTER_ATTRS = + +# The QHG_LOCATION tag can be used to specify the location of Qt's +# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the +# generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can +# further fine-tune the look of the index. As an example, the default style +# sheet generated by doxygen has an example that shows how to put an image at +# the root of the tree instead of the PROJECT_NAME. Since the tree basically has +# the same information as the tab index, you could consider setting +# DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_TREEVIEW = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. + +TREEVIEW_WIDTH = 250 + +# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are not +# supported properly for IE 6.0, but are supported on all modern browsers. +# +# Note that when changing this option you need to delete any form_*.png files in +# the HTML output directory before the changes have effect. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# http://www.mathjax.org) which uses client side Javascript for the rendering +# instead of using prerendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. See the MathJax site (see: +# http://docs.mathjax.org/en/latest/output.html) for more details. +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility), NativeMML (i.e. MathML) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from http://www.mathjax.org before deployment. +# The default value is: http://cdn.mathjax.org/mathjax/latest. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use + S +# (what the is depends on the OS and browser, but it is typically +# , /