Merge branch 'development' into dev-raspberrypi
This commit is contained in:
+1
-1
@@ -1 +1 @@
|
||||
To get started, <a href="http://www.clahub.com/agreements/mysensors/Arduino">sign the Contributor License Agreement</a>.
|
||||
To get started, <a href="http://www.clahub.com/agreements/mysensors/MySensors">sign the Contributor License Agreement</a>.
|
||||
|
||||
@@ -743,7 +743,7 @@ WARN_LOGFILE =
|
||||
# spaces.
|
||||
# Note: If this tag is empty the current directory is searched.
|
||||
|
||||
INPUT = libraries/MySensors Documentation/mainpage.dox
|
||||
INPUT = . Documentation/mainpage.dox
|
||||
|
||||
# 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
|
||||
@@ -811,7 +811,7 @@ EXCLUDE_SYMBOLS =
|
||||
# that contain example code fragments that are included (see the \include
|
||||
# command).
|
||||
|
||||
EXAMPLE_PATH = libraries/MySensors/examples libraries/MySensors/drivers/AES
|
||||
EXAMPLE_PATH = examples drivers/AES
|
||||
|
||||
# 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
|
||||
|
||||
@@ -31,14 +31,18 @@
|
||||
* Serial and debug options
|
||||
***********************************/
|
||||
|
||||
// Enables this in sketch to show debug prints. This option will add a lot to the size of the
|
||||
// Enable MY_DEBUG 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 related to RF24 radio.
|
||||
// Requires DEBUG to be enabled.
|
||||
// This will add even more to the size of the final sketch!
|
||||
//#define MY_DEBUG_VERBOSE
|
||||
// Enable MY_SPECIAL_DEBUG in sketch to activate I_DEBUG messages if MY_DEBUG is disabled.
|
||||
// I_DEBUG requests are:
|
||||
// R: routing info (only repeaters): received msg XXYY (as stream), where XX is the node and YY the routing node
|
||||
// V: CPU voltage
|
||||
// F: CPU frequency
|
||||
// M: free memory
|
||||
// E: clear MySensors EEPROM area and reboot (i.e. "factory" reset)
|
||||
//#define MY_SPECIAL_DEBUG
|
||||
|
||||
// Enable MY_DEBUG_VERBOSE_SIGNING flag for verbose debug prints related to signing.
|
||||
// Requires DEBUG to be enabled.
|
||||
@@ -77,6 +81,52 @@
|
||||
//#define MY_RADIO_RFM69
|
||||
//#define MY_RS485
|
||||
|
||||
/**
|
||||
* @def MY_TRANSPORT_SANITY_CHECK
|
||||
* @brief If enabled, node will check transport in regular intervals to detect HW issues and re-initialize in case of failure. This feature is enabled for all repeater nodes (incl. GW)
|
||||
*/
|
||||
//#define MY_TRANSPORT_SANITY_CHECK
|
||||
/**
|
||||
* @def MY_TRANSPORT_SANITY_CHECK_INTERVAL
|
||||
* @brief Interval (in ms) of transport sanity checks
|
||||
*/
|
||||
#ifndef MY_TRANSPORT_SANITY_CHECK_INTERVAL
|
||||
#define MY_TRANSPORT_SANITY_CHECK_INTERVAL ((uint32_t)60000)
|
||||
#endif
|
||||
/**
|
||||
* @def MY_REGISTRATION_FEATURE
|
||||
* @brief If enabled, node has to register to gateway/controller before allowed to send sensor data.
|
||||
*/
|
||||
#define MY_REGISTRATION_FEATURE
|
||||
|
||||
/**
|
||||
* @def MY_REGISTRATION_RETRIES
|
||||
* @brief Number of registration retries if no reply received from GW/controller
|
||||
*/
|
||||
|
||||
#ifndef MY_REGISTRATION_RETRIES
|
||||
#define MY_REGISTRATION_RETRIES 3
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @def MY_REGISTRATION_DEFAULT
|
||||
* @brief Node registration default - this applies if no registration response is recieved from controller
|
||||
*/
|
||||
|
||||
#define MY_REGISTRATION_DEFAULT true
|
||||
|
||||
/**
|
||||
* @def MY_REGISTRATION_CONTROLLER
|
||||
* @brief If enabled, node registration request has to be handled by controller
|
||||
*/
|
||||
// #define MY_REGISTRATION_CONTROLLER
|
||||
|
||||
/**
|
||||
* @def MY_CORE_COMPATIBILITY_CHECK
|
||||
* @brief If enabled, library compatibility is checked during node registration. Incompatible libraries are unable to send sensor data.
|
||||
*/
|
||||
#define MY_CORE_COMPATIBILITY_CHECK
|
||||
|
||||
/**
|
||||
* @def MY_NODE_ID
|
||||
* @brief Node id defaults to AUTO (tries to fetch id from controller).
|
||||
@@ -93,6 +143,12 @@
|
||||
#define MY_PARENT_NODE_ID AUTO
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @def MY_PARENT_NODE_IS_STATIC
|
||||
* @brief Enable MY_PARENT_NODE_IS_STATIC to disable fall back if parent node fails
|
||||
*/
|
||||
//#define MY_PARENT_NODE_IS_STATIC
|
||||
|
||||
// Enables repeater functionality (relays messages from other nodes)
|
||||
// #define MY_REPEATER_FEATURE
|
||||
|
||||
@@ -183,9 +239,9 @@
|
||||
// The following defines can be used to set the port pin, that the LED is connected to
|
||||
// If one of the following is defined here, or in the sketch, MY_LEDS_BLINKING_FEATURE will be
|
||||
// enabled by default. (Replace x with the pin number you have the LED on)
|
||||
//#define MY_DEFAULT_ERR_LED x
|
||||
//#define MY_DEFAULT_TX_LED x
|
||||
//#define MY_DEFAULT_RX_LED x
|
||||
//#define MY_DEFAULT_ERR_LED_PIN x
|
||||
//#define MY_DEFAULT_TX_LED_PIN x
|
||||
//#define MY_DEFAULT_RX_LED_PIN x
|
||||
|
||||
/**********************************************
|
||||
* Gateway inclusion button/mode configuration
|
||||
@@ -327,12 +383,24 @@
|
||||
#endif
|
||||
|
||||
/**********************************
|
||||
* NRF24L01 Driver Defaults
|
||||
* NRF24L01P Driver Defaults
|
||||
***********************************/
|
||||
|
||||
// Enables RF24 encryption (all nodes and gateway must have this enabled, and all must be personalized with the same AES key)
|
||||
//#define MY_RF24_ENABLE_ENCRYPTION
|
||||
|
||||
/**
|
||||
* @def MY_DEBUG_VERBOSE_RF24
|
||||
* @brief Enable MY_DEBUG_VERBOSE_RF24 flag for verbose debug prints related to the RF24 driver. Requires DEBUG to be enabled.
|
||||
*/
|
||||
//#define MY_DEBUG_VERBOSE_RF24
|
||||
|
||||
/**
|
||||
* @def MY_RF24_SPI_MAX_SPEED
|
||||
* @brief MY_RF24_SPI_MAX_SPEED to overrule default nRF24L01+ SPI speed.
|
||||
*/
|
||||
//#define MY_RF24_SPI_MAX_SPEED 4000000
|
||||
|
||||
/**
|
||||
* @def MY_RF24_CE_PIN
|
||||
* @brief Default RF24 chip enable pin setting. Override in sketch if needed.
|
||||
@@ -372,6 +440,22 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @def MY_RF24_IRQ_PIN
|
||||
* @brief Disable RF24 interrupt pin usage by default. Override in sketch if needed.
|
||||
*/
|
||||
//#define MY_RF24_IRQ_PIN
|
||||
|
||||
/**
|
||||
* @def MY_RX_MESSAGE_BUFFER_SIZE
|
||||
* @brief Declare the amount of incoming messages that can be buffered. Requires MY_RADIO_NRF24 and MY_RF24_IRQ_PIN. Override in sketch if needed.
|
||||
*/
|
||||
#ifdef MY_RF24_IRQ_PIN
|
||||
#ifndef MY_RX_MESSAGE_BUFFER_SIZE
|
||||
#define MY_RX_MESSAGE_BUFFER_SIZE (20)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @def MY_RF24_PA_LEVEL
|
||||
* @brief Default RF24 PA level. Override in sketch if needed.
|
||||
@@ -410,10 +494,20 @@
|
||||
* @def MY_RF24_BASE_RADIO_ID
|
||||
* @brief RF24 radio network identifier.
|
||||
*
|
||||
* This is also act as base value for sensor nodeId addresses. Change this (or channel) if you have more than one sensor network.
|
||||
* This acts 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)
|
||||
#define MY_RF24_BASE_RADIO_ID 0x00,0xFC,0xE1,0xA8,0xA8
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @def MY_RF24_ADDR_WIDTH
|
||||
* @brief RF24 address width.
|
||||
*
|
||||
* This defines the width of the base address.
|
||||
*/
|
||||
#ifndef MY_RF24_ADDR_WIDTH
|
||||
#define MY_RF24_ADDR_WIDTH 5
|
||||
#endif
|
||||
|
||||
// Enable SOFTSPI for NRF24L01, useful for the W5100 Ethernet module
|
||||
@@ -602,7 +696,7 @@
|
||||
*
|
||||
* If using a secure bootloader, grounding the pin is the only option to reactivate the node.
|
||||
* If using stock Android bootloader or a DualOptiBoot it is also possible to download a sketch
|
||||
* using serial protocol to erase EEPROM to unlock the node.
|
||||
* using serial protocol to erase EEPROM to unlock the node.
|
||||
*/
|
||||
#ifndef MY_NODE_UNLOCK_PIN
|
||||
#define MY_NODE_UNLOCK_PIN 14
|
||||
@@ -650,4 +744,11 @@
|
||||
#define MY_SIGNING_REQUEST_SIGNATURES
|
||||
#define MY_SIGNING_NODE_WHITELISTING {{.nodeId = GATEWAY_ADDRESS,.serial = {0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01}}}
|
||||
#define MY_IS_RFM69HW
|
||||
#define MY_PARENT_NODE_IS_STATIC
|
||||
#define MY_REGISTRATION_CONTROLLER
|
||||
#define MY_DEBUG_VERBOSE_RF24
|
||||
#define MY_TRANSPORT_SANITY_CHECK
|
||||
#define MY_RF24_IRQ_PIN
|
||||
#define MY_RX_MESSAGE_BUFFER_SIZE
|
||||
#define MY_NODE_LOCK_FEATURE
|
||||
#endif
|
||||
@@ -18,14 +18,14 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file MySensor.h
|
||||
* @file MySensors.h
|
||||
*
|
||||
* MySensors main interface (includes all necessary code for the library)
|
||||
*/
|
||||
#ifndef MySensor_h
|
||||
#define MySensor_h
|
||||
#ifndef MySensors_h
|
||||
#define MySensors_h
|
||||
|
||||
#include "core/MySensorCore.h"
|
||||
#include "core/MySensorsCore.h"
|
||||
|
||||
// Detect node type
|
||||
/**
|
||||
@@ -43,13 +43,13 @@
|
||||
#if defined(MY_GATEWAY_SERIAL) || defined(MY_GATEWAY_W5100) || defined(MY_GATEWAY_ENC28J60) || defined(MY_GATEWAY_ESP8266) || defined(MY_GATEWAY_RASPBERRYPI) || defined(MY_GATEWAY_LINUX) || defined(MY_GATEWAY_MQTT_CLIENT)
|
||||
#define MY_GATEWAY_FEATURE
|
||||
#define MY_IS_GATEWAY (true)
|
||||
#define MY_NODE_TYPE "gateway"
|
||||
#define MY_NODE_TYPE "GW"
|
||||
#elif defined(MY_REPEATER_FEATURE)
|
||||
#define MY_IS_GATEWAY (false)
|
||||
#define MY_NODE_TYPE "repeater"
|
||||
#define MY_NODE_TYPE "REPEATER"
|
||||
#else
|
||||
#define MY_IS_GATEWAY (false)
|
||||
#define MY_NODE_TYPE "sensor"
|
||||
#define MY_NODE_TYPE "NODE"
|
||||
#endif
|
||||
|
||||
// Enable radio "feature" if one of the radio types was enabled
|
||||
@@ -154,6 +154,8 @@
|
||||
#include "core/MyLeds.h"
|
||||
#endif
|
||||
|
||||
#include "core/MyIndication.cpp"
|
||||
|
||||
|
||||
// INCLUSION MODE
|
||||
#if defined(MY_INCLUSION_MODE_FEATURE)
|
||||
@@ -181,7 +183,39 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(MY_GATEWAY_FEATURE)
|
||||
|
||||
// FLASH
|
||||
#if defined(MY_OTA_FIRMWARE_FEATURE)
|
||||
#include "drivers/SPIFlash/SPIFlash.cpp"
|
||||
#include "core/MyOTAFirmwareUpdate.cpp"
|
||||
#endif
|
||||
|
||||
// GATEWAY - TRANSPORT
|
||||
#if defined(MY_GATEWAY_MQTT_CLIENT)
|
||||
#if defined(MY_RADIO_FEATURE)
|
||||
// We assume that a gateway having a radio also should act as repeater
|
||||
#define MY_REPEATER_FEATURE
|
||||
#endif
|
||||
// GATEWAY - COMMON FUNCTIONS
|
||||
// We only support MQTT Client using W5100 and ESP8266 at the moment
|
||||
#if !(defined(MY_CONTROLLER_URL_ADDRESS) || defined(MY_CONTROLLER_IP_ADDRESS))
|
||||
#error You must specify MY_CONTROLLER_IP_ADDRESS or MY_CONTROLLER_URL_ADDRESS
|
||||
#endif
|
||||
|
||||
#if !defined(MY_MQTT_PUBLISH_TOPIC_PREFIX)
|
||||
#error You must specify a topic publish prefix MY_MQTT_PUBLISH_TOPIC_PREFIX for this MQTT client
|
||||
#endif
|
||||
#if !defined(MY_MQTT_SUBSCRIBE_TOPIC_PREFIX)
|
||||
#error You must specify a topic subscribe prefix MY_MQTT_SUBSCRIBE_TOPIC_PREFIX for this MQTT client
|
||||
#endif
|
||||
#if !defined(MY_MQTT_CLIENT_ID)
|
||||
#error You must define a unique MY_MQTT_CLIENT_ID for this MQTT client
|
||||
#endif
|
||||
|
||||
#include "drivers/PubSubClient/PubSubClient.cpp"
|
||||
#include "core/MyGatewayTransport.cpp"
|
||||
#include "core/MyGatewayTransportMQTTClient.cpp"
|
||||
#elif defined(MY_GATEWAY_FEATURE)
|
||||
// GATEWAY - COMMON FUNCTIONS
|
||||
#include "core/MyGatewayTransport.cpp"
|
||||
|
||||
@@ -264,19 +298,31 @@
|
||||
#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"
|
||||
#include "drivers/AVR/DigitalIO/DigitalIO.h"
|
||||
#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)) || (defined(MY_RADIO_NRF24) && defined(MY_RS485)) || (defined(MY_RADIO_RFM69) && defined(MY_RS485))
|
||||
#error Only one forward link driver can be activated
|
||||
#endif
|
||||
#if defined(MY_RADIO_NRF24)
|
||||
#ifdef MY_RF24_IRQ_PIN
|
||||
// SoftSPI does not support usingInterrupt()
|
||||
#ifdef MY_SOFTSPI
|
||||
#error RF24 IRQ usage cannot be used with Soft SPI
|
||||
#endif
|
||||
// ESP8266 does not support usingInterrupt()
|
||||
#ifdef ESP8266
|
||||
#error RF24 IRQ usage cannot be used with ESP8266
|
||||
#endif
|
||||
#ifndef SPI_HAS_TRANSACTION
|
||||
#error RF24 IRQ usage requires transactional SPI support
|
||||
#endif
|
||||
#else
|
||||
#ifdef MY_RX_MESSAGE_BUFFER_SIZE
|
||||
#error Receive message buffering requires RF24 IRQ usage
|
||||
#endif
|
||||
#endif
|
||||
#if defined(MY_RF24_ENABLE_ENCRYPTION)
|
||||
#include "drivers/AES/AES.cpp"
|
||||
#endif
|
||||
@@ -293,6 +339,10 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(MY_PARENT_NODE_IS_STATIC) && (MY_PARENT_NODE_ID == AUTO)
|
||||
#error Parent is static but no parent ID defined.
|
||||
#endif
|
||||
|
||||
// Make sure to disable child features when parent feature is disabled
|
||||
#if !defined(MY_RADIO_FEATURE)
|
||||
#undef MY_OTA_FIRMWARE_FEATURE
|
||||
@@ -314,7 +364,7 @@
|
||||
|
||||
#include "core/MyCapabilities.h"
|
||||
#include "core/MyMessage.cpp"
|
||||
#include "core/MySensorCore.cpp"
|
||||
#include "core/MySensorsCore.cpp"
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
@@ -5,14 +5,20 @@ MySensors Arduino Library v2.0.0-beta
|
||||
|
||||
Please visit www.mysensors.org for more information
|
||||
|
||||
Current build status of master branch: [](http://ci.mysensors.org/job/MySensorsArduino/branch/master/)
|
||||
Doxygen
|
||||
-------
|
||||
[master](https://ci.mysensors.org/job/Verifiers/job/MySensorsArduino/branch/master/Doxygen_HTML/index.html) [development](https://ci.mysensors.org/job/Verifiers/job/MySensorsArduino/branch/development/Doxygen_HTML/index.html)
|
||||
|
||||
Current build status of development branch: [](http://ci.mysensors.org/job/MySensorsArduino/branch/development/)
|
||||
CI statuses
|
||||
-----------
|
||||
Current build status of master branch: [](https://ci.mysensors.org/job/Verifiers/job/MySensorsArduino/job/master/)
|
||||
|
||||
Current build status of master branch (nightly build): [](http://ci.mysensors.org/job/MySensorsArduinoNightly/branch/master/)
|
||||
Current build status of development branch: [](https://ci.mysensors.org/job/Verifiers/job/MySensorsArduino/job/development/)
|
||||
|
||||
Current build status of development branch (nightly build): [](http://ci.mysensors.org/job/MySensorsArduinoNightly/branch/development/)
|
||||
Current build status of master branch (nightly build): [](https://ci.mysensors.org/job/Nightlies/job/MySensorsArduinoNightly/job/master/)
|
||||
|
||||
Current build status of master branch (nightly build of Arduino IDE): [](http://ci.mysensors.org/job/MySensorsArduinoNightlyIDE/branch/master/)
|
||||
Current build status of development branch (nightly build): [](https://ci.mysensors.org/job/Nightlies/job/MySensorsArduinoNightly/job/development/)
|
||||
|
||||
Current build status of development branch (nightly build of Arduino IDE): [](http://ci.mysensors.org/job/MySensorsArduinoNightlyIDE/branch/development/)
|
||||
Current build status of master branch (nightly build of Arduino IDE): [](https://ci.mysensors.org/job/Nightlies/job/MySensorsArduinoNightlyIDE/job/master/)
|
||||
|
||||
Current build status of development branch (nightly build of Arduino IDE): [](https://ci.mysensors.org/job/Nightlies/job/MySensorsArduinoNightlyIDE/job/development/)
|
||||
|
||||
@@ -70,7 +70,13 @@
|
||||
#define MY_CAP_SIGN "-"
|
||||
#endif
|
||||
|
||||
#if defined(MY_RX_MESSAGE_BUFFER_SIZE)
|
||||
#define MY_CAP_RXBUF "Q"
|
||||
#else
|
||||
#define MY_CAP_RXBUF "-"
|
||||
#endif
|
||||
|
||||
#define MY_CAPABILIIES MY_CAP_RESET MY_CAP_RADIO MY_CAP_OTA_FW MY_CAP_TYPE MY_CAP_ARCH MY_CAP_SIGN
|
||||
|
||||
#define MY_CAPABILITIES MY_CAP_RESET MY_CAP_RADIO MY_CAP_OTA_FW MY_CAP_TYPE MY_CAP_ARCH MY_CAP_SIGN MY_CAP_RXBUF
|
||||
|
||||
#endif /* MyGatewayTransportEthernet_h */
|
||||
@@ -19,9 +19,10 @@
|
||||
|
||||
#include "MyGatewayTransport.h"
|
||||
|
||||
extern boolean transportSendRoute(MyMessage &message);
|
||||
extern bool transportSendRoute(MyMessage &message);
|
||||
extern MyMessage _msg;
|
||||
|
||||
|
||||
inline void gatewayTransportProcess() {
|
||||
if (gatewayTransportAvailable()) {
|
||||
_msg = gatewayTransportReceive();
|
||||
@@ -40,7 +41,7 @@ inline void gatewayTransportProcess() {
|
||||
if (mGetCommand(_msg) == C_INTERNAL) {
|
||||
if (_msg.type == I_VERSION) {
|
||||
// Request for version. Create the response
|
||||
gatewayTransportSend(buildGw(_msg, I_VERSION).set(LIBRARY_VERSION));
|
||||
gatewayTransportSend(buildGw(_msg, I_VERSION).set(MYSENSORS_LIBRARY_VERSION));
|
||||
#ifdef MY_INCLUSION_MODE_FEATURE
|
||||
} else if (_msg.type == I_INCLUSION_MODE) {
|
||||
// Request to change inclusion mode
|
||||
@@ -21,6 +21,7 @@
|
||||
#define MyGatewayTransport_h
|
||||
|
||||
#include "MyProtocol.h"
|
||||
#include "MySensorsCore.h"
|
||||
|
||||
// Common gateway functions
|
||||
|
||||
+52
-43
@@ -1,4 +1,4 @@
|
||||
/**
|
||||
/*
|
||||
* 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
|
||||
@@ -47,8 +47,8 @@ typedef struct
|
||||
#define EthernetUDP WiFiUDP
|
||||
|
||||
#if defined(MY_IP_ADDRESS)
|
||||
IPAddress gateway(MY_IP_GATEWAY_ADDRESS);
|
||||
IPAddress subnet(MY_IP_SUBNET_ADDRESS);
|
||||
IPAddress _gatewayIp(MY_IP_GATEWAY_ADDRESS);
|
||||
IPAddress _subnetIp(MY_IP_SUBNET_ADDRESS);
|
||||
#endif
|
||||
static bool clientsConnected[MY_GATEWAY_MAX_CLIENTS];
|
||||
#endif
|
||||
@@ -76,7 +76,7 @@ typedef struct
|
||||
// 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)
|
||||
void _w5100_spi_en(bool enable)
|
||||
{
|
||||
if (enable)
|
||||
{
|
||||
@@ -100,32 +100,40 @@ typedef struct
|
||||
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);
|
||||
#if defined(MY_ESP8266_SSID)
|
||||
// Turn off access point
|
||||
WiFi.mode (WIFI_STA);
|
||||
#if defined(MY_ESP8266_HOSTNAME)
|
||||
WiFi.hostname(MY_ESP8266_HOSTNAME);
|
||||
#endif
|
||||
#ifdef MY_IP_ADDRESS
|
||||
WiFi.config(_ethernetGatewayIP, _gatewayIp, _subnetIp);
|
||||
#endif
|
||||
(void)WiFi.begin(MY_ESP8266_SSID, MY_ESP8266_PASSWORD);
|
||||
while (WiFi.status() != WL_CONNECTED)
|
||||
{
|
||||
delay(500);
|
||||
MY_SERIALDEVICE.print(".");
|
||||
yield();
|
||||
}
|
||||
MY_SERIALDEVICE.print(F("IP: "));
|
||||
MY_SERIALDEVICE.println(WiFi.localIP());
|
||||
#endif
|
||||
while (WiFi.status() != WL_CONNECTED)
|
||||
{
|
||||
delay(500);
|
||||
MY_SERIALDEVICE.print(".");
|
||||
yield();
|
||||
}
|
||||
MY_SERIALDEVICE.print(F("IP: "));
|
||||
MY_SERIALDEVICE.println(WiFi.localIP());
|
||||
|
||||
#else
|
||||
#ifdef MY_IP_ADDRESS
|
||||
Ethernet.begin(_ethernetGatewayMAC, _ethernetGatewayIP);
|
||||
MY_SERIALDEVICE.print(F("IP: "));
|
||||
MY_SERIALDEVICE.println(Ethernet.localIP());
|
||||
#else
|
||||
// Get IP address from DHCP
|
||||
Ethernet.begin(_ethernetGatewayMAC);
|
||||
MY_SERIALDEVICE.print(F("IP: "));
|
||||
MY_SERIALDEVICE.println(Ethernet.localIP());
|
||||
#endif /* IP_ADDRESS_DHCP */
|
||||
if (!Ethernet.begin(_ethernetGatewayMAC)) {
|
||||
MY_SERIALDEVICE.print("DHCP FAILURE...");
|
||||
_w5100_spi_en(false);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
MY_SERIALDEVICE.print(F("IP: "));
|
||||
MY_SERIALDEVICE.println(Ethernet.localIP());
|
||||
// give the Ethernet interface a second to initialize
|
||||
// TODO: use HW delay
|
||||
delay(1000);
|
||||
#endif
|
||||
|
||||
@@ -134,9 +142,6 @@ bool gatewayTransportInit() {
|
||||
#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;
|
||||
@@ -147,6 +152,8 @@ bool gatewayTransportSend(MyMessage &message)
|
||||
bool ret = true;
|
||||
char *_ethernetMsg = protocolFormat(message);
|
||||
|
||||
setIndication(INDICATION_GW_TX);
|
||||
|
||||
_w5100_spi_en(true);
|
||||
#if defined(MY_CONTROLLER_IP_ADDRESS)
|
||||
#if defined(MY_USE_UDP)
|
||||
@@ -157,17 +164,16 @@ bool gatewayTransportSend(MyMessage &message)
|
||||
#else
|
||||
EthernetClient client;
|
||||
#if defined(MY_CONTROLLER_URL_ADDRESS)
|
||||
if (client.connect(MY_CONTROLLER_URL_ADDRESS, MY_PORT)) {
|
||||
#else
|
||||
if (client.connect(_ethernetControllerIP, MY_PORT)) {
|
||||
#endif
|
||||
client.write(_ethernetMsg, strlen(_ethernetMsg));
|
||||
client.stop();
|
||||
}
|
||||
else {
|
||||
// connecting to the server failed!
|
||||
ret = false;
|
||||
}
|
||||
if (client.connected() || client.connect(MY_CONTROLLER_URL_ADDRESS, MY_PORT)) {
|
||||
#else
|
||||
if (client.connected() || client.connect(_ethernetControllerIP, MY_PORT)) {
|
||||
#endif
|
||||
client.write(_ethernetMsg, strlen(_ethernetMsg));
|
||||
}
|
||||
else {
|
||||
// connecting to the server failed!
|
||||
ret = false;
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
// Send message to connected clients
|
||||
@@ -264,14 +270,17 @@ bool gatewayTransportAvailable()
|
||||
|
||||
if (packet_size) {
|
||||
//debug(PSTR("UDP packet available. Size:%d\n"), packet_size);
|
||||
setIndication(INDICATION_GW_RX);
|
||||
#if defined(MY_GATEWAY_ESP8266)
|
||||
_ethernetServer.read(inputString[0].string, MY_GATEWAY_MAX_RECEIVE_LENGTH);
|
||||
inputString[0].string[packet_size] = 0;
|
||||
debug(PSTR("UDP packet received: %s\n"), inputString[0].string);
|
||||
return protocolParse(_ethernetMsg, inputString[0].string);
|
||||
#else
|
||||
_ethernetServer.read(inputString.string, MY_GATEWAY_MAX_RECEIVE_LENGTH);
|
||||
_w5100_spi_en(false);
|
||||
inputString.string[packet_size] = 0;
|
||||
debug(PSTR("UDP packet received: %s\n"), inputString.string);
|
||||
_w5100_spi_en(false);
|
||||
return protocolParse(_ethernetMsg, inputString.string);
|
||||
#endif
|
||||
}
|
||||
@@ -291,11 +300,9 @@ bool gatewayTransportAvailable()
|
||||
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);
|
||||
if (presentation)
|
||||
presentation();
|
||||
// Send presentation of locally attached sensors (and node if applicable)
|
||||
presentNode();
|
||||
}
|
||||
}
|
||||
bool connected = clients[i].connected();
|
||||
@@ -311,6 +318,7 @@ bool gatewayTransportAvailable()
|
||||
// Loop over clients connect and read available data
|
||||
for (uint8_t i = 0; i < ARRAY_SIZE(clients); i++) {
|
||||
if (_readFromClient(i)) {
|
||||
setIndication(INDICATION_GW_RX);
|
||||
_w5100_spi_en(false);
|
||||
return true;
|
||||
}
|
||||
@@ -326,8 +334,8 @@ bool gatewayTransportAvailable()
|
||||
debug(PSTR("Eth: connect\n"));
|
||||
_w5100_spi_en(false);
|
||||
gatewayTransportSend(buildGw(_msg, I_GATEWAY_READY).set("Gateway startup complete."));
|
||||
if (presentation)
|
||||
presentation();
|
||||
_w5100_spi_en(true);
|
||||
presentNode();
|
||||
}
|
||||
}
|
||||
if (client) {
|
||||
@@ -336,6 +344,7 @@ bool gatewayTransportAvailable()
|
||||
client.stop();
|
||||
} else {
|
||||
if (_readFromClient()) {
|
||||
setIndication(INDICATION_GW_RX);
|
||||
_w5100_spi_en(false);
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,244 @@
|
||||
/*
|
||||
* 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 <henrik.ekblad@mysensors.org>
|
||||
* 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.
|
||||
*/
|
||||
|
||||
|
||||
// Topic structure: MY_MQTT_PUBLISH_TOPIC_PREFIX/NODE-ID/SENSOR-ID/CMD-TYPE/ACK-FLAG/SUB-TYPE
|
||||
|
||||
#include "MyGatewayTransport.h"
|
||||
|
||||
#if defined MY_CONTROLLER_IP_ADDRESS
|
||||
IPAddress _brokerIp(MY_CONTROLLER_IP_ADDRESS);
|
||||
#endif
|
||||
|
||||
#if defined(MY_GATEWAY_ESP8266)
|
||||
#define EthernetClient WiFiClient
|
||||
#if defined(MY_IP_ADDRESS)
|
||||
IPAddress _gatewayIp(MY_IP_GATEWAY_ADDRESS);
|
||||
IPAddress _subnetIp(MY_IP_SUBNET_ADDRESS);
|
||||
#endif
|
||||
#else
|
||||
byte _MQTT_clientMAC[] = { MY_MAC_ADDRESS };
|
||||
#endif
|
||||
|
||||
#if defined(MY_IP_ADDRESS)
|
||||
IPAddress _MQTT_clientIp(MY_IP_ADDRESS);
|
||||
#endif
|
||||
|
||||
static EthernetClient _MQTT_ethClient;
|
||||
static PubSubClient _MQTT_client(_MQTT_ethClient);
|
||||
static bool _MQTT_connecting = true;
|
||||
static bool _MQTT_available = false;
|
||||
static MyMessage _MQTT_msg;
|
||||
|
||||
uint8_t protocolH2i(char c) {
|
||||
uint8_t i = 0;
|
||||
if (c <= '9')
|
||||
i += c - '0';
|
||||
else if (c >= 'a')
|
||||
i += c - 'a' + 10;
|
||||
else
|
||||
i += c - 'A' + 10;
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
bool gatewayTransportSend(MyMessage &message) {
|
||||
if (!_MQTT_client.connected())
|
||||
return false;
|
||||
setIndication(INDICATION_GW_TX);
|
||||
char _fmtBuffer[MY_GATEWAY_MAX_SEND_LENGTH];
|
||||
char _convBuffer[MAX_PAYLOAD * 2 + 1];
|
||||
snprintf_P(_fmtBuffer, MY_GATEWAY_MAX_SEND_LENGTH, PSTR(MY_MQTT_PUBLISH_TOPIC_PREFIX "/%d/%d/%d/%d/%d"), message.sender, message.sensor, mGetCommand(message), mGetAck(message), message.type);
|
||||
debug(PSTR("Sending message on topic: %s\n"), _fmtBuffer);
|
||||
return _MQTT_client.publish(_fmtBuffer, message.getString(_convBuffer));
|
||||
}
|
||||
|
||||
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;
|
||||
uint8_t bvalue[MAX_PAYLOAD];
|
||||
uint8_t blen = 0;
|
||||
uint8_t command = 0;
|
||||
for (str = strtok_r(topic, "/", &p); str && i <= 5;
|
||||
str = strtok_r(NULL, "/", &p)) {
|
||||
switch (i) {
|
||||
case 0: {
|
||||
// Topic prefix
|
||||
if (strcmp(str, MY_MQTT_SUBSCRIBE_TOPIC_PREFIX) != 0) {
|
||||
// Message not for us or malformed!
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
// Node id
|
||||
_MQTT_msg.destination = atoi(str);
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
// Sensor id
|
||||
_MQTT_msg.sensor = atoi(str);
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
// Command type
|
||||
command = atoi(str);
|
||||
mSetCommand(_MQTT_msg, command);
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
// Ack flag
|
||||
mSetRequestAck(_MQTT_msg, atoi(str) ? 1 : 0);
|
||||
break;
|
||||
}
|
||||
case 5: {
|
||||
// Sub type
|
||||
_MQTT_msg.type = atoi(str);
|
||||
// Add payload
|
||||
if (command == C_STREAM) {
|
||||
blen = 0;
|
||||
uint8_t val;
|
||||
while (*payload) {
|
||||
val = protocolH2i(*payload++) << 4;
|
||||
val += protocolH2i(*payload++);
|
||||
bvalue[blen] = val;
|
||||
blen++;
|
||||
}
|
||||
_MQTT_msg.set(bvalue, blen);
|
||||
}
|
||||
else {
|
||||
char* ca;
|
||||
ca = (char *)payload;
|
||||
ca += length;
|
||||
*ca = '\0';
|
||||
_MQTT_msg.set((const char*)payload);
|
||||
}
|
||||
_MQTT_available = true;
|
||||
}
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool reconnectMQTT() {
|
||||
debug(PSTR("Attempting MQTT connection...\n"));
|
||||
// Attempt to connect
|
||||
if (_MQTT_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"));
|
||||
|
||||
// Send presentation of locally attached sensors (and node if applicable)
|
||||
presentNode();
|
||||
|
||||
// Once connected, publish an announcement...
|
||||
//_MQTT_client.publish("outTopic","hello world");
|
||||
// ... and resubscribe
|
||||
_MQTT_client.subscribe(MY_MQTT_SUBSCRIBE_TOPIC_PREFIX "/+/+/+/+/+");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool gatewayTransportConnect() {
|
||||
#if defined(MY_GATEWAY_ESP8266)
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
delay(500); // delay calls yield
|
||||
MY_SERIALDEVICE.print(".");
|
||||
}
|
||||
MY_SERIALDEVICE.print("IP: ");
|
||||
MY_SERIALDEVICE.println(WiFi.localIP());
|
||||
#else
|
||||
#ifdef MY_IP_ADDRESS
|
||||
Ethernet.begin(_MQTT_clientMAC, _MQTT_clientIp);
|
||||
#else
|
||||
// Get IP address from DHCP
|
||||
if (!Ethernet.begin(_MQTT_clientMAC)) {
|
||||
MY_SERIALDEVICE.print("DHCP FAILURE...");
|
||||
_MQTT_connecting = false;
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
MY_SERIALDEVICE.print("IP: ");
|
||||
MY_SERIALDEVICE.println(Ethernet.localIP());
|
||||
// give the Ethernet interface a second to initialize
|
||||
delay(1000);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gatewayTransportInit() {
|
||||
_MQTT_connecting = true;
|
||||
#if defined(MY_CONTROLLER_IP_ADDRESS)
|
||||
_MQTT_client.setServer(_brokerIp, MY_PORT);
|
||||
#else
|
||||
_MQTT_client.setServer(MY_CONTROLLER_URL_ADDRESS, MY_PORT);
|
||||
#endif
|
||||
|
||||
_MQTT_client.setCallback(incomingMQTT);
|
||||
|
||||
#if defined(MY_GATEWAY_ESP8266)
|
||||
// Turn off access point
|
||||
WiFi.mode(WIFI_STA);
|
||||
#if defined(MY_ESP8266_HOSTNAME)
|
||||
WiFi.hostname(MY_ESP8266_HOSTNAME);
|
||||
#endif
|
||||
(void)WiFi.begin(MY_ESP8266_SSID, MY_ESP8266_PASSWORD);
|
||||
#ifdef MY_IP_ADDRESS
|
||||
WiFi.config(_MQTT_clientIp, _gatewayIp, _subnetIp);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
gatewayTransportConnect();
|
||||
|
||||
_MQTT_connecting = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool gatewayTransportAvailable() {
|
||||
|
||||
if (_MQTT_connecting)
|
||||
return false;
|
||||
|
||||
//keep lease on dhcp address
|
||||
//Ethernet.maintain();
|
||||
if (!_MQTT_client.connected()) {
|
||||
//reinitialise client
|
||||
if (gatewayTransportConnect())
|
||||
reconnectMQTT();
|
||||
return false;
|
||||
}
|
||||
_MQTT_client.loop();
|
||||
return _MQTT_available;
|
||||
}
|
||||
|
||||
MyMessage & gatewayTransportReceive() {
|
||||
// Return the last parsed message
|
||||
_MQTT_available = false;
|
||||
return _MQTT_msg;
|
||||
}
|
||||
|
||||
|
||||
|
||||
+5
-2
@@ -31,6 +31,7 @@ MyMessage _serialMsg;
|
||||
|
||||
|
||||
bool gatewayTransportSend(MyMessage &message) {
|
||||
setIndication(INDICATION_GW_TX);
|
||||
MY_SERIALDEVICE.print(protocolFormat(message));
|
||||
// Serial print is always successful
|
||||
return true;
|
||||
@@ -38,6 +39,9 @@ bool gatewayTransportSend(MyMessage &message) {
|
||||
|
||||
bool gatewayTransportInit() {
|
||||
gatewayTransportSend(buildGw(_msg, I_GATEWAY_READY).set("Gateway startup complete."));
|
||||
// Send presentation of locally attached sensors (and node if applicable)
|
||||
presentNode();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -52,8 +56,7 @@ bool gatewayTransportAvailable() {
|
||||
if (inChar == '\n') {
|
||||
_serialInputString[_serialInputPos] = 0;
|
||||
bool ok = protocolParse(_serialMsg, _serialInputString);
|
||||
if (ok)
|
||||
_serialInputPos = 0;
|
||||
_serialInputPos = 0;
|
||||
return ok;
|
||||
} else {
|
||||
// add it to the inputString:
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* 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 <henrik.ekblad@mysensors.org>
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file MyHw.h
|
||||
*
|
||||
* MySensors hardware abstraction layer
|
||||
*/
|
||||
|
||||
#ifndef MyHw_h
|
||||
#define MyHw_h
|
||||
|
||||
#include "MyConfig.h"
|
||||
#include "MyMessage.h"
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
|
||||
// Implement these as functions or macros
|
||||
/*
|
||||
#define hwDigitalWrite(__pin, __value)
|
||||
#define hwInit() MY_SERIALDEVICE.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);
|
||||
*/
|
||||
|
||||
/**
|
||||
* Sleep for a defined time, using minimum power.
|
||||
* @param ms Time to sleep, in [ms].
|
||||
* @return Nonsense, please ignore.
|
||||
*/
|
||||
int8_t hwSleep(unsigned long ms);
|
||||
|
||||
/**
|
||||
* Sleep for a defined time, using minimum power, or until woken by interrupt.
|
||||
* @param interrupt Interrupt number, which can wake the mcu from sleep.
|
||||
* @param mode Interrupt mode, as passed to attachInterrupt.
|
||||
* @param ms Time to sleep, in [ms].
|
||||
* @return -1 when woken by timer, or interrupt number when woken by interrupt.
|
||||
*/
|
||||
int8_t hwSleep(uint8_t interrupt, uint8_t mode, unsigned long ms);
|
||||
|
||||
/**
|
||||
* Sleep for a defined time, using minimum power, or until woken by one of the interrupts.
|
||||
* @param interrupt1 Interrupt1 number, which can wake the mcu from sleep.
|
||||
* @param mode1 Interrupt1 mode, as passed to attachInterrupt.
|
||||
* @param interrupt2 Interrupt2 number, which can wake the mcu from sleep.
|
||||
* @param mode2 Interrupt2 mode, as passed to attachInterrupt.
|
||||
* @param ms Time to sleep, in [ms].
|
||||
* @return -1 when woken by timer, or interrupt number when woken by interrupt.
|
||||
*/
|
||||
int8_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
|
||||
|
||||
/**
|
||||
* @def MY_CRITICAL_SECTION
|
||||
* @brief Creates a block of code that is guaranteed to be executed atomically.
|
||||
* Upon entering the block all interrupts are disabled, and re-enabled upon
|
||||
* exiting the block from any exit path.
|
||||
* A typical example that requires atomic access is a 16 (or more) bit variable
|
||||
* that is shared between the main execution path and an ISR, on an 8-bit
|
||||
* platform (e.g AVR):
|
||||
* @code
|
||||
* volatile uint16_t val = 0;
|
||||
*
|
||||
* void interrupHandler()
|
||||
* {
|
||||
* val = ~val;
|
||||
* }
|
||||
*
|
||||
* void loop()
|
||||
* {
|
||||
* uint16_t copy_val;
|
||||
* MY_CRITICAL_SECTION
|
||||
* {
|
||||
* copy_val = val;
|
||||
* }
|
||||
* }
|
||||
* @endcode
|
||||
* All code within the MY_CRITICAL_SECTION block will be protected from being
|
||||
* interrupted during execution.
|
||||
*/
|
||||
#ifdef DOXYGEN
|
||||
#define MY_CRITICAL_SECTION
|
||||
#endif /* DOXYGEN */
|
||||
|
||||
#endif // #ifdef MyHw_h
|
||||
@@ -21,15 +21,26 @@
|
||||
|
||||
#include "MyHwATMega328.h"
|
||||
|
||||
int8_t pinIntTrigger = 0;
|
||||
#define INVALID_INTERRUPT_NUM (0xFFu)
|
||||
|
||||
void wakeUp() //place to send the interrupts
|
||||
volatile uint8_t _wokeUpByInterrupt = INVALID_INTERRUPT_NUM; // Interrupt number that woke the mcu.
|
||||
volatile uint8_t _wakeUp1Interrupt = INVALID_INTERRUPT_NUM; // Interrupt number for wakeUp1-callback.
|
||||
volatile uint8_t _wakeUp2Interrupt = INVALID_INTERRUPT_NUM; // Interrupt number for wakeUp2-callback.
|
||||
|
||||
void wakeUp1() //place to send the interrupts
|
||||
{
|
||||
pinIntTrigger = 1;
|
||||
detachInterrupt(_wakeUp1Interrupt);
|
||||
_wokeUpByInterrupt = _wakeUp1Interrupt;
|
||||
}
|
||||
void wakeUp2() //place to send the second interrupts
|
||||
{
|
||||
pinIntTrigger = 2;
|
||||
detachInterrupt(_wakeUp2Interrupt);
|
||||
_wokeUpByInterrupt = _wakeUp2Interrupt;
|
||||
}
|
||||
|
||||
bool interruptWakeUp()
|
||||
{
|
||||
return _wokeUpByInterrupt != INVALID_INTERRUPT_NUM;
|
||||
}
|
||||
|
||||
// Watchdog Timer interrupt service routine. This routine is required
|
||||
@@ -39,7 +50,6 @@ ISR (WDT_vect)
|
||||
}
|
||||
|
||||
void hwPowerDown(period_t period) {
|
||||
|
||||
// disable ADC for power saving
|
||||
ADCSRA &= ~(1 << ADEN);
|
||||
// save WDT settings
|
||||
@@ -56,11 +66,12 @@ void hwPowerDown(period_t period) {
|
||||
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
|
||||
cli();
|
||||
sleep_enable();
|
||||
#if defined __AVR_ATmega328P__
|
||||
sleep_bod_disable();
|
||||
#endif
|
||||
#if defined __AVR_ATmega328P__
|
||||
sleep_bod_disable();
|
||||
#endif
|
||||
// Enable interrupts & sleep until WDT or ext. interrupt
|
||||
sei();
|
||||
// sleep until WDT or ext. interrupt
|
||||
// Directly sleep CPU, to prevent race conditions! (see chapter 7.7 of ATMega328P datasheet)
|
||||
sleep_cpu();
|
||||
sleep_disable();
|
||||
// restore previous WDT settings
|
||||
@@ -77,21 +88,19 @@ void hwPowerDown(period_t period) {
|
||||
|
||||
void hwInternalSleep(unsigned long ms) {
|
||||
// Let serial prints finish (debug, log etc)
|
||||
#ifndef MY_DISABLED_SERIAL
|
||||
Serial.flush();
|
||||
#endif
|
||||
// reset interrupt trigger var
|
||||
pinIntTrigger = 0;
|
||||
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; }
|
||||
#ifndef MY_DISABLED_SERIAL
|
||||
MY_SERIALDEVICE.flush();
|
||||
#endif
|
||||
while (!interruptWakeUp() && ms >= 8000) { hwPowerDown(SLEEP_8S); ms -= 8000; }
|
||||
if (!interruptWakeUp() && ms >= 4000) { hwPowerDown(SLEEP_4S); ms -= 4000; }
|
||||
if (!interruptWakeUp() && ms >= 2000) { hwPowerDown(SLEEP_2S); ms -= 2000; }
|
||||
if (!interruptWakeUp() && ms >= 1000) { hwPowerDown(SLEEP_1S); ms -= 1000; }
|
||||
if (!interruptWakeUp() && ms >= 500) { hwPowerDown(SLEEP_500MS); ms -= 500; }
|
||||
if (!interruptWakeUp() && ms >= 250) { hwPowerDown(SLEEP_250MS); ms -= 250; }
|
||||
if (!interruptWakeUp() && ms >= 125) { hwPowerDown(SLEEP_120MS); ms -= 120; }
|
||||
if (!interruptWakeUp() && ms >= 64) { hwPowerDown(SLEEP_60MS); ms -= 60; }
|
||||
if (!interruptWakeUp() && ms >= 32) { hwPowerDown(SLEEP_30MS); ms -= 30; }
|
||||
if (!interruptWakeUp() && ms >= 16) { hwPowerDown(SLEEP_15MS); ms -= 15; }
|
||||
}
|
||||
|
||||
int8_t hwSleep(unsigned long ms) {
|
||||
@@ -100,15 +109,18 @@ int8_t hwSleep(unsigned long ms) {
|
||||
}
|
||||
|
||||
int8_t hwSleep(uint8_t interrupt, uint8_t mode, unsigned long ms) {
|
||||
return hwSleep(interrupt,mode,0xFF,0x00,ms);
|
||||
return hwSleep(interrupt,mode,INVALID_INTERRUPT_NUM,0u,ms);
|
||||
}
|
||||
|
||||
int8_t hwSleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2, unsigned long ms) {
|
||||
// reset interrupt trigger var
|
||||
pinIntTrigger = 0;
|
||||
// Disable interrupts until going to sleep, otherwise interrupts occurring between attachInterrupt()
|
||||
// and sleep might cause the ATMega to not wakeup from sleep as interrupt has already be handled!
|
||||
cli();
|
||||
// attach interrupts
|
||||
attachInterrupt(interrupt1, wakeUp, mode1);
|
||||
if (interrupt2!=0xFF) attachInterrupt(interrupt2, wakeUp2, mode2);
|
||||
_wakeUp1Interrupt = interrupt1;
|
||||
_wakeUp2Interrupt = interrupt2;
|
||||
if (interrupt1 != INVALID_INTERRUPT_NUM) attachInterrupt(interrupt1, wakeUp1, mode1);
|
||||
if (interrupt2 != INVALID_INTERRUPT_NUM) attachInterrupt(interrupt2, wakeUp2, mode2);
|
||||
|
||||
if (ms>0) {
|
||||
// sleep for defined time
|
||||
@@ -118,18 +130,14 @@ int8_t hwSleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mo
|
||||
hwPowerDown(SLEEP_FOREVER);
|
||||
}
|
||||
|
||||
detachInterrupt(interrupt1);
|
||||
if (interrupt2!=0xFF) detachInterrupt(interrupt2);
|
||||
|
||||
// default: no interrupt triggered, timer wake up
|
||||
int8_t retVal = -1;
|
||||
// Return what woke the mcu.
|
||||
int ret = -1; // default: no interrupt triggered, timer wake up
|
||||
if (interruptWakeUp()) ret = static_cast<int8_t>(_wokeUpByInterrupt);
|
||||
|
||||
if (pinIntTrigger == 1) {
|
||||
retVal = (int8_t)interrupt1;
|
||||
} else if (pinIntTrigger == 2) {
|
||||
retVal = (int8_t)interrupt2;
|
||||
}
|
||||
return retVal;
|
||||
// Clear woke-up-by-interrupt flag, so next sleeps won't return immediately.
|
||||
_wokeUpByInterrupt = INVALID_INTERRUPT_NUM;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint16_t hwCPUVoltage() {
|
||||
@@ -152,8 +160,8 @@ uint16_t hwCPUVoltage() {
|
||||
return (1125300UL) / ADC;
|
||||
}
|
||||
|
||||
uint8_t hwCPUFrequency() {
|
||||
noInterrupts();
|
||||
uint16_t hwCPUFrequency() {
|
||||
cli();
|
||||
// setup timer1
|
||||
TIFR1 = 0xFF;
|
||||
TCNT1 = 0;
|
||||
@@ -175,11 +183,17 @@ uint8_t hwCPUFrequency() {
|
||||
wdt_reset();
|
||||
WDTCSR |= (1 << WDCE) | (1 << WDE);
|
||||
WDTCSR = WDTsave;
|
||||
interrupts();
|
||||
sei();
|
||||
// return frequency in 1/10MHz (accuracy +- 10%)
|
||||
return TCNT1 * 2048UL / 100000UL;
|
||||
}
|
||||
|
||||
uint16_t hwFreeMem() {
|
||||
extern int __heap_start, *__brkval;
|
||||
int v;
|
||||
return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef MY_DEBUG
|
||||
@@ -188,7 +202,7 @@ void hwDebugPrint(const char *fmt, ... ) {
|
||||
#ifdef MY_GATEWAY_FEATURE
|
||||
// prepend debug message to be handled correctly by controller (C_INTERNAL, I_LOG_MESSAGE)
|
||||
snprintf_P(fmtBuffer, 299, PSTR("0;255;%d;0;%d;"), C_INTERNAL, I_LOG_MESSAGE);
|
||||
Serial.print(fmtBuffer);
|
||||
MY_SERIALDEVICE.print(fmtBuffer);
|
||||
#endif
|
||||
va_list args;
|
||||
va_start (args, fmt );
|
||||
@@ -202,10 +216,10 @@ void hwDebugPrint(const char *fmt, ... ) {
|
||||
vsnprintf_P(fmtBuffer, 299, fmt, args);
|
||||
#endif
|
||||
va_end (args);
|
||||
Serial.print(fmtBuffer);
|
||||
Serial.flush();
|
||||
MY_SERIALDEVICE.print(fmtBuffer);
|
||||
MY_SERIALDEVICE.flush();
|
||||
|
||||
//Serial.write(freeRam());
|
||||
//MY_SERIALDEVICE.write(freeRam());
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
#include <avr/power.h>
|
||||
#include <avr/interrupt.h>
|
||||
#include <avr/wdt.h>
|
||||
|
||||
#include <util/atomic.h>
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
@@ -57,7 +57,7 @@ do { \
|
||||
// Define these as macros to save valuable space
|
||||
|
||||
#define hwDigitalWrite(__pin, __value) (digitalWrite(__pin, __value))
|
||||
#define hwInit() Serial.begin(MY_BAUD_RATE)
|
||||
#define hwInit() MY_SERIALDEVICE.begin(MY_BAUD_RATE)
|
||||
#define hwWatchdogReset() wdt_reset()
|
||||
#define hwReboot() wdt_enable(WDTO_15MS); while (1)
|
||||
#define hwMillis() millis()
|
||||
@@ -92,4 +92,8 @@ enum period_t
|
||||
|
||||
void hwInternalSleep(unsigned long ms);
|
||||
|
||||
#ifndef DOXYGEN
|
||||
#define MY_CRITICAL_SECTION ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
|
||||
#endif /* DOXYGEN */
|
||||
|
||||
#endif
|
||||
@@ -90,20 +90,27 @@ void hwWriteConfig(int adr, uint8_t value)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
int8_t hwSleep(unsigned long ms) {
|
||||
// TODO: Not supported!
|
||||
(void)ms;
|
||||
return -2;
|
||||
}
|
||||
|
||||
int8_t hwSleep(uint8_t interrupt, uint8_t mode, unsigned long ms) {
|
||||
// TODO: Not supported!
|
||||
(void)interrupt;
|
||||
(void)mode;
|
||||
(void)ms;
|
||||
return -2;
|
||||
}
|
||||
|
||||
int8_t hwSleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2, unsigned long ms) {
|
||||
// TODO: Not supported!
|
||||
// TODO: Not supported!
|
||||
(void)interrupt1;
|
||||
(void)mode1;
|
||||
(void)interrupt2;
|
||||
(void)mode2;
|
||||
(void)ms;
|
||||
return -2;
|
||||
}
|
||||
|
||||
@@ -119,13 +126,17 @@ uint16_t hwCPUFrequency() {
|
||||
return ESP.getCpuFreqMHz()*10;
|
||||
}
|
||||
|
||||
uint16_t hwFreeMem() {
|
||||
return ESP.getFreeHeap();
|
||||
}
|
||||
|
||||
#ifdef MY_DEBUG
|
||||
void hwDebugPrint(const char *fmt, ... ) {
|
||||
char fmtBuffer[300];
|
||||
#ifdef MY_GATEWAY_FEATURE
|
||||
// prepend debug message to be handled correctly by controller (C_INTERNAL, I_LOG_MESSAGE)
|
||||
snprintf_P(fmtBuffer, 299, PSTR("0;255;%d;0;%d;"), C_INTERNAL, I_LOG_MESSAGE);
|
||||
Serial.print(fmtBuffer);
|
||||
MY_SERIALDEVICE.print(fmtBuffer);
|
||||
#endif
|
||||
va_list args;
|
||||
va_start (args, fmt );
|
||||
@@ -139,9 +150,9 @@ void hwDebugPrint(const char *fmt, ... ) {
|
||||
vsnprintf_P(fmtBuffer, 299, fmt, args);
|
||||
#endif
|
||||
va_end (args);
|
||||
Serial.print(fmtBuffer);
|
||||
Serial.flush();
|
||||
MY_SERIALDEVICE.print(fmtBuffer);
|
||||
MY_SERIALDEVICE.flush();
|
||||
|
||||
//Serial.write(freeRam());
|
||||
//MY_SERIALDEVICE.write(freeRam());
|
||||
}
|
||||
#endif
|
||||
@@ -23,7 +23,6 @@
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include <Arduino.h>
|
||||
//#include <SPI.h>
|
||||
#endif
|
||||
|
||||
#define MY_SERIALDEVICE Serial
|
||||
@@ -34,9 +33,9 @@
|
||||
// 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 hwInit() MY_SERIALDEVICE.begin(MY_BAUD_RATE); MY_SERIALDEVICE.setDebugOutput(true)
|
||||
#define hwWatchdogReset() wdt_reset()
|
||||
#define hwReboot() wdt_enable(WDTO_15MS); while (1)
|
||||
#define hwReboot() ESP.restart();
|
||||
#define hwMillis() millis()
|
||||
|
||||
void hwReadConfigBlock(void* buf, void* adr, size_t length);
|
||||
@@ -44,5 +43,17 @@ void hwWriteConfigBlock(void* buf, void* adr, size_t length);
|
||||
void hwWriteConfig(int adr, uint8_t value);
|
||||
uint8_t hwReadConfig(int adr);
|
||||
|
||||
/**
|
||||
* Restore interrupt state.
|
||||
* Helper function for MY_CRITICAL_SECTION.
|
||||
*/
|
||||
static __inline__ void __psRestore(const uint32_t *__s)
|
||||
{
|
||||
xt_wsr_ps( *__s );
|
||||
}
|
||||
|
||||
#ifndef DOXYGEN
|
||||
#define MY_CRITICAL_SECTION for ( uint32_t __psSaved __attribute__((__cleanup__(__psRestore))) = xt_rsil(15), __ToDo = 1; __ToDo ; __ToDo = 0 )
|
||||
#endif /* DOXYGEN */
|
||||
|
||||
#endif // #ifdef ARDUINO_ARCH_ESP8266
|
||||
@@ -68,7 +68,7 @@ void hwReadConfigBlock(void* buf, void* adr, size_t length)
|
||||
int offs = reinterpret_cast<int>(adr);
|
||||
while (length-- > 0)
|
||||
{
|
||||
*dst++ = i2c_eeprom_read_byte(offs++);
|
||||
*dst++ = i2c_eeprom_read_byte(offs++);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ void hwWriteConfig(int adr, uint8_t value)
|
||||
|
||||
void hwInit() {
|
||||
MY_SERIALDEVICE.begin(MY_BAUD_RATE);
|
||||
#ifndef MY_GATEWAY_W5100
|
||||
#if defined(MY_GATEWAY_SERIAL)
|
||||
while (!MY_SERIALDEVICE) {}
|
||||
#endif
|
||||
Wire.begin();
|
||||
@@ -138,6 +138,21 @@ int8_t hwSleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mo
|
||||
return -2;
|
||||
}
|
||||
|
||||
uint16_t hwCPUVoltage() {
|
||||
// TODO: Not supported!
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t hwCPUFrequency() {
|
||||
// TODO: Not supported!
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t hwFreeMem() {
|
||||
// TODO: Not supported!
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef MY_DEBUG
|
||||
void hwDebugPrint(const char *fmt, ... ) {
|
||||
if (MY_SERIALDEVICE) {
|
||||
@@ -162,7 +177,7 @@ void hwDebugPrint(const char *fmt, ... ) {
|
||||
MY_SERIALDEVICE.print(fmtBuffer);
|
||||
// MY_SERIALDEVICE.flush();
|
||||
|
||||
//Serial.write(freeRam());
|
||||
//MY_SERIALDEVICE.write(freeRam());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -58,4 +58,28 @@ uint8_t hwReadConfig(int adr);
|
||||
#define hwWriteConfig(__adr, __value) ( __value = __value)
|
||||
#define hwReadConfig(__adr) (0)
|
||||
*/
|
||||
|
||||
/**
|
||||
* Disable all interrupts.
|
||||
* Helper function for MY_CRITICAL_SECTION.
|
||||
*/
|
||||
static __inline__ uint8_t __disableIntsRetVal(void)
|
||||
{
|
||||
__disable_irq();
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore priority mask register.
|
||||
* Helper function for MY_CRITICAL_SECTION.
|
||||
*/
|
||||
static __inline__ void __priMaskRestore(const uint32_t *priMask)
|
||||
{
|
||||
__set_PRIMASK(*priMask);
|
||||
}
|
||||
|
||||
#ifndef DOXYGEN
|
||||
#define MY_CRITICAL_SECTION for ( uint32_t __savePriMask __attribute__((__cleanup__(__priMaskRestore))) = __get_PRIMASK(), __ToDo = __disableIntsRetVal(); __ToDo ; __ToDo = 0 )
|
||||
#endif /* DOXYGEN */
|
||||
|
||||
#endif // #ifdef ARDUINO_ARCH_SAMD
|
||||
@@ -33,7 +33,7 @@ inline void inclusionInit() {
|
||||
}
|
||||
|
||||
|
||||
void inclusionModeSet(boolean newMode) {
|
||||
void inclusionModeSet(bool newMode) {
|
||||
if (newMode != _inclusionMode) {
|
||||
_inclusionMode = newMode;
|
||||
// Send back mode change to controller
|
||||
@@ -1,4 +1,4 @@
|
||||
/**
|
||||
/*
|
||||
* 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
|
||||
@@ -20,13 +20,13 @@
|
||||
#ifndef MyInclusionMode_h
|
||||
#define MyInclusionMode_h
|
||||
|
||||
#include "MySensorCore.h"
|
||||
#include "MySensorsCore.h"
|
||||
|
||||
|
||||
extern bool gatewayTransportSend(MyMessage &message);
|
||||
|
||||
void inclusionInit();
|
||||
void inclusionModeSet(boolean newMode);
|
||||
void inclusionModeSet(bool newMode);
|
||||
void inclusionProcess();
|
||||
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* The MySensors Arduino library handles the wireless radio link and protocol
|
||||
* between your home built sensors/actuators and HA controller of choice.
|
||||
* The sensors forms a self healing radio network with optional repeaters. Each
|
||||
* repeater and gateway builds a routing tables in EEPROM which keeps track of the
|
||||
* network topology allowing messages to be routed to nodes.
|
||||
*
|
||||
* Created by Henrik Ekblad <henrik.ekblad@mysensors.org>
|
||||
* Copyright (C) 2013-2016 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 "MyIndication.h"
|
||||
#ifdef MY_LEDS_BLINKING_FEATURE
|
||||
#include "MyLeds.h"
|
||||
#endif
|
||||
|
||||
void setIndication( const indication_t ind )
|
||||
{
|
||||
#ifdef MY_LEDS_BLINKING_FEATURE
|
||||
if ((INDICATION_TX == ind) || (INDICATION_GW_TX == ind))
|
||||
{
|
||||
ledsBlinkTx(1);
|
||||
} else if ((INDICATION_RX == ind) || (INDICATION_GW_RX == ind))
|
||||
{
|
||||
ledsBlinkRx(1);
|
||||
} else if (ind > INDICATION_ERR_START)
|
||||
{
|
||||
// Number of blinks indicates which error occurred.
|
||||
ledsBlinkErr(ind-INDICATION_ERR_START);
|
||||
}
|
||||
#endif
|
||||
if (indication)
|
||||
indication(ind);
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* The MySensors Arduino library handles the wireless radio link and protocol
|
||||
* between your home built sensors/actuators and HA controller of choice.
|
||||
* The sensors forms a self healing radio network with optional repeaters. Each
|
||||
* repeater and gateway builds a routing tables in EEPROM which keeps track of the
|
||||
* network topology allowing messages to be routed to nodes.
|
||||
*
|
||||
* Created by Henrik Ekblad <henrik.ekblad@mysensors.org>
|
||||
* Copyright (C) 2013-2016 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 MyIndication_h
|
||||
#define MyIndication_h
|
||||
|
||||
/**
|
||||
* Indication type
|
||||
*/
|
||||
typedef enum {
|
||||
INDICATION_TX = 0, //!< Sent a message.
|
||||
INDICATION_RX, //!< Received a message.
|
||||
|
||||
INDICATION_GW_TX, //!< Gateway transmit message.
|
||||
INDICATION_GW_RX, //!< Gateway receive message.
|
||||
|
||||
INDICATION_FIND_PARENT, //!< Start finding parent node.
|
||||
INDICATION_GOT_PARENT, //!< Found parent node.
|
||||
INDICATION_REQ_NODEID, //!< Request node ID.
|
||||
INDICATION_GOT_NODEID, //!< Got a node ID.
|
||||
INDICATION_REQ_REGISTRATION, //!< Request node registration.
|
||||
INDICATION_GOT_REGISTRATION, //!< Got registration reponse.
|
||||
INDICATION_REBOOT, //!< Rebooting node.
|
||||
INDICATION_PRESENT, //!< Presenting node to gateway.
|
||||
INDICATION_CLEAR_ROUTING, //!< Clear rrouting table requested.
|
||||
INDICATION_SLEEP, //!< Node goes to sleep.
|
||||
INDICATION_WAKEUP, //!< Node just woke from sleep.
|
||||
INDICATION_FW_UPDATE_START, //!< Start of OTA firmware update process.
|
||||
INDICATION_FW_UPDATE_RX, //!< Received a piece of firmware data.
|
||||
|
||||
INDICATION_ERR_START = 100,
|
||||
INDICATION_ERR_TX, //!< Failed to transmit message.
|
||||
INDICATION_ERR_TRANSPORT_FAILURE, //!< Transport failure.
|
||||
INDICATION_ERR_INIT_TRANSPORT, //!< MySensors transport hardware (radio) init failure.
|
||||
INDICATION_ERR_FIND_PARENT, //!< Failed to find parent node.
|
||||
INDICATION_ERR_GET_NODEID, //!< Failed to receive node ID.
|
||||
INDICATION_ERR_SIGN, //!< Error signing.
|
||||
INDICATION_ERR_VERSION, //!< Protocol version mismatch.
|
||||
INDICATION_ERR_NET_FULL, //!< Network full. All node ID's are taken.
|
||||
INDICATION_ERR_INIT_GWTRANSPORT, //!< Gateway transport hardware init failure.
|
||||
INDICATION_ERR_LOCKED, //!< Node is locked.
|
||||
INDICATION_ERR_FW_FLASH_INIT, //!< Firmware update flash initialisation failure.
|
||||
INDICATION_ERR_FW_TIMEOUT, //!< Firmware update timeout.
|
||||
INDICATION_ERR_FW_CHECKSUM, //!< Firmware update checksum mismatch.
|
||||
INDICATION_ERR_END
|
||||
} indication_t;
|
||||
|
||||
/**
|
||||
* Function which is called when something changes about the internal state of MySensors.
|
||||
* @param ind Event indication of what happened.
|
||||
*/
|
||||
void setIndication( const indication_t ind );
|
||||
|
||||
/**
|
||||
* Allow user to define their own indication handler.
|
||||
*/
|
||||
void indication( const indication_t ) __attribute__((weak));
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* 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 <henrik.ekblad@mysensors.org>
|
||||
* 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"
|
||||
|
||||
#define LED_ON_OFF_RATIO (4) // Power of 2 please
|
||||
#define LED_PROCESS_INTERVAL_MS (MY_DEFAULT_LED_BLINK_PERIOD/LED_ON_OFF_RATIO)
|
||||
|
||||
// these variables don't need to be volatile, since we are not using interrupts
|
||||
static uint8_t countRx;
|
||||
static uint8_t countTx;
|
||||
static uint8_t countErr;
|
||||
static unsigned long prevTime = hwMillis() - LED_PROCESS_INTERVAL_MS; // Substract some, to make sure leds gets updated on first run.
|
||||
|
||||
|
||||
inline void ledsInit()
|
||||
{
|
||||
// initialize counters
|
||||
countRx = 0;
|
||||
countTx = 0;
|
||||
countErr = 0;
|
||||
|
||||
// Setup led pins
|
||||
pinMode(MY_DEFAULT_RX_LED_PIN, OUTPUT);
|
||||
pinMode(MY_DEFAULT_TX_LED_PIN, OUTPUT);
|
||||
pinMode(MY_DEFAULT_ERR_LED_PIN, OUTPUT);
|
||||
|
||||
ledsProcess();
|
||||
}
|
||||
|
||||
void ledsProcess() {
|
||||
// Just return if it is not the time...
|
||||
if ((hwMillis() - prevTime) < LED_PROCESS_INTERVAL_MS)
|
||||
return;
|
||||
|
||||
prevTime += LED_PROCESS_INTERVAL_MS;
|
||||
|
||||
uint8_t state;
|
||||
|
||||
// For an On/Off ratio of 4, the pattern repeated will be [on, on, on, off]
|
||||
// until the counter becomes 0.
|
||||
state = (countRx & (LED_ON_OFF_RATIO-1)) ? LED_ON : LED_OFF;
|
||||
hwDigitalWrite(MY_DEFAULT_RX_LED_PIN, state);
|
||||
if (countRx) --countRx;
|
||||
|
||||
state = (countTx & (LED_ON_OFF_RATIO-1)) ? LED_ON : LED_OFF;
|
||||
hwDigitalWrite(MY_DEFAULT_TX_LED_PIN, state);
|
||||
if (countTx) --countTx;
|
||||
|
||||
state = (countErr & (LED_ON_OFF_RATIO-1)) ? LED_ON : LED_OFF;
|
||||
hwDigitalWrite(MY_DEFAULT_ERR_LED_PIN, state);
|
||||
if (countErr) --countErr;
|
||||
}
|
||||
|
||||
void ledsBlinkRx(uint8_t cnt) {
|
||||
if (!countRx) { countRx = cnt*LED_ON_OFF_RATIO; }
|
||||
}
|
||||
|
||||
void ledsBlinkTx(uint8_t cnt) {
|
||||
if(!countTx) { countTx = cnt*LED_ON_OFF_RATIO; }
|
||||
}
|
||||
|
||||
void ledsBlinkErr(uint8_t cnt) {
|
||||
if(!countErr) { countErr = cnt*LED_ON_OFF_RATIO; }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
|
||||
//This may be used to change user task stack size:
|
||||
//#define CONT_STACKSIZE 4096
|
||||
#include "Schedule.h"
|
||||
extern "C" {
|
||||
#include "ets_sys.h"
|
||||
#include "os_type.h"
|
||||
#include "osapi.h"
|
||||
#include "mem.h"
|
||||
#include "user_interface.h"
|
||||
#include "cont.h"
|
||||
}
|
||||
#include <core_version.h>
|
||||
|
||||
#define LOOP_TASK_PRIORITY 1
|
||||
#define LOOP_QUEUE_SIZE 1
|
||||
|
||||
#define OPTIMISTIC_YIELD_TIME_US 16000
|
||||
|
||||
struct rst_info resetInfo;
|
||||
|
||||
extern "C" {
|
||||
extern const uint32_t __attribute__((section(".ver_number"))) core_version = ARDUINO_ESP8266_GIT_VER;
|
||||
const char* core_release =
|
||||
#ifdef ARDUINO_ESP8266_RELEASE
|
||||
ARDUINO_ESP8266_RELEASE;
|
||||
#else
|
||||
NULL;
|
||||
#endif
|
||||
} // extern "C"
|
||||
|
||||
int atexit(void(*func)()) {
|
||||
(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 esp_yield() {
|
||||
if (cont_can_yield(&g_cont)) {
|
||||
cont_yield(&g_cont);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void esp_schedule() {
|
||||
ets_post(LOOP_TASK_PRIORITY, 0, 0);
|
||||
}
|
||||
|
||||
extern "C" void __yield() {
|
||||
if (cont_can_yield(&g_cont)) {
|
||||
esp_schedule();
|
||||
esp_yield();
|
||||
}
|
||||
else {
|
||||
panic();
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
preloop_update_frequency();
|
||||
if (!setup_done) {
|
||||
_begin(); // Startup MySensors library
|
||||
setup_done = true;
|
||||
}
|
||||
_process(); // Process incoming data
|
||||
loop();
|
||||
run_scheduled_functions();
|
||||
esp_schedule();
|
||||
}
|
||||
|
||||
static void loop_task(os_event_t *events) {
|
||||
(void)events;
|
||||
g_micros_at_task_start = system_get_time();
|
||||
cont_run(&g_cont, &loop_wrapper);
|
||||
if (cont_check(&g_cont) != 0) {
|
||||
panic();
|
||||
}
|
||||
}
|
||||
|
||||
static void do_global_ctors(void) {
|
||||
void(**p)(void) = &__init_array_end;
|
||||
while (p != &__init_array_start)
|
||||
(*--p)();
|
||||
}
|
||||
|
||||
extern "C" void __gdb_init() {}
|
||||
extern "C" void gdb_init(void) __attribute__((weak, alias("__gdb_init")));
|
||||
|
||||
extern "C" void __gdb_do_break() {}
|
||||
extern "C" void gdb_do_break(void) __attribute__((weak, alias("__gdb_do_break")));
|
||||
|
||||
void init_done() {
|
||||
system_set_os_print(1);
|
||||
gdb_init();
|
||||
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);
|
||||
|
||||
ets_task(loop_task,
|
||||
LOOP_TASK_PRIORITY, g_loop_queue,
|
||||
LOOP_QUEUE_SIZE);
|
||||
|
||||
system_init_done_cb(&init_done);
|
||||
}
|
||||
@@ -23,14 +23,28 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
MyMessage::MyMessage() {
|
||||
destination = 0; // Gateway is default destination
|
||||
MyMessage::MyMessage()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
MyMessage::MyMessage(uint8_t _sensor, uint8_t _type) {
|
||||
destination = 0; // Gateway is default destination
|
||||
MyMessage::MyMessage(uint8_t _sensor, uint8_t _type)
|
||||
{
|
||||
clear();
|
||||
sensor = _sensor;
|
||||
type = _type;
|
||||
type = _type;
|
||||
}
|
||||
|
||||
void MyMessage::clear()
|
||||
{
|
||||
last = 0u;
|
||||
sender = 0u;
|
||||
destination = 0u; // Gateway is default destination
|
||||
version_length = 0u;
|
||||
command_ack_payload = 0u;
|
||||
type = 0u;
|
||||
sensor = 0u;
|
||||
(void)memset(data, 0u, sizeof(data));
|
||||
}
|
||||
|
||||
bool MyMessage::isAck() const {
|
||||
@@ -0,0 +1,379 @@
|
||||
/*
|
||||
* 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 <henrik.ekblad@mysensors.org>
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file MyMessage.h
|
||||
*
|
||||
* @brief API and type declarations for MySensors messages
|
||||
* @defgroup MyMessagegrp MyMessage
|
||||
* @{
|
||||
*
|
||||
* @brief Here you can find all message types used by the MySensors protocol as well as macros for
|
||||
* parsing and manipulating messages.
|
||||
*/
|
||||
#ifndef MyMessage_h
|
||||
#define MyMessage_h
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include <Arduino.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
#define PROTOCOL_VERSION 2 //!< The version of the protocol
|
||||
#define MAX_MESSAGE_LENGTH 32 //!< The maximum size of a message (including header)
|
||||
#define HEADER_SIZE 7 //!< The size of the header
|
||||
#define MAX_PAYLOAD (MAX_MESSAGE_LENGTH - HEADER_SIZE) //!< The maximum size of a payload depends on #MAX_MESSAGE_LENGTH and #HEADER_SIZE
|
||||
|
||||
/// @brief The command field (message-type) defines the overall properties of a message
|
||||
typedef enum {
|
||||
C_PRESENTATION = 0, //!< Sent by a node when they present attached sensors. This is usually done in presentation() at startup.
|
||||
C_SET = 1, //!< This message is sent from or to a sensor when a sensor value should be updated.
|
||||
C_REQ = 2, //!< Requests a variable value (usually from an actuator destined for controller).
|
||||
C_INTERNAL = 3, //!< Internal MySensors messages (also include common messages provided/generated by the library).
|
||||
C_STREAM = 4 //!< For firmware and other larger chunks of data that need to be divided into pieces.
|
||||
} mysensor_command;
|
||||
|
||||
/// @brief Type of sensor (used when presenting sensors)
|
||||
typedef enum {
|
||||
S_DOOR = 0, //!< Door sensor, V_TRIPPED, V_ARMED
|
||||
S_MOTION = 1, //!< Motion sensor, V_TRIPPED, V_ARMED
|
||||
S_SMOKE = 2, //!< Smoke sensor, V_TRIPPED, V_ARMED
|
||||
S_BINARY = 3, //!< Binary light or relay, V_STATUS, V_WATT
|
||||
S_LIGHT = 3, //!< \deprecated Same as S_BINARY, **** DEPRECATED, DO NOT USE ****
|
||||
S_DIMMER = 4, //!< Dimmable light or fan device, V_STATUS (on/off), V_PERCENTAGE (dimmer level 0-100), V_WATT
|
||||
S_COVER = 5, //!< Blinds or window cover, V_UP, V_DOWN, V_STOP, V_PERCENTAGE (open/close to a percentage)
|
||||
S_TEMP = 6, //!< Temperature sensor, V_TEMP
|
||||
S_HUM = 7, //!< Humidity sensor, V_HUM
|
||||
S_BARO = 8, //!< Barometer sensor, V_PRESSURE, V_FORECAST
|
||||
S_WIND = 9, //!< Wind sensor, V_WIND, V_GUST
|
||||
S_RAIN = 10, //!< Rain sensor, V_RAIN, V_RAINRATE
|
||||
S_UV = 11, //!< Uv sensor, V_UV
|
||||
S_WEIGHT = 12, //!< Personal scale sensor, V_WEIGHT, V_IMPEDANCE
|
||||
S_POWER = 13, //!< Power meter, V_WATT, V_KWH, V_VAR, V_VA, V_POWER_FACTOR
|
||||
S_HEATER = 14, //!< Header device, V_HVAC_SETPOINT_HEAT, V_HVAC_FLOW_STATE, V_TEMP
|
||||
S_DISTANCE = 15, //!< Distance sensor, V_DISTANCE
|
||||
S_LIGHT_LEVEL = 16, //!< Light level sensor, V_LIGHT_LEVEL (uncalibrated in percentage), V_LEVEL (light level in lux)
|
||||
S_ARDUINO_NODE = 17, //!< Used (internally) for presenting a non-repeating Arduino node
|
||||
S_ARDUINO_REPEATER_NODE = 18, //!< Used (internally) for presenting a repeating Arduino node
|
||||
S_LOCK = 19, //!< Lock device, V_LOCK_STATUS
|
||||
S_IR = 20, //!< IR device, V_IR_SEND, V_IR_RECEIVE
|
||||
S_WATER = 21, //!< Water meter, V_FLOW, V_VOLUME
|
||||
S_AIR_QUALITY = 22, //!< Air quality sensor, V_LEVEL
|
||||
S_CUSTOM = 23, //!< Custom sensor
|
||||
S_DUST = 24, //!< Dust sensor, V_LEVEL
|
||||
S_SCENE_CONTROLLER = 25, //!< Scene controller device, V_SCENE_ON, V_SCENE_OFF.
|
||||
S_RGB_LIGHT = 26, //!< RGB light. Send color component data using V_RGB. Also supports V_WATT
|
||||
S_RGBW_LIGHT = 27, //!< RGB light with an additional White component. Send data using V_RGBW. Also supports V_WATT
|
||||
S_COLOR_SENSOR = 28, //!< Color sensor, send color information using V_RGB
|
||||
S_HVAC = 29, //!< Thermostat/HVAC device. V_HVAC_SETPOINT_HEAT, V_HVAC_SETPOINT_COLD, V_HVAC_FLOW_STATE, V_HVAC_FLOW_MODE, V_TEMP
|
||||
S_MULTIMETER = 30, //!< Multimeter device, V_VOLTAGE, V_CURRENT, V_IMPEDANCE
|
||||
S_SPRINKLER = 31, //!< Sprinkler, V_STATUS (turn on/off), V_TRIPPED (if fire detecting device)
|
||||
S_WATER_LEAK = 32, //!< Water leak sensor, V_TRIPPED, V_ARMED
|
||||
S_SOUND = 33, //!< Sound sensor, V_TRIPPED, V_ARMED, V_LEVEL (sound level in dB)
|
||||
S_VIBRATION = 34, //!< Vibration sensor, V_TRIPPED, V_ARMED, V_LEVEL (vibration in Hz)
|
||||
S_MOISTURE = 35, //!< Moisture sensor, V_TRIPPED, V_ARMED, V_LEVEL (water content or moisture in percentage?)
|
||||
S_INFO = 36, //!< LCD text device / Simple information device on controller, V_TEXT
|
||||
S_GAS = 37, //!< Gas meter, V_FLOW, V_VOLUME
|
||||
S_GPS = 38, //!< GPS Sensor, V_POSITION
|
||||
S_WATER_QUALITY = 39 //!< V_TEMP, V_PH, V_ORP, V_EC, V_STATUS
|
||||
} mysensor_sensor;
|
||||
|
||||
/// @brief Type of sensor data (for set/req/ack messages)
|
||||
typedef enum {
|
||||
V_TEMP = 0, //!< S_TEMP. Temperature S_TEMP, S_HEATER, S_HVAC
|
||||
V_HUM = 1, //!< S_HUM. Humidity
|
||||
V_STATUS = 2, //!< S_BINARY, S_DIMMER, S_SPRINKLER, S_HVAC, S_HEATER. Used for setting/reporting binary (on/off) status. 1=on, 0=off
|
||||
V_LIGHT = 2, //!< \deprecated Same as V_STATUS, **** DEPRECATED, DO NOT USE ****
|
||||
V_PERCENTAGE = 3, //!< S_DIMMER. Used for sending a percentage value 0-100 (%).
|
||||
V_DIMMER = 3, //!< \deprecated Same as V_PERCENTAGE, **** DEPRECATED, DO NOT USE ****
|
||||
V_PRESSURE = 4, //!< S_BARO. Atmospheric Pressure
|
||||
V_FORECAST = 5, //!< S_BARO. Whether forecast. string of "stable", "sunny", "cloudy", "unstable", "thunderstorm" or "unknown"
|
||||
V_RAIN = 6, //!< S_RAIN. Amount of rain
|
||||
V_RAINRATE = 7, //!< S_RAIN. Rate of rain
|
||||
V_WIND = 8, //!< S_WIND. Wind speed
|
||||
V_GUST = 9, //!< S_WIND. Gust
|
||||
V_DIRECTION = 10, //!< S_WIND. Wind direction 0-360 (degrees)
|
||||
V_UV = 11, //!< S_UV. UV light level
|
||||
V_WEIGHT = 12, //!< S_WEIGHT. Weight(for scales etc)
|
||||
V_DISTANCE = 13, //!< S_DISTANCE. Distance
|
||||
V_IMPEDANCE = 14, //!< S_MULTIMETER, S_WEIGHT. Impedance value
|
||||
V_ARMED = 15, //!< S_DOOR, S_MOTION, S_SMOKE, S_SPRINKLER. Armed status of a security sensor. 1 = Armed, 0 = Bypassed
|
||||
V_TRIPPED = 16, //!< S_DOOR, S_MOTION, S_SMOKE, S_SPRINKLER, S_WATER_LEAK, S_SOUND, S_VIBRATION, S_MOISTURE. Tripped status of a security sensor. 1 = Tripped, 0
|
||||
V_WATT = 17, //!< S_POWER, S_BINARY, S_DIMMER, S_RGB_LIGHT, S_RGBW_LIGHT. Watt value for power meters
|
||||
V_KWH = 18, //!< S_POWER. Accumulated number of KWH for a power meter
|
||||
V_SCENE_ON = 19, //!< S_SCENE_CONTROLLER. Turn on a scene
|
||||
V_SCENE_OFF = 20, //!< S_SCENE_CONTROLLER. Turn of a scene
|
||||
V_HVAC_FLOW_STATE = 21, //!< S_HEATER, S_HVAC. HVAC flow state ("Off", "HeatOn", "CoolOn", or "AutoChangeOver")
|
||||
V_HEATER = 21, //!< \deprecated Same as V_HVAC_FLOW_STATE, **** DEPRECATED, DO NOT USE ****
|
||||
V_HVAC_SPEED = 22, //!< S_HVAC, S_HEATER. HVAC/Heater fan speed ("Min", "Normal", "Max", "Auto")
|
||||
V_LIGHT_LEVEL = 23, //!< S_LIGHT_LEVEL. Uncalibrated light level. 0-100%. Use V_LEVEL for light level in lux
|
||||
V_VAR1 = 24, //!< VAR1
|
||||
V_VAR2 = 25, //!< VAR2
|
||||
V_VAR3 = 26, //!< VAR3
|
||||
V_VAR4 = 27, //!< VAR4
|
||||
V_VAR5 = 28, //!< VAR5
|
||||
V_UP = 29, //!< S_COVER. Window covering. Up
|
||||
V_DOWN = 30, //!< S_COVER. Window covering. Down
|
||||
V_STOP = 31, //!< S_COVER. Window covering. Stop
|
||||
V_IR_SEND = 32, //!< S_IR. Send out an IR-command
|
||||
V_IR_RECEIVE = 33, //!< S_IR. This message contains a received IR-command
|
||||
V_FLOW = 34, //!< S_WATER. Flow of water (in meter)
|
||||
V_VOLUME = 35, //!< S_WATER. Water volume
|
||||
V_LOCK_STATUS = 36, //!< S_LOCK. Set or get lock status. 1=Locked, 0=Unlocked
|
||||
V_LEVEL = 37, //!< S_DUST, S_AIR_QUALITY, S_SOUND (dB), S_VIBRATION (hz), S_LIGHT_LEVEL (lux)
|
||||
V_VOLTAGE = 38, //!< S_MULTIMETER
|
||||
V_CURRENT = 39, //!< S_MULTIMETER
|
||||
V_RGB = 40, //!< S_RGB_LIGHT, S_COLOR_SENSOR. Sent as ASCII hex: RRGGBB (RR=red, GG=green, BB=blue component)
|
||||
V_RGBW = 41, //!< S_RGBW_LIGHT. Sent as ASCII hex: RRGGBBWW (WW=white component)
|
||||
V_ID = 42, //!< Used for sending in sensors hardware ids (i.e. OneWire DS1820b).
|
||||
V_UNIT_PREFIX = 43, //!< Allows sensors to send in a string representing the unit prefix to be displayed in GUI, not parsed by controller! E.g. cm, m, km, inch.
|
||||
V_HVAC_SETPOINT_COOL = 44, //!< S_HVAC. HVAC cool setpoint (Integer between 0-100)
|
||||
V_HVAC_SETPOINT_HEAT = 45, //!< S_HEATER, S_HVAC. HVAC/Heater setpoint (Integer between 0-100)
|
||||
V_HVAC_FLOW_MODE = 46, //!< S_HVAC. Flow mode for HVAC ("Auto", "ContinuousOn", "PeriodicOn")
|
||||
V_TEXT = 47, //!< S_INFO. Text message to display on LCD or controller device
|
||||
V_CUSTOM = 48, //!< Custom messages used for controller/inter node specific commands, preferably using S_CUSTOM device type.
|
||||
V_POSITION = 49, //!< GPS position and altitude. Payload: latitude;longitude;altitude(m). E.g. "55.722526;13.017972;18"
|
||||
V_IR_RECORD = 50, //!< Record IR codes S_IR for playback
|
||||
V_PH = 51, //!< S_WATER_QUALITY, water PH
|
||||
V_ORP = 52, //!< S_WATER_QUALITY, water ORP : redox potential in mV
|
||||
V_EC = 53, //!< S_WATER_QUALITY, water electric conductivity μS/cm (microSiemens/cm)
|
||||
V_VAR = 54, //!< S_POWER, Reactive power: volt-ampere reactive (var)
|
||||
V_VA = 55, //!< S_POWER, Apparent power: volt-ampere (VA)
|
||||
V_POWER_FACTOR = 56, //!< S_POWER, Ratio of real power to apparent power: floating point value in the range [-1,..,1]
|
||||
} mysensor_data;
|
||||
|
||||
|
||||
/// @brief Type of internal messages (for internal messages)
|
||||
typedef enum {
|
||||
I_BATTERY_LEVEL = 0, //!< Battery level
|
||||
I_TIME = 1, //!< Time
|
||||
I_VERSION = 2, //!< Version
|
||||
I_ID_REQUEST = 3, //!< ID request
|
||||
I_ID_RESPONSE = 4, //!< ID response
|
||||
I_INCLUSION_MODE = 5, //!< Inclusion mode
|
||||
I_CONFIG = 6, //!< Config
|
||||
I_FIND_PARENT = 7, //!< Find parent
|
||||
I_FIND_PARENT_RESPONSE = 8, //!< Find parent response
|
||||
I_LOG_MESSAGE = 9, //!< Log message
|
||||
I_CHILDREN = 10, //!< Children
|
||||
I_SKETCH_NAME = 11, //!< Sketch name
|
||||
I_SKETCH_VERSION = 12, //!< Sketch version
|
||||
I_REBOOT = 13, //!< Reboot request
|
||||
I_GATEWAY_READY = 14, //!< Gateway ready
|
||||
I_SIGNING_PRESENTATION = 15, //!< Provides signing related preferences (first byte is preference version)
|
||||
I_NONCE_REQUEST = 16, //!< Request for a nonce
|
||||
I_NONCE_RESPONSE = 17, //!< Payload is nonce data
|
||||
I_HEARTBEAT = 18, //!< Heartbeat request
|
||||
I_PRESENTATION = 19, //!< Presentation message
|
||||
I_DISCOVER = 20, //!< Discover request
|
||||
I_DISCOVER_RESPONSE = 21, //!< Discover response
|
||||
I_HEARTBEAT_RESPONSE = 22, //!< Heartbeat response
|
||||
I_LOCKED = 23, //!< Node is locked (reason in string-payload)
|
||||
I_PING = 24, //!< Ping sent to node, payload incremental hop counter
|
||||
I_PONG = 25, //!< In return to ping, sent back to sender, payload incremental hop counter
|
||||
I_REGISTRATION_REQUEST = 26, //!< Register request to GW
|
||||
I_REGISTRATION_RESPONSE = 27, //!< Register response from GW
|
||||
I_DEBUG = 28 //!< Debug message
|
||||
} mysensor_internal;
|
||||
|
||||
|
||||
/// @brief Type of data stream (for streamed message)
|
||||
typedef enum {
|
||||
ST_FIRMWARE_CONFIG_REQUEST = 0, //!< Request new FW, payload contains current FW details
|
||||
ST_FIRMWARE_CONFIG_RESPONSE = 1, //!< New FW details to initiate OTA FW update
|
||||
ST_FIRMWARE_REQUEST = 2, //!< Request FW block
|
||||
ST_FIRMWARE_RESPONSE = 3, //!< Response FW block
|
||||
ST_SOUND = 4, //!< Sound
|
||||
ST_IMAGE = 5 //!< Image
|
||||
} mysensor_stream;
|
||||
|
||||
/// @brief Type of payload
|
||||
typedef enum {
|
||||
P_STRING = 0, //!< Payload type is string
|
||||
P_BYTE = 1, //!< Payload type is byte
|
||||
P_INT16 = 2, //!< Payload type is INT16
|
||||
P_UINT16 = 3, //!< Payload type is UINT16
|
||||
P_LONG32 = 4, //!< Payload type is INT32
|
||||
P_ULONG32 = 5, //!< Payload type is UINT32
|
||||
P_CUSTOM = 6, //!< Payload type is binary
|
||||
P_FLOAT32 = 7 //!< Payload type is float32
|
||||
} mysensor_payload;
|
||||
|
||||
|
||||
|
||||
#ifndef BIT
|
||||
#define BIT(n) ( 1<<(n) ) //!< Bit indexing macro
|
||||
#endif
|
||||
#define BIT_MASK(len) ( BIT(len)-1 ) //!< Create a bitmask of length 'len'
|
||||
#define BF_MASK(start, len) ( BIT_MASK(len)<<(start) ) //!< Create a bitfield mask of length starting at bit 'start'
|
||||
|
||||
#define BF_PREP(x, start, len) ( ((x)&BIT_MASK(len)) << (start) ) //!< Prepare a bitmask for insertion or combining
|
||||
#define BF_GET(y, start, len) ( ((y)>>(start)) & BIT_MASK(len) ) //!< Extract a bitfield of length 'len' starting at bit 'start' from 'y'
|
||||
#define BF_SET(y, x, start, len) ( y= ((y) &~ BF_MASK(start, len)) | BF_PREP(x, start, len) ) //!< Insert a new bitfield value 'x' into 'y'
|
||||
|
||||
// Getters/setters for special bit fields in header
|
||||
#define mSetVersion(_msg,_version) BF_SET(_msg.version_length, _version, 0, 2) //!< Set version field
|
||||
#define mGetVersion(_msg) ((uint8_t)BF_GET(_msg.version_length, 0, 2)) //!< Get version field
|
||||
|
||||
#define mSetSigned(_msg,_signed) BF_SET(_msg.version_length, _signed, 2, 1) //!< Set signed field
|
||||
#define mGetSigned(_msg) ((bool)BF_GET(_msg.version_length, 2, 1)) //!< Get versignedsion field
|
||||
|
||||
#define mSetLength(_msg,_length) BF_SET(_msg.version_length, _length, 3, 5) //!< Set length field
|
||||
#define mGetLength(_msg) ((uint8_t)BF_GET(_msg.version_length, 3, 5)) //!< Get length field
|
||||
|
||||
#define mSetCommand(_msg,_command) BF_SET(_msg.command_ack_payload, _command, 0, 3) //!< Set command field
|
||||
#define mGetCommand(_msg) ((uint8_t)BF_GET(_msg.command_ack_payload, 0, 3)) //!< Get command field
|
||||
|
||||
#define mSetRequestAck(_msg,_rack) BF_SET(_msg.command_ack_payload, _rack, 3, 1) //!< Set ack-request field
|
||||
#define mGetRequestAck(_msg) ((bool)BF_GET(_msg.command_ack_payload, 3, 1)) //!< Get ack-request field
|
||||
|
||||
#define mSetAck(_msg,_ackMsg) BF_SET(_msg.command_ack_payload, _ackMsg, 4, 1) //!< Set ack field
|
||||
#define mGetAck(_msg) ((bool)BF_GET(_msg.command_ack_payload, 4, 1)) //!< Get ack field
|
||||
|
||||
#define mSetPayloadType(_msg, _pt) BF_SET(_msg.command_ack_payload, _pt, 5, 3) //!< Set payload type field
|
||||
#define mGetPayloadType(_msg) ((uint8_t)BF_GET(_msg.command_ack_payload, 5, 3)) //!< Get payload type field
|
||||
|
||||
|
||||
// internal access for special fields
|
||||
#define miGetCommand() ((uint8_t)BF_GET(command_ack_payload, 0, 3)) //!< Internal getter for command field
|
||||
|
||||
#define miSetLength(_length) BF_SET(version_length, _length, 3, 5) //!< Internal setter for length field
|
||||
#define miGetLength() ((uint8_t)BF_GET(version_length, 3, 5)) //!< Internal getter for length field
|
||||
|
||||
#define miSetRequestAck(_rack) BF_SET(command_ack_payload, _rack, 3, 1) //!< Internal setter for ack-request field
|
||||
#define miGetRequestAck() ((bool)BF_GET(command_ack_payload, 3, 1)) //!< Internal getter for ack-request field
|
||||
|
||||
#define miSetAck(_ack) BF_SET(command_ack_payload, _ack, 4, 1) //!< Internal setter for ack field
|
||||
#define miGetAck() ((bool)BF_GET(command_ack_payload, 4, 1)) //!< Internal getter for ack field
|
||||
|
||||
#define miSetPayloadType(_pt) BF_SET(command_ack_payload, _pt, 5, 3) //!< Internal setter for payload type field
|
||||
#define miGetPayloadType() (uint8_t)BF_GET(command_ack_payload, 5, 3) //!< Internal getter for payload type field
|
||||
|
||||
|
||||
#if !DOXYGEN
|
||||
#ifdef __cplusplus
|
||||
class MyMessage
|
||||
{
|
||||
private:
|
||||
char* getCustomString(char *buffer) const;
|
||||
|
||||
public:
|
||||
// Constructors
|
||||
MyMessage();
|
||||
|
||||
MyMessage(uint8_t sensor, uint8_t type);
|
||||
|
||||
char i2h(uint8_t i) const;
|
||||
|
||||
/**
|
||||
* Clear message contents.
|
||||
*/
|
||||
void clear();
|
||||
|
||||
/**
|
||||
* If payload is something else than P_STRING you can have the payload value converted
|
||||
* into string representation by supplying a buffer with the minimum size of
|
||||
* 2*MAX_PAYLOAD+1. This is to be able to fit hex-conversion of a full binary payload.
|
||||
*/
|
||||
char* getStream(char *buffer) const;
|
||||
char* getString(char *buffer) const;
|
||||
const char* getString() const;
|
||||
void* getCustom() const;
|
||||
bool getBool() const;
|
||||
uint8_t getByte() const;
|
||||
float getFloat() const;
|
||||
int16_t getInt() const;
|
||||
uint16_t getUInt() const;
|
||||
int32_t getLong() const;
|
||||
uint32_t getULong() const;
|
||||
|
||||
// Getter for command type
|
||||
uint8_t getCommand() const;
|
||||
|
||||
// Getter for ack-flag. True if this is an ack message.
|
||||
bool isAck() const;
|
||||
|
||||
// Setters for building message "on the fly"
|
||||
MyMessage& setType(uint8_t type);
|
||||
MyMessage& setSensor(uint8_t sensor);
|
||||
MyMessage& setDestination(uint8_t destination);
|
||||
|
||||
// Setters for payload
|
||||
MyMessage& set(void* payload, uint8_t length);
|
||||
MyMessage& set(const char* value);
|
||||
MyMessage& set(float value, uint8_t decimals);
|
||||
MyMessage& set(bool value);
|
||||
MyMessage& set(uint8_t value);
|
||||
MyMessage& set(uint32_t value);
|
||||
MyMessage& set(int32_t value);
|
||||
MyMessage& set(uint16_t value);
|
||||
MyMessage& set(int16_t value);
|
||||
|
||||
#else
|
||||
|
||||
typedef union {
|
||||
struct
|
||||
{
|
||||
|
||||
#endif
|
||||
uint8_t last; // 8 bit - Id of last node this message passed
|
||||
uint8_t sender; // 8 bit - Id of sender node (origin)
|
||||
uint8_t destination; // 8 bit - Id of destination node
|
||||
|
||||
uint8_t version_length; // 2 bit - Protocol version
|
||||
// 1 bit - Signed flag
|
||||
// 5 bit - Length of payload
|
||||
uint8_t command_ack_payload; // 3 bit - Command type
|
||||
// 1 bit - Request an ack - Indicator that receiver should send an ack back.
|
||||
// 1 bit - Is ack messsage - Indicator that this is the actual ack message.
|
||||
// 3 bit - Payload data type
|
||||
uint8_t type; // 8 bit - Type varies depending on command
|
||||
uint8_t sensor; // 8 bit - Id of sensor that this message concerns.
|
||||
|
||||
// Each message can transfer a payload. We add one extra byte for string
|
||||
// terminator \0 to be "printable" this is not transferred OTA
|
||||
// This union is used to simplify the construction of the binary data types transferred.
|
||||
union {
|
||||
uint8_t bValue;
|
||||
uint16_t uiValue;
|
||||
int16_t iValue;
|
||||
uint32_t ulValue;
|
||||
int32_t lValue;
|
||||
struct { // Float messages
|
||||
float fValue;
|
||||
uint8_t fPrecision; // Number of decimals when serializing
|
||||
};
|
||||
struct { // Presentation messages
|
||||
uint8_t version; // Library version
|
||||
uint8_t sensorType; // Sensor type hint for controller, see table above
|
||||
};
|
||||
char data[MAX_PAYLOAD + 1];
|
||||
} __attribute__((packed));
|
||||
#ifdef __cplusplus
|
||||
} __attribute__((packed));
|
||||
#else
|
||||
};
|
||||
uint8_t array[HEADER_SIZE + MAX_PAYLOAD + 1];
|
||||
} __attribute__((packed)) MyMessage;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
/** @}*/
|
||||
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* 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 <henrik.ekblad@mysensors.org>
|
||||
* Copyright (C) 2013-2016 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 "MyOTAFirmwareUpdate.h"
|
||||
|
||||
SPIFlash _flash(MY_OTA_FLASH_SS, MY_OTA_FLASH_JDECID);
|
||||
NodeFirmwareConfig _fc;
|
||||
bool _fwUpdateOngoing;
|
||||
unsigned long _fwLastRequestTime;
|
||||
uint16_t _fwBlock;
|
||||
uint8_t _fwRetry;
|
||||
|
||||
inline void readFirmwareSettings() {
|
||||
hwReadConfigBlock((void*)&_fc, (void*)EEPROM_FIRMWARE_TYPE_ADDRESS, sizeof(NodeFirmwareConfig));
|
||||
}
|
||||
|
||||
inline void firmwareOTAUpdateRequest() {
|
||||
unsigned long enter = hwMillis();
|
||||
if (_fwUpdateOngoing && (enter - _fwLastRequestTime > MY_OTA_RETRY_DELAY)) {
|
||||
if (!_fwRetry) {
|
||||
setIndication(INDICATION_ERR_FW_TIMEOUT);
|
||||
debug(PSTR("fw upd fail\n"));
|
||||
// Give up. We have requested MY_OTA_RETRY times without any packet in return.
|
||||
_fwUpdateOngoing = false;
|
||||
return;
|
||||
}
|
||||
_fwRetry--;
|
||||
_fwLastRequestTime = enter;
|
||||
// Time to (re-)request firmware block from controller
|
||||
RequestFWBlock firmwareRequest;
|
||||
firmwareRequest.type = _fc.type;
|
||||
firmwareRequest.version = _fc.version;
|
||||
firmwareRequest.block = (_fwBlock - 1);
|
||||
debug(PSTR("req FW: T=%02X, V=%02X, B=%04X\n"),_fc.type,_fc.version,_fwBlock - 1);
|
||||
_sendRoute(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_STREAM, ST_FIRMWARE_REQUEST, false).set(&firmwareRequest,sizeof(RequestFWBlock)));
|
||||
}
|
||||
}
|
||||
|
||||
inline bool firmwareOTAUpdateProcess() {
|
||||
if (_msg.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))) {
|
||||
setIndication(INDICATION_FW_UPDATE_START);
|
||||
debug(PSTR("fw update\n"));
|
||||
// copy new FW config
|
||||
memcpy(&_fc,firmwareConfigResponse,sizeof(NodeFirmwareConfig));
|
||||
// Init flash
|
||||
if (!_flash.initialize()) {
|
||||
setIndication(INDICATION_ERR_FW_FLASH_INIT);
|
||||
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 true;
|
||||
}
|
||||
debug(PSTR("fw update skipped\n"));
|
||||
} else if (_msg.type == ST_FIRMWARE_RESPONSE) {
|
||||
if (_fwUpdateOngoing) {
|
||||
// Save block to flash
|
||||
setIndication(INDICATION_FW_UPDATE_RX);
|
||||
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 {
|
||||
setIndication(INDICATION_ERR_FW_CHECKSUM);
|
||||
debug(PSTR("fw checksum fail\n"));
|
||||
}
|
||||
}
|
||||
// reset flags
|
||||
_fwRetry = MY_OTA_RETRY+1;
|
||||
_fwLastRequestTime = 0;
|
||||
} else {
|
||||
debug(PSTR("No fw update ongoing\n"));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline void presentBootloaderInformation(){
|
||||
RequestFirmwareConfig *reqFWConfig = (RequestFirmwareConfig *)_msgTmp.data;
|
||||
mSetLength(_msgTmp, sizeof(RequestFirmwareConfig));
|
||||
mSetCommand(_msgTmp, C_STREAM);
|
||||
mSetPayloadType(_msgTmp,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(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_STREAM, ST_FIRMWARE_CONFIG_REQUEST, false));
|
||||
}
|
||||
// do a crc16 on the whole received firmware
|
||||
inline 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;
|
||||
}
|
||||
@@ -6,7 +6,7 @@
|
||||
* network topology allowing messages to be routed to nodes.
|
||||
*
|
||||
* Created by Henrik Ekblad <henrik.ekblad@mysensors.org>
|
||||
* Copyright (C) 2013-2015 Sensnology AB
|
||||
* Copyright (C) 2013-2016 Sensnology AB
|
||||
* Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
|
||||
*
|
||||
* Documentation: http://www.mysensors.org
|
||||
@@ -17,11 +17,10 @@
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef MyTransport_h
|
||||
#define MyTransport_h
|
||||
#ifndef MyOTAFirmwareUpdate_h
|
||||
#define MyOTAFirmwareUpdate_h
|
||||
|
||||
#include <stdint.h>
|
||||
#include "MySensorCore.h"
|
||||
#include "MySensorsCore.h"
|
||||
|
||||
// Size of each firmware block
|
||||
#define FIRMWARE_BLOCK_SIZE 16
|
||||
@@ -38,9 +37,6 @@
|
||||
#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
|
||||
|
||||
|
||||
/// @brief FW config structure, stored in eeprom
|
||||
typedef struct {
|
||||
@@ -75,35 +71,31 @@ typedef struct {
|
||||
} __attribute__((packed)) ReplyFWBlock;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Read firmware settings from EEPROM
|
||||
*
|
||||
* Current firmware settings (type, version, crc, blocks) are read into _fc
|
||||
*/
|
||||
void readFirmwareSettings();
|
||||
/**
|
||||
* @brief Handle OTA FW update requests
|
||||
*/
|
||||
void firmwareOTAUpdateRequest();
|
||||
/**
|
||||
* @brief Handle OTA FW update responses
|
||||
*
|
||||
* This function handles incoming OTA FW packets and stores them to external flash (Sensebender)
|
||||
*/
|
||||
bool firmwareOTAUpdateProcess();
|
||||
/**
|
||||
* @brief Validate uploaded FW CRC
|
||||
*
|
||||
* This function verifies if uploaded FW CRC is valid
|
||||
*/
|
||||
bool transportIsValidFirmware();
|
||||
/**
|
||||
* @brief Present bootloader/FW information upon startup
|
||||
*/
|
||||
void presentBootloaderInformation();
|
||||
|
||||
#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
|
||||
#endif
|
||||
@@ -22,7 +22,7 @@
|
||||
* it in the sketch:
|
||||
* #include "MyPrivateConfig.h"
|
||||
* Make sure to include it before
|
||||
* #include <MySensor.h>
|
||||
* #include <MySensors.h>
|
||||
* Then adapt the contents to your personal preference.
|
||||
*/
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
#ifndef MyProtocol_h
|
||||
#define MyProtocol_h
|
||||
|
||||
#include "MySensorCore.h"
|
||||
#include "MySensorsCore.h"
|
||||
|
||||
|
||||
// parse(message, inputString)
|
||||
@@ -96,7 +96,7 @@ bool protocolParse(MyMessage &message, char *inputString) {
|
||||
}
|
||||
|
||||
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));
|
||||
snprintf_P(_fmtBuffer, MY_GATEWAY_MAX_SEND_LENGTH, PSTR("%d;%d;%d;%d;%d;%s\n"), message.sender, message.sensor, (uint8_t)mGetCommand(message), (uint8_t)mGetAck(message), message.type, message.getString(_convBuffer));
|
||||
return _fmtBuffer;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,561 @@
|
||||
/*
|
||||
* 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 <henrik.ekblad@mysensors.org>
|
||||
* 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 "MySensorsCore.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).
|
||||
|
||||
bool _nodeRegistered = false;
|
||||
|
||||
#if defined(MY_DEBUG)
|
||||
char _convBuf[MAX_PAYLOAD*2+1];
|
||||
#endif
|
||||
|
||||
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
|
||||
|
||||
}
|
||||
|
||||
void _infiniteLoop() {
|
||||
while(1) {
|
||||
#if defined(ARDUINO_ARCH_ESP8266)
|
||||
yield();
|
||||
#endif
|
||||
#if defined (MY_LEDS_BLINKING_FEATURE)
|
||||
ledsProcess();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void _begin() {
|
||||
|
||||
if (preHwInit)
|
||||
preHwInit();
|
||||
|
||||
#if !defined(MY_DISABLED_SERIAL)
|
||||
hwInit();
|
||||
#endif
|
||||
|
||||
debug(PSTR("MCO:BGN:INIT " MY_NODE_TYPE ",CP=" MY_CAPABILITIES ",VER=" MYSENSORS_LIBRARY_VERSION "\n"));
|
||||
|
||||
// Call before() in sketch (if it exists)
|
||||
if (before)
|
||||
before();
|
||||
|
||||
#if defined(MY_LEDS_BLINKING_FEATURE)
|
||||
ledsInit();
|
||||
#endif
|
||||
|
||||
signerInit();
|
||||
|
||||
// Read latest received controller configuration from EEPROM
|
||||
// Note: _cc.isMetric is bool, hence empty EEPROM (=0xFF) evaluates to true (default)
|
||||
hwReadConfigBlock((void*)&_cc, (void*)EEPROM_CONTROLLER_CONFIG_ADDRESS, sizeof(ControllerConfig));
|
||||
|
||||
#if defined(MY_OTA_FIRMWARE_FEATURE)
|
||||
// Read firmware config from EEPROM, i.e. type, version, CRC, blocks
|
||||
readFirmwareSettings();
|
||||
#endif
|
||||
|
||||
#if defined(MY_RADIO_FEATURE)
|
||||
// Save static parent id in eeprom (used by bootloader)
|
||||
hwWriteConfig(EEPROM_PARENT_NODE_ID_ADDRESS, MY_PARENT_NODE_ID);
|
||||
transportInitialize();
|
||||
while (!isTransportReady()) {
|
||||
hwWatchdogReset();
|
||||
transportProcess();
|
||||
#if defined(ARDUINO_ARCH_ESP8266)
|
||||
yield();
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#ifdef MY_NODE_LOCK_FEATURE
|
||||
// Check if node has been locked down
|
||||
if (hwReadConfig(EEPROM_NODE_LOCK_COUNTER) == 0) {
|
||||
// Node is locked, check if unlock pin is asserted, else hang the node
|
||||
pinMode(MY_NODE_UNLOCK_PIN, INPUT_PULLUP);
|
||||
// Make a short delay so we are sure any large external nets are fully pulled
|
||||
unsigned long enter = hwMillis();
|
||||
while (hwMillis() - enter < 2);
|
||||
if (digitalRead(MY_NODE_UNLOCK_PIN) == 0) {
|
||||
// Pin is grounded, reset lock counter
|
||||
hwWriteConfig(EEPROM_NODE_LOCK_COUNTER, MY_NODE_LOCK_COUNTER_MAX);
|
||||
// Disable pullup
|
||||
pinMode(MY_NODE_UNLOCK_PIN, INPUT);
|
||||
setIndication(INDICATION_ERR_LOCKED);
|
||||
debug(PSTR("MCO:BGN:NODE UNLOCKED\n"));
|
||||
} else {
|
||||
// Disable pullup
|
||||
pinMode(MY_NODE_UNLOCK_PIN, INPUT);
|
||||
nodeLock("LDB"); //Locked during boot
|
||||
}
|
||||
} else if (hwReadConfig(EEPROM_NODE_LOCK_COUNTER) == 0xFF) {
|
||||
// Reset walue
|
||||
hwWriteConfig(EEPROM_NODE_LOCK_COUNTER, MY_NODE_LOCK_COUNTER_MAX);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(MY_GATEWAY_FEATURE)
|
||||
#if defined(MY_INCLUSION_BUTTON_FEATURE)
|
||||
inclusionInit();
|
||||
#endif
|
||||
|
||||
// initialize the transport driver
|
||||
if (!gatewayTransportInit()) {
|
||||
setIndication(INDICATION_ERR_INIT_GWTRANSPORT);
|
||||
debug(PSTR("!MCO:BGN:TSP FAIL\n"));
|
||||
// Nothing more we can do
|
||||
_infiniteLoop();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !defined(MY_GATEWAY_FEATURE)
|
||||
presentNode();
|
||||
#endif
|
||||
|
||||
// register node
|
||||
_registerNode();
|
||||
|
||||
// Call sketch setup
|
||||
if (setup) {
|
||||
setup();
|
||||
}
|
||||
|
||||
debug(PSTR("MCO:BGN:INIT OK,ID=%d,PAR=%d,DIS=%d,REG=%d\n"), _nc.nodeId, _nc.parentNodeId, _nc.distance, _nodeRegistered);
|
||||
}
|
||||
|
||||
|
||||
void _registerNode() {
|
||||
#if defined (MY_REGISTRATION_FEATURE) && !defined(MY_GATEWAY_FEATURE)
|
||||
debug(PSTR("MCO:REG:REQ\n")); // registration request
|
||||
setIndication(INDICATION_REQ_REGISTRATION);
|
||||
_nodeRegistered = MY_REGISTRATION_DEFAULT;
|
||||
uint8_t counter = MY_REGISTRATION_RETRIES;
|
||||
// only proceed if register response received or retries exceeded
|
||||
do {
|
||||
_sendRoute(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_REGISTRATION_REQUEST, false).set(MY_CORE_VERSION));
|
||||
} while (!wait(2000, C_INTERNAL, I_REGISTRATION_RESPONSE) && counter--);
|
||||
|
||||
#else
|
||||
_nodeRegistered = true;
|
||||
debug(PSTR("MCO:REG:NOT NEEDED\n"));
|
||||
#endif
|
||||
}
|
||||
|
||||
void presentNode() {
|
||||
setIndication(INDICATION_PRESENT);
|
||||
// Present node and request config
|
||||
#if defined(MY_GATEWAY_FEATURE)
|
||||
// Send presentation for this gateway device
|
||||
#if defined(MY_REPEATER_FEATURE)
|
||||
present(NODE_SENSOR_ID, S_ARDUINO_REPEATER_NODE);
|
||||
#else
|
||||
present(NODE_SENSOR_ID, S_ARDUINO_NODE);
|
||||
#endif
|
||||
#else
|
||||
|
||||
#if defined(MY_OTA_FIRMWARE_FEATURE)
|
||||
presentBootloaderInformation();
|
||||
#endif
|
||||
|
||||
// Send signing preferences for this node to the GW
|
||||
signerPresentation(_msgTmp, GATEWAY_ADDRESS);
|
||||
|
||||
// Send presentation for this radio node
|
||||
#if defined(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
|
||||
_sendRoute(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_CONFIG, false).set(_nc.parentNodeId));
|
||||
|
||||
// Wait configuration reply.
|
||||
wait(2000, C_INTERNAL, I_CONFIG);
|
||||
|
||||
#endif
|
||||
|
||||
if (presentation)
|
||||
presentation();
|
||||
|
||||
}
|
||||
|
||||
|
||||
uint8_t getNodeId() {
|
||||
return _nc.nodeId;
|
||||
}
|
||||
|
||||
uint8_t getParentNodeId() {
|
||||
return _nc.parentNodeId;
|
||||
}
|
||||
|
||||
ControllerConfig getConfig() {
|
||||
return _cc;
|
||||
}
|
||||
|
||||
|
||||
bool _sendRoute(MyMessage &message) {
|
||||
#if defined(MY_CORE_ONLY)
|
||||
(void)message;
|
||||
#endif
|
||||
#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.
|
||||
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);
|
||||
|
||||
#if defined(MY_REGISTRATION_FEATURE) && !defined(MY_GATEWAY_FEATURE)
|
||||
if (_nodeRegistered) {
|
||||
return _sendRoute(message);
|
||||
}
|
||||
else {
|
||||
debug(PSTR("!MCO:SND:NODE NOT REG\n")); // node not registered
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
return _sendRoute(message);
|
||||
#endif
|
||||
}
|
||||
|
||||
void sendBatteryLevel(uint8_t value, bool enableAck) {
|
||||
_sendRoute(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_BATTERY_LEVEL, enableAck).set(value));
|
||||
}
|
||||
|
||||
void sendHeartbeat(void) {
|
||||
#if defined(MY_RADIO_NRF24) || defined(MY_RADIO_RFM69) || defined(MY_RS485)
|
||||
uint32_t heartbeat = transportGetHeartbeat();
|
||||
#else
|
||||
uint32_t heartbeat = hwMillis();
|
||||
#endif
|
||||
_sendRoute(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_HEARTBEAT_RESPONSE, false).set(heartbeat));
|
||||
}
|
||||
|
||||
void present(uint8_t childSensorId, uint8_t sensorType, const char *description, bool enableAck) {
|
||||
_sendRoute(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, childSensorId, C_PRESENTATION, sensorType, enableAck).set(childSensorId==NODE_SENSOR_ID?MYSENSORS_LIBRARY_VERSION:description));
|
||||
}
|
||||
|
||||
void sendSketchInfo(const char *name, const char *version, bool enableAck) {
|
||||
if (name) _sendRoute(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_SKETCH_NAME, enableAck).set(name));
|
||||
if (version) _sendRoute(build(_msgTmp, _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(_msgTmp, _nc.nodeId, destination, childSensorId, C_REQ, variableType, false).set(""));
|
||||
}
|
||||
|
||||
void requestTime() {
|
||||
_sendRoute(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_TIME, false).set(""));
|
||||
}
|
||||
|
||||
// Message delivered through _msg
|
||||
bool _processInternalMessages() {
|
||||
uint8_t type = _msg.type;
|
||||
|
||||
if (_msg.sender == GATEWAY_ADDRESS) {
|
||||
if (type == I_REBOOT) {
|
||||
#if !defined(MY_DISABLE_REMOTE_RESET)
|
||||
// Requires MySensors or other bootloader with watchdogs enabled
|
||||
setIndication(INDICATION_REBOOT);
|
||||
hwReboot();
|
||||
#endif
|
||||
}
|
||||
else if (type == I_REGISTRATION_RESPONSE) {
|
||||
#if defined (MY_REGISTRATION_FEATURE) && !defined(MY_GATEWAY_FEATURE)
|
||||
_nodeRegistered = _msg.getBool();
|
||||
setIndication(INDICATION_GOT_REGISTRATION);
|
||||
debug(PSTR("MCO:PIM:NODE REG=%d\n"), _nodeRegistered); // node registration
|
||||
#endif
|
||||
}
|
||||
else if (type == I_CONFIG) {
|
||||
// Pick up configuration from controller (currently only metric/imperial) and store it in eeprom if changed
|
||||
_cc.isMetric = _msg.data[0] == 0x00 || _msg.data[0] == 'M'; // metric if null terminated or M
|
||||
hwWriteConfig(EEPROM_CONTROLLER_CONFIG_ADDRESS, _cc.isMetric);
|
||||
}
|
||||
else if (type == I_PRESENTATION) {
|
||||
// Re-send node presentation to controller
|
||||
presentNode();
|
||||
}
|
||||
else if (type == I_HEARTBEAT) {
|
||||
sendHeartbeat();
|
||||
}
|
||||
else if (type == I_TIME) {
|
||||
// Deliver time to callback
|
||||
if (receiveTime)
|
||||
receiveTime(_msg.getULong());
|
||||
}
|
||||
else if (type == I_CHILDREN) {
|
||||
#if defined(MY_REPEATER_FEATURE)
|
||||
if (_msg.data[0] == 'C') {
|
||||
// Clears child relay data for this node
|
||||
setIndication(INDICATION_CLEAR_ROUTING);
|
||||
transportClearRoutingTable();
|
||||
_sendRoute(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_CHILDREN, false).set("ok"));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else if (type == I_DEBUG) {
|
||||
#if defined(MY_DEBUG) || defined(MY_SPECIAL_DEBUG)
|
||||
char debug_msg = _msg.data[0];
|
||||
if (debug_msg == 'R') { // routing table
|
||||
#if defined(MY_REPEATER_FEATURE)
|
||||
for (uint8_t cnt = 0; cnt != 255; cnt++) {
|
||||
uint8_t route = hwReadConfig(EEPROM_ROUTES_ADDRESS + cnt);
|
||||
if (route != BROADCAST_ADDRESS) {
|
||||
debug(PSTR("MCO:PIM:ROUTE N=%d,R=%d\n"), cnt, route);
|
||||
uint8_t OutBuf[2] = { cnt,route };
|
||||
_sendRoute(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_DEBUG, false).set(OutBuf, 2));
|
||||
wait(200);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else if (debug_msg == 'V') { // CPU voltage
|
||||
_sendRoute(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_DEBUG, false).set(hwCPUVoltage()));
|
||||
}
|
||||
else if (debug_msg == 'F') { // CPU frequency in 1/10Mhz
|
||||
_sendRoute(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_DEBUG, false).set(hwCPUFrequency()));
|
||||
}
|
||||
else if (debug_msg == 'M') { // free memory
|
||||
_sendRoute(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_DEBUG, false).set(hwFreeMem()));
|
||||
}
|
||||
else if (debug_msg == 'E') { // clear MySensors eeprom area and reboot
|
||||
_sendRoute(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_DEBUG, false).set("ok"));
|
||||
for (int i = EEPROM_START; i<EEPROM_LOCAL_CONFIG_ADDRESS; i++) hwWriteConfig(i, 0xFF);
|
||||
setIndication(INDICATION_REBOOT);
|
||||
hwReboot();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
else {
|
||||
// sender is a node
|
||||
if (type == I_REGISTRATION_REQUEST) {
|
||||
#if defined(MY_GATEWAY_FEATURE)
|
||||
// register request are exclusively handled by GW/Controller
|
||||
// !!! eventually define if AUTO ACK or register request forwarded to controller
|
||||
#if !defined(MY_REGISTRATION_CONTROLLER)
|
||||
// auto register if version compatible
|
||||
bool approveRegistration = true;
|
||||
|
||||
#if defined(MY_CORE_COMPATIBILITY_CHECK)
|
||||
approveRegistration = (_msg.getByte() >= MY_CORE_MIN_VERSION);
|
||||
#endif
|
||||
_sendRoute(build(_msgTmp, _nc.nodeId, _msg.sender, NODE_SENSOR_ID, C_INTERNAL, I_REGISTRATION_RESPONSE, false).set(approveRegistration));
|
||||
#else
|
||||
return false; // processing of this request via controller
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
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();
|
||||
#if defined(ARDUINO_ARCH_ESP8266)
|
||||
yield();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
bool wait(unsigned long ms, uint8_t cmd, uint8_t msgtype) {
|
||||
unsigned long enter = hwMillis();
|
||||
// invalidate msg type
|
||||
_msg.type = !msgtype;
|
||||
bool expectedResponse = false;
|
||||
while ( (hwMillis() - enter < ms) && !expectedResponse ) {
|
||||
_process();
|
||||
#if defined(ARDUINO_ARCH_ESP8266)
|
||||
yield();
|
||||
#endif
|
||||
expectedResponse = (mGetCommand(_msg) == cmd && _msg.type == msgtype);
|
||||
}
|
||||
return expectedResponse;
|
||||
}
|
||||
|
||||
|
||||
int8_t sleep(unsigned long ms) {
|
||||
#if defined(MY_OTA_FIRMWARE_FEATURE)
|
||||
if (_fwUpdateOngoing) {
|
||||
// Do not sleep node while fw update is ongoing
|
||||
wait(ms);
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
// if repeater, do not sleep
|
||||
#if defined(MY_REPEATER_FEATURE)
|
||||
wait(ms);
|
||||
return -1;
|
||||
#else
|
||||
#if defined(MY_RADIO_FEATURE)
|
||||
transportPowerDown();
|
||||
#endif
|
||||
setIndication(INDICATION_SLEEP);
|
||||
const int8_t res = hwSleep(ms);
|
||||
setIndication(INDICATION_WAKEUP);
|
||||
return res;
|
||||
#endif
|
||||
}
|
||||
|
||||
int8_t smartSleep(unsigned long ms) {
|
||||
// notify controller about going to sleep
|
||||
sendHeartbeat();
|
||||
// listen for incoming messages
|
||||
wait(MY_SMART_SLEEP_WAIT_DURATION);
|
||||
return sleep(ms);
|
||||
}
|
||||
|
||||
int8_t sleep(uint8_t interrupt, uint8_t mode, unsigned long ms) {
|
||||
#if defined(MY_OTA_FIRMWARE_FEATURE)
|
||||
if (_fwUpdateOngoing) {
|
||||
// not supported
|
||||
return -2;
|
||||
}
|
||||
#endif
|
||||
#if defined(MY_REPEATER_FEATURE)
|
||||
// not supported
|
||||
(void)interrupt;
|
||||
(void)mode;
|
||||
(void)ms;
|
||||
return -2;
|
||||
#else
|
||||
#if defined(MY_RADIO_FEATURE)
|
||||
transportPowerDown();
|
||||
#endif
|
||||
setIndication(INDICATION_SLEEP);
|
||||
const int8_t res = hwSleep(interrupt, mode, ms);
|
||||
setIndication(INDICATION_WAKEUP);
|
||||
return res;
|
||||
#endif
|
||||
}
|
||||
|
||||
int8_t smartSleep(uint8_t interrupt, uint8_t mode, unsigned long ms) {
|
||||
// notify controller about going to sleep
|
||||
sendHeartbeat();
|
||||
// listen for incoming messages
|
||||
wait(MY_SMART_SLEEP_WAIT_DURATION);
|
||||
return sleep(interrupt, mode, ms);
|
||||
}
|
||||
|
||||
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) {
|
||||
// not supported
|
||||
return -2;
|
||||
}
|
||||
#endif
|
||||
#if defined(MY_REPEATER_FEATURE)
|
||||
// not supported
|
||||
(void)interrupt1;
|
||||
(void)mode1;
|
||||
(void)interrupt2;
|
||||
(void)mode2;
|
||||
(void)ms;
|
||||
return -2;
|
||||
#else
|
||||
#if defined(MY_RADIO_FEATURE)
|
||||
transportPowerDown();
|
||||
#endif
|
||||
setIndication(INDICATION_SLEEP);
|
||||
const int8_t res = hwSleep(interrupt1, mode1, interrupt2, mode2, ms);
|
||||
setIndication(INDICATION_WAKEUP);
|
||||
return res;
|
||||
#endif
|
||||
}
|
||||
|
||||
int8_t smartSleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2, unsigned long ms) {
|
||||
// notify controller about going to sleep
|
||||
sendHeartbeat();
|
||||
// listen for incoming messages
|
||||
wait(MY_SMART_SLEEP_WAIT_DURATION);
|
||||
return sleep(interrupt1, mode1, interrupt2, mode2, ms);
|
||||
}
|
||||
|
||||
#ifdef MY_NODE_LOCK_FEATURE
|
||||
void nodeLock(const char* str) {
|
||||
// Make sure EEPROM is updated to locked status
|
||||
hwWriteConfig(EEPROM_NODE_LOCK_COUNTER, 0);
|
||||
while (1) {
|
||||
setIndication(INDICATION_ERR_LOCKED);
|
||||
debug(PSTR("MCO:NLK:NODE LOCKED. TO UNLOCK, GND PIN %d AND RESET\n"), MY_NODE_UNLOCK_PIN);
|
||||
#if defined(ARDUINO_ARCH_ESP8266)
|
||||
yield();
|
||||
#endif
|
||||
_sendRoute(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID,C_INTERNAL, I_LOCKED, false).set(str));
|
||||
#if defined(MY_RADIO_FEATURE)
|
||||
transportPowerDown();
|
||||
#endif
|
||||
setIndication(INDICATION_SLEEP);
|
||||
(void)hwSleep((unsigned long)1000*60*30); // Sleep for 30 min before resending LOCKED message
|
||||
setIndication(INDICATION_WAKEUP);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -17,42 +17,74 @@
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file MySensorsCore.h
|
||||
*
|
||||
* @defgroup MySensorsCoregrp MySensorsCore
|
||||
* @ingroup internals
|
||||
* @{
|
||||
*
|
||||
* MySensorsCore-related log messages, format: [!]SYSTEM:[SUB SYSTEM:]MESSAGE
|
||||
* - [!] Exclamation mark is prepended in case of error
|
||||
* - SYSTEM:
|
||||
* - <b>MCO</b> messages emitted by MySensorsCore
|
||||
* - SUB SYSTEMS:
|
||||
* - MCO:<b>BGN</b> from @ref _begin()
|
||||
* - MCO:<b>REG</b> from @ref _registerNode()
|
||||
* - MCO:<b>SND</b> from @ref send()
|
||||
* - MCO:<b>PIM</b> from @ref _processInternalMessages()
|
||||
* - MCO:<b>NLK</b> from @ref nodeLock()
|
||||
*
|
||||
* MySensorsCore debug log messages:
|
||||
*
|
||||
* |E| SYS | SUB | Message | Comment
|
||||
* |-|------|-------|-------------------------------------------|----------------------------------------------------------------------------
|
||||
* | | MCO | BGN | INIT %%s,CP=%%s,LIB=%%s | Core initialization, capabilities (CP), library version (VER)
|
||||
* | | MCO | BGN | INIT OK,ID=%%d,PAR=%%d,DIS=%%d,REG=%%d | Core initialized, parent ID (PAR), distance to GW (DIS), registration (REG)
|
||||
* | | MCO | BGN | NODE UNLOCKED | Node successfully unlocked (see signing chapter)
|
||||
* |!| MCO | BGN | TSP FAIL | Transport initialization failed
|
||||
* | | MCO | REG | REQ | Registration request
|
||||
* | | MCO | REG | NOT NEEDED | No registration needed (i.e. GW)
|
||||
* |!| MCO | SND | NODE NOT REG | Node is not registered, cannot send message
|
||||
* | | MCO | PIM | NODE REG=%%d | Registration response received, registration status (REG)
|
||||
* | | MCO | PIM | ROUTE N=%%d,R=%%d | Routing table, messages to node (N) are routed via node (R)
|
||||
* | | MCO | NLK | NODE LOCKED. UNLOCK: GND PIN %%d AND RESET| Node locked during booting, see signing chapter for additional information
|
||||
*
|
||||
*
|
||||
* @brief API declaration for MySensorsCore
|
||||
*/
|
||||
|
||||
#ifndef MySensorCore_h
|
||||
#define MySensorCore_h
|
||||
|
||||
#ifndef MySensorsCore_h
|
||||
#define MySensorsCore_h
|
||||
|
||||
#include "Version.h" // Auto generated by bot
|
||||
#include "MyConfig.h"
|
||||
#include "MyEepromAddresses.h"
|
||||
#include "MyMessage.h"
|
||||
#ifdef MY_OTA_FIRMWARE_FEATURE
|
||||
#include "drivers/SPIFlash/SPIFlash.h"
|
||||
#endif
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#define GATEWAY_ADDRESS ((uint8_t)0) //!< Node ID for GW sketch
|
||||
#define NODE_SENSOR_ID 0xFF //!< Node child is always created/presented when a node is started
|
||||
#define MY_CORE_VERSION ((uint8_t)2) //!< core version
|
||||
#define MY_CORE_MIN_VERSION ((uint8_t)2) //!< min core version required for compatibility
|
||||
|
||||
#ifdef MY_DEBUG
|
||||
#define debug(x,...) hwDebugPrint(x, ##__VA_ARGS__)
|
||||
#define debug(x,...) hwDebugPrint(x, ##__VA_ARGS__) //!< debug
|
||||
#else
|
||||
#define debug(x,...)
|
||||
#define debug(x,...) //!< debug NULL
|
||||
#endif
|
||||
|
||||
// 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
|
||||
|
||||
|
||||
/**
|
||||
* @brief Node configuration
|
||||
*
|
||||
* This structure stores node-related configurations
|
||||
*/
|
||||
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)
|
||||
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)
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -61,16 +93,33 @@ struct NodeConfig {
|
||||
* This structure stores controllerrelated configurations
|
||||
*/
|
||||
struct ControllerConfig {
|
||||
uint8_t isMetric; //!< Flag indicating if metric or imperial measurements are used
|
||||
uint8_t isMetric; //!< Flag indicating if metric or imperial measurements are used
|
||||
};
|
||||
|
||||
extern NodeConfig _nc; //!< Node config
|
||||
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
|
||||
|
||||
// **** public functions ********
|
||||
|
||||
/**
|
||||
* Return this nodes id.
|
||||
*/
|
||||
uint8_t getNodeId();
|
||||
|
||||
/**
|
||||
* Return the parent node id.
|
||||
*/
|
||||
uint8_t getParentNodeId();
|
||||
|
||||
/**
|
||||
* Sends node information to the gateway.
|
||||
*/
|
||||
void presentNode();
|
||||
|
||||
/**
|
||||
* 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().
|
||||
@@ -132,8 +181,6 @@ void request(uint8_t childSensorId, uint8_t variableType, uint8_t destination=GA
|
||||
*/
|
||||
void requestTime();
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Returns the most recent node configuration received from controller
|
||||
*/
|
||||
@@ -168,12 +215,29 @@ uint8_t loadState(uint8_t pos);
|
||||
*/
|
||||
void wait(unsigned long ms);
|
||||
|
||||
/**
|
||||
* Wait for a specified amount of time to pass or until specified message received. 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.
|
||||
* @param cmd Command of incoming message.
|
||||
* @param msgtype Message type.
|
||||
* @return True if specified message received
|
||||
*/
|
||||
bool wait(unsigned long ms, uint8_t cmd, uint8_t msgtype);
|
||||
|
||||
/**
|
||||
* Sleep (PowerDownMode) the MCU and radio. Wake up on timer.
|
||||
* @param ms Number of milliseconds to sleep.
|
||||
* @return -1 if timer woke it up, -2 if not possible (e.g. ongoing FW update)
|
||||
*/
|
||||
int8_t sleep(unsigned long ms);
|
||||
/**
|
||||
* Same as sleep(), send heartbeat upon wakeup and process incoming messages
|
||||
* @param ms Number of milliseconds to sleep.
|
||||
* @return -1 if timer woke it up, -2 if not possible (e.g. ongoing FW update)
|
||||
*/
|
||||
int8_t smartSleep(unsigned long ms);
|
||||
|
||||
/**
|
||||
@@ -186,6 +250,13 @@ int8_t smartSleep(unsigned long ms);
|
||||
* @return Interrupt number wake up was triggered by pin change, -1 if timer woke it up, -2 if not possible (e.g. ongoing FW update)
|
||||
*/
|
||||
int8_t sleep(uint8_t interrupt, uint8_t mode, unsigned long ms=0);
|
||||
/**
|
||||
* Same as sleep(), send heartbeat upon wakeup and process incoming messages
|
||||
* @param interrupt Interrupt that should trigger the wakeup
|
||||
* @param mode RISING, FALLING, CHANGE
|
||||
* @param ms Number of milliseconds to sleep.
|
||||
* @return -1 if timer woke it up, -2 if not possible (e.g. ongoing FW update)
|
||||
*/
|
||||
int8_t smartSleep(uint8_t interrupt, uint8_t mode, unsigned long ms=0);
|
||||
|
||||
/**
|
||||
@@ -200,6 +271,15 @@ int8_t smartSleep(uint8_t interrupt, uint8_t mode, unsigned long ms=0);
|
||||
* @return Interrupt number wake up was triggered by pin change, -1 if timer woke it up, -2 if not possible (e.g. ongoing FW update)
|
||||
*/
|
||||
int8_t sleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2, unsigned long ms=0);
|
||||
/**
|
||||
* Same as sleep(), send heartbeat upon wakeup and process incoming messages
|
||||
* @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, -1 if timer woke it up, -2 if not possible (e.g. ongoing FW update)
|
||||
*/
|
||||
int8_t smartSleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2, unsigned long ms=0);
|
||||
|
||||
#ifdef MY_NODE_LOCK_FEATURE
|
||||
@@ -217,27 +297,64 @@ int8_t smartSleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t
|
||||
void nodeLock(const char* str);
|
||||
#endif
|
||||
|
||||
/****** PRIVATE ********/
|
||||
// **** private functions ********
|
||||
|
||||
/**
|
||||
* @brief Node initialisation
|
||||
*/
|
||||
void _begin();
|
||||
|
||||
/**
|
||||
* @brief Main framework process
|
||||
*/
|
||||
void _process(void);
|
||||
/**
|
||||
* @brief Processes internal messages
|
||||
* @return True if received message requires further processing
|
||||
*/
|
||||
bool _processInternalMessages();
|
||||
/**
|
||||
* @brief Puts node to a infinite loop if unrecoverable situation detected
|
||||
*/
|
||||
void _infiniteLoop();
|
||||
/**
|
||||
* @brief Handles registration request
|
||||
*/
|
||||
void _registerNode();
|
||||
/**
|
||||
* @brief Sends message according to routing table
|
||||
* @param message
|
||||
* @return True if message successfully sent to next node
|
||||
*/
|
||||
bool _sendRoute(MyMessage &message);
|
||||
|
||||
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
|
||||
/**
|
||||
* @brief Callback for incoming messages
|
||||
* @param message
|
||||
*/
|
||||
void receive(const MyMessage &message) __attribute__((weak));
|
||||
/**
|
||||
* @brief Callback for incoming time messages
|
||||
*/
|
||||
void receiveTime(unsigned long) __attribute__((weak));
|
||||
/**
|
||||
* @brief Node presenation
|
||||
*/
|
||||
void presentation() __attribute__((weak));
|
||||
/**
|
||||
* @brief Called before node initialises
|
||||
*/
|
||||
void before() __attribute__((weak));
|
||||
/**
|
||||
* @brief Called before any hwInitialization is done
|
||||
*/
|
||||
void preHwInit() __attribute__((weak));
|
||||
/**
|
||||
* @brief Called after node initialises but before main loop
|
||||
*/
|
||||
void setup() __attribute__((weak));
|
||||
/**
|
||||
* @brief Main loop
|
||||
*/
|
||||
void loop() __attribute__((weak));
|
||||
|
||||
|
||||
@@ -256,7 +373,7 @@ static inline MyMessage& build(MyMessage &msg, uint8_t sender, uint8_t destinati
|
||||
static inline MyMessage& buildGw(MyMessage &msg, uint8_t type) {
|
||||
msg.sender = GATEWAY_ADDRESS;
|
||||
msg.destination = GATEWAY_ADDRESS;
|
||||
msg.sensor = 255;
|
||||
msg.sensor = NODE_SENSOR_ID;
|
||||
msg.type = type;
|
||||
mSetCommand(msg, C_INTERNAL);
|
||||
mSetRequestAck(msg, false);
|
||||
@@ -266,3 +383,5 @@ static inline MyMessage& buildGw(MyMessage &msg, uint8_t type) {
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
/** @}*/
|
||||
@@ -84,10 +84,12 @@ static bool skipSign(MyMessage &msg) {
|
||||
SIGN_DEBUG(PSTR("Skipping security for ACK on command %d type %d\n"), mGetCommand(msg), msg.type);
|
||||
return true;
|
||||
} else if (mGetCommand(msg) == C_INTERNAL &&
|
||||
(msg.type == I_NONCE_REQUEST || msg.type == I_NONCE_RESPONSE || msg.type == I_SIGNING_PRESENTATION ||
|
||||
msg.type == I_ID_REQUEST || msg.type == I_ID_RESPONSE ||
|
||||
msg.type == I_FIND_PARENT || msg.type == I_FIND_PARENT_RESPONSE ||
|
||||
msg.type == I_HEARTBEAT || msg.type == I_HEARTBEAT_RESPONSE)) {
|
||||
(msg.type == I_NONCE_REQUEST || msg.type == I_NONCE_RESPONSE || msg.type == I_SIGNING_PRESENTATION ||
|
||||
msg.type == I_ID_REQUEST || msg.type == I_ID_RESPONSE ||
|
||||
msg.type == I_FIND_PARENT || msg.type == I_FIND_PARENT_RESPONSE ||
|
||||
msg.type == I_HEARTBEAT || msg.type == I_HEARTBEAT_RESPONSE ||
|
||||
msg.type == I_PING || msg.type == I_PONG ||
|
||||
msg.type == I_REGISTRATION_REQUEST )) {
|
||||
SIGN_DEBUG(PSTR("Skipping security for command %d type %d\n"), mGetCommand(msg), msg.type);
|
||||
return true;
|
||||
} else if (mGetCommand(msg) == C_STREAM &&
|
||||
@@ -106,6 +108,7 @@ static void prepareSigningPresentation(MyMessage &msg, uint8_t destination) {
|
||||
// Only supports version 1 for now
|
||||
build(msg, _nc.nodeId, destination, NODE_SENSOR_ID, C_INTERNAL, I_SIGNING_PRESENTATION, false).set("");
|
||||
mSetLength(msg, 2);
|
||||
mSetPayloadType(msg, P_CUSTOM); // displayed as hex
|
||||
msg.data[0] = SIGNING_PRESENTATION_VERSION_1;
|
||||
msg.data[1] = 0;
|
||||
}
|
||||
@@ -129,8 +132,8 @@ void signerInit(void) {
|
||||
#endif
|
||||
}
|
||||
|
||||
void signerPresentation(MyMessage &msg) {
|
||||
prepareSigningPresentation(msg, GATEWAY_ADDRESS);
|
||||
void signerPresentation(MyMessage &msg, uint8_t destination) {
|
||||
prepareSigningPresentation(msg, destination);
|
||||
|
||||
#if defined(MY_SIGNING_REQUEST_SIGNATURES)
|
||||
msg.data[1] |= SIGNING_PRESENTATION_REQUIRE_SIGNATURES;
|
||||
@@ -141,12 +144,16 @@ void signerPresentation(MyMessage &msg) {
|
||||
SIGN_DEBUG(PSTR("Whitelisting required\n"));
|
||||
#endif
|
||||
|
||||
_sendRoute(msg);
|
||||
if (!_sendRoute(msg)) {
|
||||
SIGN_DEBUG(PSTR("Failed to transmit signing presentation!"));
|
||||
}
|
||||
|
||||
#if defined(MY_SIGNING_FEATURE)
|
||||
// If we do support signing, wait for the gateway to tell us how it prefer us to transmit our messages
|
||||
SIGN_DEBUG(PSTR("Waiting for GW to send signing preferences...\n"));
|
||||
wait(2000);
|
||||
if (destination == GATEWAY_ADDRESS) {
|
||||
SIGN_DEBUG(PSTR("Waiting for GW to send signing preferences...\n"));
|
||||
wait(2000, C_INTERNAL, I_SIGNING_PRESENTATION);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -161,7 +168,9 @@ bool signerProcessInternal(MyMessage &msg) {
|
||||
prepareSigningPresentation(msg, sender);
|
||||
SIGN_DEBUG(PSTR("Informing node %d that we do not require signatures because we do not support it\n"),
|
||||
sender);
|
||||
_sendRoute(msg);
|
||||
if (!_sendRoute(msg)) {
|
||||
SIGN_DEBUG(PSTR("Failed to transmit signing presentation!"));
|
||||
}
|
||||
return true; // No need to further process I_SIGNING_PRESENTATION in this case
|
||||
}
|
||||
#elif defined(MY_SIGNING_FEATURE)
|
||||
@@ -179,9 +188,12 @@ bool signerProcessInternal(MyMessage &msg) {
|
||||
#if defined(MY_SIGNING_ATSHA204)
|
||||
if (signerAtsha204GetNonce(msg)) {
|
||||
#endif
|
||||
SIGN_DEBUG(PSTR("Transmittng nonce\n"));
|
||||
_sendRoute(build(msg, _nc.nodeId, msg.sender, NODE_SENSOR_ID,
|
||||
C_INTERNAL, I_NONCE_RESPONSE, false));
|
||||
if (!_sendRoute(build(msg, _nc.nodeId, msg.sender, NODE_SENSOR_ID,
|
||||
C_INTERNAL, I_NONCE_RESPONSE, false))) {
|
||||
SIGN_DEBUG(PSTR("Failed to transmit nonce!\n"));
|
||||
} else {
|
||||
SIGN_DEBUG(PSTR("Transmitted nonce\n"));
|
||||
}
|
||||
} else {
|
||||
SIGN_DEBUG(PSTR("Failed to generate nonce!\n"));
|
||||
}
|
||||
@@ -245,7 +257,9 @@ bool signerProcessInternal(MyMessage &msg) {
|
||||
} else {
|
||||
SIGN_DEBUG(PSTR("Informing node %d that we do not require whitelisting\n"), sender);
|
||||
}
|
||||
_sendRoute(msg);
|
||||
if (!_sendRoute(msg)) {
|
||||
SIGN_DEBUG(PSTR("Failed to transmit signing presentation!"));
|
||||
}
|
||||
#endif // MY_GATEWAY_FEATURE
|
||||
return true; // No need to further process I_SIGNING_PRESENTATION
|
||||
} else if (msg.type == I_NONCE_RESPONSE) {
|
||||
@@ -301,7 +315,7 @@ bool signerSignMsg(MyMessage &msg) {
|
||||
} else {
|
||||
// Send nonce-request
|
||||
_signingNonceStatus=SIGN_WAITING_FOR_NONCE;
|
||||
if (!_sendRoute(build(_msgTmp, _nc.nodeId, msg.destination, msg.sensor,
|
||||
if (!_sendRoute(build(_msgSign, _nc.nodeId, msg.destination, msg.sensor,
|
||||
C_INTERNAL, I_NONCE_REQUEST, false).set(""))) {
|
||||
SIGN_DEBUG(PSTR("Failed to transmit nonce request!\n"));
|
||||
return false;
|
||||
@@ -405,3 +419,32 @@ uint8_t* signerSha256Final(void) {
|
||||
memcpy(sha256_hash, _soft_sha256.result(), 32);
|
||||
return sha256_hash;
|
||||
}
|
||||
|
||||
int signerMemcmp(const void* a, const void* b, size_t sz) {
|
||||
int retVal;
|
||||
size_t i;
|
||||
int done = 0;
|
||||
const uint8_t* ptrA = (const uint8_t*)a;
|
||||
const uint8_t* ptrB = (const uint8_t*)b;
|
||||
for (i=0; i < sz; i++) {
|
||||
if (ptrA[i] == ptrB[i]) {
|
||||
if (done > 0) {
|
||||
done = 1;
|
||||
} else {
|
||||
done = 0;
|
||||
}
|
||||
} else {
|
||||
if (done > 0) {
|
||||
done = 2;
|
||||
} else {
|
||||
done = 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (done > 0) {
|
||||
retVal = -1;
|
||||
} else {
|
||||
retVal = 0;
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
@@ -146,10 +146,10 @@
|
||||
* @code{.cpp}
|
||||
* //#define MY_SIGNING_SOFT
|
||||
* #define MY_SIGNING_ATSHA204
|
||||
* #include <MySensor.h>
|
||||
* #include <MySensors.h>
|
||||
* ...
|
||||
* @endcode
|
||||
* Make sure to set the define before the inclusion of MySensor.h.
|
||||
* Make sure to set the define before the inclusion of MySensors.h.
|
||||
* It is legal to mix hardware- and software-based backends in a network. They work together.
|
||||
*
|
||||
* You also need to decide if the node (or gateway) in question require and verify signatures in addition to calculating them.
|
||||
@@ -157,6 +157,8 @@
|
||||
* Just set the flag @ref MY_SIGNING_REQUEST_SIGNATURES and the node will inform the gateway that it expects the gateway to sign all
|
||||
* messages sent to the node. If this is set in a gateway, it will @b NOT force all nodes to sign messages to it. It will only require
|
||||
* signatures from nodes that in turn require signatures.<br>
|
||||
* If you want to have two nodes communicate securely directly with each other, the nodes that require signatures must send a presentation
|
||||
* message to all nodes it expect signed messages from (only the gateway is informed automatically). See @ref signerPresentation().<br>
|
||||
* A node can have three "states" with respect to signing:
|
||||
* 1. Node does not support signing in any way (neither @ref MY_SIGNING_ATSHA204 nor @ref MY_SIGNING_SOFT is set)
|
||||
* 2. Node does support signing but don't require messages sent to it to be signed (@ref MY_SIGNING_REQUEST_SIGNATURES is not set)
|
||||
@@ -170,7 +172,7 @@
|
||||
* #define MY_SIGNING_ATSHA204
|
||||
* #define MY_SIGNING_ATSHA204_PIN 4
|
||||
* #define MY_SIGNING_REQUEST_SIGNATURES
|
||||
* #include <MySensor.h>
|
||||
* #include <MySensors.h>
|
||||
* ...
|
||||
* @endcode
|
||||
* For the software backed signingbackend, an unconnected analog pin is required to set a random seed for the pseudo-random generator.
|
||||
@@ -178,18 +180,18 @@
|
||||
* signatures. The setting is defined using @ref MY_SIGNING_SOFT_RANDOMSEED_PIN and the default is to use pin A7. The same configuration
|
||||
* possibilities exist as with the other configuration options.
|
||||
*
|
||||
* <b>Thirdly</b>, if you use the software backend, you need to personalize the node (see @ref perzonalization).
|
||||
* <b>Thirdly</b>, if you use the software backend, you need to personalize the node (see @ref personalization).
|
||||
* @code{.cpp}
|
||||
* #define MY_SIGNING_SOFT
|
||||
* #define MY_SIGNING_SOFT_RANDOMSEED_PIN 7
|
||||
* #define MY_SIGNING_REQUEST_SIGNATURES
|
||||
* #include <MySensor.h>
|
||||
* #include <MySensors.h>
|
||||
* ...
|
||||
* @endcode
|
||||
*
|
||||
* An example of a node that require signatures is available in @ref SecureActuator.ino.
|
||||
*
|
||||
* @anchor perzonalization If you use the “real” ATSHA204A, before any signing operations can be done, the device needs to be <i>personalized</i>.
|
||||
* @anchor personalization If you use the “real” ATSHA204A, before any signing operations can be done, the device needs to be <i>personalized</i>.
|
||||
* This can be a daunting process as it involves irreversibly writing configurations to the device, which cannot be undone. I have however tried
|
||||
* to simplify the process as much as possibly by creating a helper-sketch specifically for this purpose in @ref SecurityPersonalizer.ino
|
||||
* Note that you also need to do personalization for software signing, but then the values are stored in EEPROM (described below).
|
||||
@@ -324,7 +326,7 @@
|
||||
* #define MY_SIGNING_ATSHA204
|
||||
* #define MY_SIGNING_REQUEST_SIGNATURES
|
||||
* #define MY_SIGNING_NODE_WHITELISTING {{.nodeId = GATEWAY_ADDRESS,.serial = {0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01}},{.nodeId = 2,.serial = {0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09}}}
|
||||
* #include <MySensor.h>
|
||||
* #include <MySensors.h>
|
||||
* ...
|
||||
* @endcode
|
||||
* In this example, there are two nodes in the whitelist; the gateway, and a separate node that communicates directly with this node (with signed
|
||||
@@ -336,7 +338,7 @@
|
||||
* #define MY_SIGNING_SOFT_RANDOMSEED_PIN 7
|
||||
* #define MY_SIGNING_REQUEST_SIGNATURES
|
||||
* #define MY_SIGNING_NODE_WHITELISTING {{.nodeId = GATEWAY_ADDRESS,.serial = {0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01}},{.nodeId = 2,.serial = {0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09}}}
|
||||
* #include <MySensor.h>
|
||||
* #include <MySensors.h>
|
||||
* ...
|
||||
* @endcode
|
||||
*
|
||||
@@ -403,7 +405,7 @@
|
||||
* @code{.cpp}
|
||||
* #define MY_SIGNING_ATSHA204
|
||||
* #define MY_SIGNING_REQUEST_SIGNATURES
|
||||
* #include <MySensor.h>
|
||||
* #include <MySensors.h>
|
||||
* ...
|
||||
* @endcode
|
||||
* If you do also want your gateway to require signatures from your lock you just enable the same (or similar if using software signing) settings
|
||||
@@ -423,7 +425,7 @@
|
||||
* #define MY_SIGNING_SOFT
|
||||
* #define MY_SIGNING_SOFT_RANDOMSEED_PIN 7
|
||||
* #define MY_SIGNING_REQUEST_SIGNATURES
|
||||
* #include <MySensor.h>
|
||||
* #include <MySensors.h>
|
||||
* ...
|
||||
* @endcode
|
||||
*
|
||||
@@ -433,7 +435,7 @@
|
||||
* #define MY_SIGNING_SOFT_RANDOMSEED_PIN 7
|
||||
* #define MY_SIGNING_REQUEST_SIGNATURES
|
||||
* #define MY_SIGNING_NODE_WHITELISTING {{.nodeId = MOTION_SENSOR_ID,.serial = {0x12,0x34,0x56,0x78,0x90,0x12,0x34,0x56,0x78}}}
|
||||
* #include <MySensor.h>
|
||||
* #include <MySensors.h>
|
||||
* ...
|
||||
* @endcode
|
||||
|
||||
@@ -445,7 +447,7 @@
|
||||
* Configuration example for the keyfob (keyfob will only transmit to another node and not receive anything):<br>
|
||||
* @code{.cpp}
|
||||
* #define MY_SIGNING_ATSHA204
|
||||
* #include <MySensor.h>
|
||||
* #include <MySensors.h>
|
||||
* ...
|
||||
* @endcode
|
||||
*
|
||||
@@ -455,7 +457,7 @@
|
||||
* #define MY_SIGNING_SOFT_RANDOMSEED_PIN 7
|
||||
* #define MY_SIGNING_REQUEST_SIGNATURES
|
||||
* #define MY_SIGNING_NODE_WHITELISTING {{.nodeId = GATEWAY_ADDRESS,.serial = {0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88}},{.nodeId = KEYFOB_ID,.serial = {<FROM ATSHA ON KEYFOB>}}}
|
||||
* #include <MySensor.h>
|
||||
* #include <MySensors.h>
|
||||
* ...
|
||||
* @endcode
|
||||
*
|
||||
@@ -473,7 +475,7 @@
|
||||
#ifndef MySigning_h
|
||||
#define MySigning_h
|
||||
|
||||
#include "MySensorCore.h"
|
||||
#include "MySensorsCore.h"
|
||||
#include "drivers/ATSHA204/ATSHA204.h"
|
||||
|
||||
#ifdef MY_SIGNING_NODE_WHITELISTING
|
||||
@@ -506,14 +508,19 @@ void signerInit(void);
|
||||
/**
|
||||
* @brief Does signing specific presentation for a node.
|
||||
*
|
||||
* This function makes sure any signing related presentation info is shared with the gateway.
|
||||
* This function makes sure any signing related presentation info is shared with the other part.
|
||||
* The presentation of the gateways signing preferences is done in @ref signerProcessInternal().
|
||||
* \n@b Usage: This function should be called by the presentation routine of the mysensors library.
|
||||
* There is no need to call this directly from a sketch.
|
||||
* You only need to call this directly from a sketch to set up a node to node signed message exchange.
|
||||
* If you do call this directly from a sketch, and you at some point change your sketch to go from
|
||||
* requireing signing to not requireing signatures, you need to present this change to the node at least
|
||||
* once, so it can update its requirements tables accordingly. Or it will keep believing that this node
|
||||
* require signatures and attempt to send signed messages to it.
|
||||
*
|
||||
* @param msg Message buffer to use.
|
||||
* @param destination Node ID of the destination.
|
||||
*/
|
||||
void signerPresentation(MyMessage &msg);
|
||||
void signerPresentation(MyMessage &msg, uint8_t destination);
|
||||
|
||||
/**
|
||||
* @brief Manages internal signing message handshaking.
|
||||
@@ -615,5 +622,94 @@ void signerSha256Update(const uint8_t* data, size_t sz);
|
||||
*/
|
||||
uint8_t* signerSha256Final(void);
|
||||
|
||||
/**
|
||||
* @brief Do a timing neutral memory comparison.
|
||||
*
|
||||
* The function behaves similar to memcmp with the difference that it will
|
||||
* always use the same number of instructions for a given number of bytes,
|
||||
* no matter how the two buffers differ and the response is either 0 or -1.
|
||||
*
|
||||
* @param a First buffer for comparison.
|
||||
* @param b Second buffer for comparison.
|
||||
* @param sz The number of bytes to compare.
|
||||
* @returns 0 if buffers match, -1 if they do not.
|
||||
*/
|
||||
int signerMemcmp(const void* a, const void* b, size_t sz);
|
||||
|
||||
#endif
|
||||
/** @}*/
|
||||
/** @}*/
|
||||
/**
|
||||
* @defgroup MySigningTroubleshootinggrp Troubleshooting
|
||||
* @ingroup MySigninggrp
|
||||
* @{
|
||||
*
|
||||
* @section MySigningTroubleshootingSymptoms Symptoms and solutions
|
||||
*
|
||||
* The first thing to do if you suspect signing is causing problems, is to enable the verbose debug
|
||||
* flag for the signing backend. @see MY_DEBUG_VERBOSE_SIGNING
|
||||
*
|
||||
* If you are having trouble getting signing to work, please see the following troubleshooting tips.
|
||||
*
|
||||
* @subsection MySigningTroubleshootingSymptomStFail Signing fails and logs show st=fail on transmissions
|
||||
*
|
||||
* This is actually not a signing problem, although ofthen st=fail becomes st=ok when signing is disabled.
|
||||
* This is by far the most commonly reported problem with signing, but the problems is not with signing,
|
||||
* it is with radio performence.<br>
|
||||
* This is a typical log which might look like a signing related issue but isn't:
|
||||
* @code{.unparsed}
|
||||
* 0;255;3;0;9;Skipping security for command 3 type 16
|
||||
* 0;255;3;0;9;read: 3-3-0 s=255,c=3,t=16,pt=0,l=0,sg=0:
|
||||
* 0;255;3;0;9;Signing backend: ATSHA204Soft
|
||||
* 0;255;3;0;9;SHA256: 86DEAE1DAF50D577A4E2262B33ABF9DEE05DD8FAF84F94F50900000000000000
|
||||
* 0;255;3;0;9;Skipping security for command 3 type 17
|
||||
* 0;255;3;0;9;send: 0-0-3-3 s=255,c=3,t=17,pt=6,l=25,sg=0,st=fail:86DEAE1DAF50D577A4E2262B33ABF9DEE05DD8FAF84F94F509
|
||||
* 0;255;3;0;9;Failed to transmit nonce!
|
||||
* 0;255;3;0;9;Message is not signed, but it should have been!
|
||||
* 0;255;3;0;9;verify fail
|
||||
* @endcode
|
||||
*
|
||||
* The reason for this is that when signing is used, the messages transmitted become relatively large.<br>
|
||||
* Because of this, the message is more sensitive to noise, and the chance for a message to get scrambled
|
||||
* increase with the message size. Please refer to the troubleshooting section at the MySensors forum for
|
||||
* information on how to improve radio performence.<br>
|
||||
* This is a good place to start: https://forum.mysensors.org/topic/666/debug-faq-and-how-ask-for-help
|
||||
*
|
||||
* @subsection MySigningTroubleshootingSymptomNonce Failed to generate nonce
|
||||
*
|
||||
* The signing backend failed to generate the nonce needed to sign a message. This indicate a hardware
|
||||
* problem. Please post the debug info on the forum together with a description of your hardware setup.
|
||||
*
|
||||
* @subsection MySigningTroubleshootingSymptomSign Failed to sign message
|
||||
*
|
||||
* The signing backend failed to sign the message. Typically this happens if your message is so large,
|
||||
* that there is no room left in the buffer to store a signature.
|
||||
*
|
||||
* @subsection MySigningTroubleshootingSymptomWrongSource Nonce did not come from the destination (XX) of the message to be signed! It came from YY
|
||||
*
|
||||
* This should not really happen. The reason for this message is that the signing backend is only capable
|
||||
* of handling one signed message session at any time. If for some reason multiple nodes send a nonce message to
|
||||
* the same node, only the nonce from a node that is the destination of the current message signing session will be
|
||||
* accepted. Any other nonces will be dropped. This should not happen as no node should send a nonce unless asked to,
|
||||
* and a node will only ask for a nonce to one destination for every signing session.<br>
|
||||
* If you see this message, please post the debugging details on the MySensors forum so it can be investigated further
|
||||
* together with a description of your setup.
|
||||
*
|
||||
* @subsection MySigningTroubleshootingSymptomNotSigned Message is not signed, but it should have been
|
||||
*
|
||||
* A node has failed to comply with the signing preferences of this node. Check that the node has received a
|
||||
* signing presentation message from this node. This is automatically transmitted to gateways. For other nodes,
|
||||
* you need to transmit the presentation from the sketch. @see signerPresentation
|
||||
*
|
||||
* @subsection MySigningTroubleshootingSymptomNotSignedGeneral "Messages do not appear to be signed but I think they should be..."
|
||||
*
|
||||
* Make sure you have enabled the flag to require signatures to require signatures and have enabled one of the signing
|
||||
* backends. @see MY_SIGNING_REQUEST_SIGNATURES @see MY_SIGNING_ATSHA204 @see MY_SIGNING_SOFT
|
||||
*
|
||||
* @subsection MySigningTroubleshootingSymptomNotWorkingWhitelisting Signature verification failed!
|
||||
*
|
||||
* Make sure both source and destination of the signed message has undergone @ref personalization with the same HMAC key.<br>
|
||||
* Also, if whitelisting is used, make sure the proper serial is paired with the proper node ID at the destination.
|
||||
* Whitelisting preferences are communicated with the signing presentation (done automatically from nodes to gateway but
|
||||
* has to be explicitly done by sketch for node to node communication). @see signerPresentation
|
||||
*/
|
||||
/** @}*/
|
||||
@@ -31,20 +31,20 @@
|
||||
|
||||
// Define MY_DEBUG_VERBOSE_SIGNING in your sketch to enable signing backend debugprints
|
||||
|
||||
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_verifying_nonce[NONCE_NUMIN_SIZE_PASSTHROUGH+SHA204_SERIAL_SZ+1];
|
||||
uint8_t _signing_signing_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];
|
||||
uint8_t _signing_rx_buffer[SHA204_RSP_SIZE_MAX];
|
||||
uint8_t _signing_tx_buffer[SHA204_CMD_SIZE_MAX];
|
||||
extern uint8_t _doWhitelist[32];
|
||||
|
||||
#ifdef MY_SIGNING_NODE_WHITELISTING
|
||||
const whitelist_entry_t _signing_whitelist[] = MY_SIGNING_NODE_WHITELISTING;
|
||||
#endif
|
||||
|
||||
static void signerCalculateSignature(MyMessage &msg);
|
||||
static void signerCalculateSignature(MyMessage &msg, bool signing);
|
||||
static uint8_t* signerSha256(const uint8_t* data, size_t sz);
|
||||
|
||||
|
||||
@@ -87,14 +87,16 @@ static void DEBUG_SIGNING_PRINTBUF(const __FlashStringHelper* str, uint8_t* buf,
|
||||
#endif
|
||||
|
||||
void signerAtsha204Init(void) {
|
||||
atsha204_init(MY_SIGNING_ATSHA204_PIN);
|
||||
}
|
||||
|
||||
bool signerAtsha204CheckTimer(void) {
|
||||
if (_signing_verification_ongoing) {
|
||||
if (millis() < _signing_timestamp || millis() > _signing_timestamp + MY_VERIFICATION_TIMEOUT_MS) {
|
||||
if (hwMillis() < _signing_timestamp || hwMillis() > _signing_timestamp + MY_VERIFICATION_TIMEOUT_MS) {
|
||||
DEBUG_SIGNING_PRINTBUF(F("Verification timeout"), NULL, 0);
|
||||
// Purge nonce
|
||||
memset(_signing_current_nonce, 0x00, NONCE_NUMIN_SIZE_PASSTHROUGH);
|
||||
memset(_signing_signing_nonce, 0x00, NONCE_NUMIN_SIZE_PASSTHROUGH);
|
||||
memset(_signing_verifying_nonce, 0x00, NONCE_NUMIN_SIZE_PASSTHROUGH);
|
||||
_signing_verification_ongoing = false;
|
||||
return false;
|
||||
}
|
||||
@@ -105,38 +107,41 @@ bool signerAtsha204CheckTimer(void) {
|
||||
bool signerAtsha204GetNonce(MyMessage &msg) {
|
||||
DEBUG_SIGNING_PRINTBUF(F("Signing backend: ATSHA204"), NULL, 0);
|
||||
// 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
|
||||
// We used a basic whitening technique that XORs each byte in a 32byte random value with current hwMillis() counter
|
||||
// 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, _singning_tx_buffer, RANDOM_RSP_SIZE, _singning_rx_buffer) != SHA204_SUCCESS) {
|
||||
DEBUG_SIGNING_PRINTBUF(F("Failed to generate nonce"), NULL, 0);
|
||||
return false;
|
||||
}
|
||||
_signing_current_nonce[i] = _singning_rx_buffer[SHA204_BUFFER_POS_DATA];
|
||||
(void)atsha204_wakeup(_signing_temp_message);
|
||||
if (atsha204_execute(SHA204_RANDOM, RANDOM_SEED_UPDATE, 0, 0, NULL,
|
||||
RANDOM_COUNT, _signing_tx_buffer, RANDOM_RSP_SIZE, _signing_rx_buffer) != SHA204_SUCCESS) {
|
||||
DEBUG_SIGNING_PRINTBUF(F("Failed to generate nonce"), NULL, 0);
|
||||
return false;
|
||||
}
|
||||
memcpy(_signing_current_nonce, signerSha256(_signing_current_nonce, 32), MAX_PAYLOAD);
|
||||
for (int i = 0; i < 32; i++) {
|
||||
_signing_verifying_nonce[i] = _signing_rx_buffer[SHA204_BUFFER_POS_DATA+i] ^ (hwMillis()&0xFF);
|
||||
}
|
||||
memcpy(_signing_verifying_nonce, signerSha256(_signing_verifying_nonce, 32), MAX_PAYLOAD);
|
||||
|
||||
atsha204_idle(); // We just idle the chip now since we expect to use it soon when the signed message arrives
|
||||
|
||||
// We set the part of the 32-byte nonce that does not fit into a message to 0xAA
|
||||
memset(&_signing_current_nonce[MAX_PAYLOAD], 0xAA, sizeof(_signing_current_nonce)-MAX_PAYLOAD);
|
||||
memset(&_signing_verifying_nonce[MAX_PAYLOAD], 0xAA, sizeof(_signing_verifying_nonce)-MAX_PAYLOAD);
|
||||
|
||||
// Transfer the first part of the nonce to the message
|
||||
msg.set(_signing_current_nonce, MAX_PAYLOAD);
|
||||
msg.set(_signing_verifying_nonce, MAX_PAYLOAD);
|
||||
_signing_verification_ongoing = true;
|
||||
_signing_timestamp = millis(); // Set timestamp to determine when to purge nonce
|
||||
_signing_timestamp = hwMillis(); // 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 (_signing_timestamp + MY_VERIFICATION_TIMEOUT_MS < millis()) _signing_timestamp = 0;
|
||||
if (_signing_timestamp + MY_VERIFICATION_TIMEOUT_MS < hwMillis()) _signing_timestamp = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
void signerAtsha204PutNonce(MyMessage &msg) {
|
||||
DEBUG_SIGNING_PRINTBUF(F("Signing backend: ATSHA204"), NULL, 0);
|
||||
|
||||
memcpy(_signing_current_nonce, (uint8_t*)msg.getCustom(), MAX_PAYLOAD);
|
||||
memcpy(_signing_signing_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(&_signing_current_nonce[MAX_PAYLOAD], 0xAA, sizeof(_signing_current_nonce)-MAX_PAYLOAD);
|
||||
memset(&_signing_signing_nonce[MAX_PAYLOAD], 0xAA, sizeof(_signing_signing_nonce)-MAX_PAYLOAD);
|
||||
}
|
||||
|
||||
bool signerAtsha204SignMsg(MyMessage &msg) {
|
||||
@@ -148,22 +153,25 @@ bool signerAtsha204SignMsg(MyMessage &msg) {
|
||||
|
||||
// Calculate signature of message
|
||||
mSetSigned(msg, 1); // make sure signing flag is set before signature is calculated
|
||||
signerCalculateSignature(msg);
|
||||
signerCalculateSignature(msg, true);
|
||||
|
||||
if (DO_WHITELIST(msg.destination)) {
|
||||
// Salt the signature with the senders nodeId and the unique serial of the ATSHA device
|
||||
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
|
||||
memcpy(_signing_signing_nonce, &_signing_rx_buffer[SHA204_BUFFER_POS_DATA], 32); // We can reuse the nonce buffer now since it is no longer needed
|
||||
_signing_signing_nonce[32] = msg.sender;
|
||||
atsha204_getSerialNumber(&_signing_signing_nonce[33]);
|
||||
(void)signerSha256(_signing_signing_nonce, 32+1+SHA204_SERIAL_SZ); // we can 'void' sha256 because the hash is already put in the correct place
|
||||
DEBUG_SIGNING_PRINTBUF(F("Signature salted with serial"), NULL, 0);
|
||||
}
|
||||
|
||||
// Put device back to sleep
|
||||
atsha204_sleep();
|
||||
|
||||
// Overwrite the first byte in the signature with the signing identifier
|
||||
_singning_rx_buffer[SHA204_BUFFER_POS_DATA] = SIGNING_IDENTIFIER;
|
||||
_signing_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)], &_singning_rx_buffer[SHA204_BUFFER_POS_DATA], MAX_PAYLOAD-mGetLength(msg));
|
||||
memcpy(&msg.data[mGetLength(msg)], &_signing_rx_buffer[SHA204_BUFFER_POS_DATA], MAX_PAYLOAD-mGetLength(msg));
|
||||
DEBUG_SIGNING_PRINTBUF(F("Signature in message: "), (uint8_t*)&msg.data[mGetLength(msg)], MAX_PAYLOAD-mGetLength(msg));
|
||||
|
||||
return true;
|
||||
@@ -187,7 +195,7 @@ bool signerAtsha204VerifyMsg(MyMessage &msg) {
|
||||
}
|
||||
|
||||
DEBUG_SIGNING_PRINTBUF(F("Signature in message: "), (uint8_t*)&msg.data[mGetLength(msg)], MAX_PAYLOAD-mGetLength(msg));
|
||||
signerCalculateSignature(msg); // Get signature of message
|
||||
signerCalculateSignature(msg, false); // Get signature of message
|
||||
|
||||
#ifdef MY_SIGNING_NODE_WHITELISTING
|
||||
// Look up the senders nodeId in our whitelist and salt the signature with that data
|
||||
@@ -195,25 +203,30 @@ bool signerAtsha204VerifyMsg(MyMessage &msg) {
|
||||
for (j=0; j < NUM_OF(_signing_whitelist); j++) {
|
||||
if (_signing_whitelist[j].nodeId == msg.sender) {
|
||||
DEBUG_SIGNING_PRINTBUF(F("Sender found in whitelist"), NULL, 0);
|
||||
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
|
||||
memcpy(_signing_verifying_nonce, &_signing_rx_buffer[SHA204_BUFFER_POS_DATA], 32); // We can reuse the nonce buffer now since it is no longer needed
|
||||
_signing_verifying_nonce[32] = msg.sender;
|
||||
memcpy(&_signing_verifying_nonce[33], _signing_whitelist[j].serial, SHA204_SERIAL_SZ);
|
||||
(void)signerSha256(_signing_verifying_nonce, 32+1+SHA204_SERIAL_SZ); // we can 'void' sha256 because the hash is already put in the correct place
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (j == NUM_OF(_signing_whitelist)) {
|
||||
DEBUG_SIGNING_PRINTBUF(F("Sender not found in whitelist, message rejected!"), NULL, 0);
|
||||
// Put device back to sleep
|
||||
atsha204_sleep();
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Put device back to sleep
|
||||
atsha204_sleep();
|
||||
|
||||
// Overwrite the first byte in the signature with the signing identifier
|
||||
_singning_rx_buffer[SHA204_BUFFER_POS_DATA] = SIGNING_IDENTIFIER;
|
||||
_signing_rx_buffer[SHA204_BUFFER_POS_DATA] = SIGNING_IDENTIFIER;
|
||||
|
||||
// Compare the caluclated signature with the provided signature
|
||||
if (memcmp(&msg.data[mGetLength(msg)], &_singning_rx_buffer[SHA204_BUFFER_POS_DATA], MAX_PAYLOAD-mGetLength(msg))) {
|
||||
DEBUG_SIGNING_PRINTBUF(F("Signature bad: "), &_singning_rx_buffer[SHA204_BUFFER_POS_DATA], MAX_PAYLOAD-mGetLength(msg));
|
||||
if (signerMemcmp(&msg.data[mGetLength(msg)], &_signing_rx_buffer[SHA204_BUFFER_POS_DATA], MAX_PAYLOAD-mGetLength(msg))) {
|
||||
DEBUG_SIGNING_PRINTBUF(F("Signature bad: "), &_signing_rx_buffer[SHA204_BUFFER_POS_DATA], MAX_PAYLOAD-mGetLength(msg));
|
||||
#ifdef MY_SIGNING_NODE_WHITELISTING
|
||||
DEBUG_SIGNING_PRINTBUF(F("Is the sender whitelisted and serial correct?"), NULL, 0);
|
||||
#endif
|
||||
@@ -225,44 +238,43 @@ bool signerAtsha204VerifyMsg(MyMessage &msg) {
|
||||
}
|
||||
}
|
||||
|
||||
// Helper to calculate signature of msg (returned in _singning_rx_buffer[SHA204_BUFFER_POS_DATA])
|
||||
static void signerCalculateSignature(MyMessage &msg) {
|
||||
// Helper to calculate signature of msg (returned in _signing_rx_buffer[SHA204_BUFFER_POS_DATA])
|
||||
static void signerCalculateSignature(MyMessage &msg, bool signing) {
|
||||
(void)atsha204_wakeup(_signing_temp_message);
|
||||
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("Message to process: "), (uint8_t*)&msg.data[1-HEADER_SIZE], MAX_MESSAGE_LENGTH-1-(MAX_PAYLOAD-mGetLength(msg)));
|
||||
DEBUG_SIGNING_PRINTBUF(F("Current nonce: "), _signing_current_nonce, 32);
|
||||
(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);
|
||||
DEBUG_SIGNING_PRINTBUF(F("Current nonce: "), signing ? _signing_signing_nonce : _signing_verifying_nonce, 32);
|
||||
(void)atsha204_execute(SHA204_WRITE, SHA204_ZONE_DATA | SHA204_ZONE_COUNT_FLAG, 8 << 3, 32, _signing_temp_message,
|
||||
WRITE_COUNT_LONG, _signing_tx_buffer, WRITE_RSP_SIZE, _signing_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, _signing_current_nonce,
|
||||
NONCE_COUNT_LONG, _singning_tx_buffer, NONCE_RSP_SIZE_SHORT, _singning_rx_buffer);
|
||||
(void)atsha204_execute(SHA204_NONCE, NONCE_MODE_PASSTHROUGH, 0, NONCE_NUMIN_SIZE_PASSTHROUGH,
|
||||
signing ? _signing_signing_nonce : _signing_verifying_nonce,
|
||||
NONCE_COUNT_LONG, _signing_tx_buffer, NONCE_RSP_SIZE_SHORT, _signing_rx_buffer);
|
||||
|
||||
// Purge nonce when used
|
||||
memset(_signing_current_nonce, 0x00, NONCE_NUMIN_SIZE_PASSTHROUGH);
|
||||
memset(signing ? _signing_signing_nonce : _signing_verifying_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, _singning_tx_buffer, GENDIG_RSP_SIZE, _singning_rx_buffer);
|
||||
(void)atsha204_execute(SHA204_GENDIG, GENDIG_ZONE_DATA, 8, 0, NULL,
|
||||
GENDIG_COUNT_DATA, _signing_tx_buffer, GENDIG_RSP_SIZE, _signing_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, _singning_tx_buffer, HMAC_RSP_SIZE, _singning_rx_buffer);
|
||||
(void)atsha204_execute(SHA204_HMAC, HMAC_MODE_SOURCE_FLAG_MATCH, 0, 0, NULL,
|
||||
HMAC_COUNT, _signing_tx_buffer, HMAC_RSP_SIZE, _signing_rx_buffer);
|
||||
|
||||
// Put device back to sleep
|
||||
atsha204.sha204c_sleep();
|
||||
|
||||
DEBUG_SIGNING_PRINTBUF(F("HMAC: "), &_singning_rx_buffer[SHA204_BUFFER_POS_DATA], 32);
|
||||
DEBUG_SIGNING_PRINTBUF(F("HMAC: "), &_signing_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 _singning_rx_buffer[SHA204_BUFFER_POS_DATA])
|
||||
// The pointer to the hash is returned, but the hash is also stored in _signing_rx_buffer[SHA204_BUFFER_POS_DATA])
|
||||
static 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, _singning_tx_buffer, SHA_RSP_SIZE_SHORT, _singning_rx_buffer);
|
||||
(void)atsha204_execute(SHA204_SHA, SHA_INIT, 0, 0, NULL,
|
||||
SHA_COUNT_SHORT, _signing_tx_buffer, SHA_RSP_SIZE_SHORT, _signing_rx_buffer);
|
||||
|
||||
// Calculate a hash
|
||||
memset(_signing_temp_message, 0x00, SHA_MSG_SIZE);
|
||||
@@ -271,12 +283,9 @@ static uint8_t* signerSha256(const uint8_t* data, size_t sz) {
|
||||
// Write length data to the last bytes
|
||||
_signing_temp_message[SHA_MSG_SIZE-2] = (sz >> 5);
|
||||
_signing_temp_message[SHA_MSG_SIZE-1] = (sz << 3);
|
||||
(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);
|
||||
(void)atsha204_execute(SHA204_SHA, SHA_CALC, 0, SHA_MSG_SIZE, _signing_temp_message,
|
||||
SHA_COUNT_LONG, _signing_tx_buffer, SHA_RSP_SIZE_LONG, _signing_rx_buffer);
|
||||
|
||||
// Put device back to sleep
|
||||
atsha204.sha204c_sleep();
|
||||
|
||||
DEBUG_SIGNING_PRINTBUF(F("SHA256: "), &_singning_rx_buffer[SHA204_BUFFER_POS_DATA], 32);
|
||||
return &_singning_rx_buffer[SHA204_BUFFER_POS_DATA];
|
||||
DEBUG_SIGNING_PRINTBUF(F("SHA256: "), &_signing_rx_buffer[SHA204_BUFFER_POS_DATA], 32);
|
||||
return &_signing_rx_buffer[SHA204_BUFFER_POS_DATA];
|
||||
}
|
||||
+24
-22
@@ -38,7 +38,8 @@
|
||||
Sha256Class _signing_sha256;
|
||||
unsigned long _signing_timestamp;
|
||||
bool _signing_verification_ongoing = false;
|
||||
uint8_t _signing_current_nonce[NONCE_NUMIN_SIZE_PASSTHROUGH];
|
||||
uint8_t _signing_verifying_nonce[32];
|
||||
uint8_t _signing_signing_nonce[32];
|
||||
uint8_t _signing_temp_message[32];
|
||||
static uint8_t _signing_hmac_key[32];
|
||||
uint8_t _signing_hmac[32];
|
||||
@@ -49,7 +50,7 @@ static uint8_t _signing_node_serial_info[9];
|
||||
const whitelist_entry_t _signing_whitelist[] = MY_SIGNING_NODE_WHITELISTING;
|
||||
#endif
|
||||
|
||||
static void signerCalculateSignature(MyMessage &msg);
|
||||
static void signerCalculateSignature(MyMessage &msg, bool signing);
|
||||
|
||||
#ifdef MY_DEBUG_VERBOSE_SIGNING
|
||||
static char i2h(uint8_t i)
|
||||
@@ -99,10 +100,11 @@ void signerAtsha204SoftInit(void) {
|
||||
|
||||
bool signerAtsha204SoftCheckTimer(void) {
|
||||
if (_signing_verification_ongoing) {
|
||||
if (millis() < _signing_timestamp || millis() > _signing_timestamp + MY_VERIFICATION_TIMEOUT_MS) {
|
||||
if (hwMillis() < _signing_timestamp || hwMillis() > _signing_timestamp + MY_VERIFICATION_TIMEOUT_MS) {
|
||||
DEBUG_SIGNING_PRINTBUF(F("Verification timeout"), NULL, 0);
|
||||
// Purge nonce
|
||||
memset(_signing_current_nonce, 0xAA, 32);
|
||||
memset(_signing_signing_nonce, 0xAA, 32);
|
||||
memset(_signing_verifying_nonce, 0xAA, 32);
|
||||
_signing_verification_ongoing = false;
|
||||
return false;
|
||||
}
|
||||
@@ -113,35 +115,35 @@ bool signerAtsha204SoftCheckTimer(void) {
|
||||
bool signerAtsha204SoftGetNonce(MyMessage &msg) {
|
||||
DEBUG_SIGNING_PRINTBUF(F("Signing backend: ATSHA204Soft"), NULL, 0);
|
||||
|
||||
// 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
|
||||
// We used a basic whitening technique that XORs a random byte with the current hwMillis() counter and then the byte is
|
||||
// hashed (SHA256) to produce the resulting nonce
|
||||
_signing_sha256.init();
|
||||
for (int i = 0; i < 32; i++) {
|
||||
_signing_sha256.write(random(256));
|
||||
_signing_sha256.write(random(256) ^ (hwMillis()&0xFF));
|
||||
}
|
||||
memcpy(_signing_current_nonce, _signing_sha256.result(), MAX_PAYLOAD);
|
||||
DEBUG_SIGNING_PRINTBUF(F("SHA256: "), _signing_current_nonce, 32);
|
||||
memcpy(_signing_verifying_nonce, _signing_sha256.result(), MAX_PAYLOAD);
|
||||
DEBUG_SIGNING_PRINTBUF(F("SHA256: "), _signing_verifying_nonce, 32);
|
||||
|
||||
// We set the part of the 32-byte nonce that does not fit into a message to 0xAA
|
||||
memset(&_signing_current_nonce[MAX_PAYLOAD], 0xAA, sizeof(_signing_current_nonce)-MAX_PAYLOAD);
|
||||
memset(&_signing_verifying_nonce[MAX_PAYLOAD], 0xAA, sizeof(_signing_verifying_nonce)-MAX_PAYLOAD);
|
||||
|
||||
// Transfer the first part of the nonce to the message
|
||||
msg.set(_signing_current_nonce, MAX_PAYLOAD);
|
||||
msg.set(_signing_verifying_nonce, MAX_PAYLOAD);
|
||||
_signing_verification_ongoing = true;
|
||||
_signing_timestamp = millis(); // Set timestamp to determine when to purge nonce
|
||||
_signing_timestamp = hwMillis(); // 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 (_signing_timestamp + MY_VERIFICATION_TIMEOUT_MS < millis()) _signing_timestamp = 0;
|
||||
if (_signing_timestamp + MY_VERIFICATION_TIMEOUT_MS < hwMillis()) _signing_timestamp = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
void signerAtsha204SoftPutNonce(MyMessage &msg) {
|
||||
DEBUG_SIGNING_PRINTBUF(F("Signing backend: ATSHA204Soft"), NULL, 0);
|
||||
|
||||
memcpy(_signing_current_nonce, (uint8_t*)msg.getCustom(), MAX_PAYLOAD);
|
||||
memcpy(_signing_signing_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(&_signing_current_nonce[MAX_PAYLOAD], 0xAA, sizeof(_signing_current_nonce)-MAX_PAYLOAD);
|
||||
memset(&_signing_signing_nonce[MAX_PAYLOAD], 0xAA, sizeof(_signing_signing_nonce)-MAX_PAYLOAD);
|
||||
}
|
||||
|
||||
bool signerAtsha204SoftSignMsg(MyMessage &msg) {
|
||||
@@ -153,7 +155,7 @@ bool signerAtsha204SoftSignMsg(MyMessage &msg) {
|
||||
|
||||
// Calculate signature of message
|
||||
mSetSigned(msg, 1); // make sure signing flag is set before signature is calculated
|
||||
signerCalculateSignature(msg);
|
||||
signerCalculateSignature(msg, true);
|
||||
|
||||
if (DO_WHITELIST(msg.destination)) {
|
||||
// Salt the signature with the senders nodeId and the (hopefully) unique serial The Creator has provided
|
||||
@@ -195,7 +197,7 @@ bool signerAtsha204SoftVerifyMsg(MyMessage &msg) {
|
||||
|
||||
// Get signature of message
|
||||
DEBUG_SIGNING_PRINTBUF(F("Signature in message: "), (uint8_t*)&msg.data[mGetLength(msg)], MAX_PAYLOAD-mGetLength(msg));
|
||||
signerCalculateSignature(msg);
|
||||
signerCalculateSignature(msg, false);
|
||||
|
||||
#ifdef MY_SIGNING_NODE_WHITELISTING
|
||||
// Look up the senders nodeId in our whitelist and salt the signature with that data
|
||||
@@ -222,7 +224,7 @@ bool signerAtsha204SoftVerifyMsg(MyMessage &msg) {
|
||||
_signing_hmac[0] = SIGNING_IDENTIFIER;
|
||||
|
||||
// Compare the caluclated signature with the provided signature
|
||||
if (memcmp(&msg.data[mGetLength(msg)], _signing_hmac, MAX_PAYLOAD-mGetLength(msg))) {
|
||||
if (signerMemcmp(&msg.data[mGetLength(msg)], _signing_hmac, MAX_PAYLOAD-mGetLength(msg))) {
|
||||
DEBUG_SIGNING_PRINTBUF(F("Signature bad: "), _signing_hmac, MAX_PAYLOAD-mGetLength(msg));
|
||||
#ifdef MY_SIGNING_NODE_WHITELISTING
|
||||
DEBUG_SIGNING_PRINTBUF(F("Is the sender whitelisted and serial correct?"), NULL, 0);
|
||||
@@ -236,11 +238,11 @@ bool signerAtsha204SoftVerifyMsg(MyMessage &msg) {
|
||||
}
|
||||
|
||||
// Helper to calculate signature of msg (returned in hmac)
|
||||
static void signerCalculateSignature(MyMessage &msg) {
|
||||
static void signerCalculateSignature(MyMessage &msg, bool signing) {
|
||||
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)));
|
||||
DEBUG_SIGNING_PRINTBUF(F("Message to process: "), (uint8_t*)&msg.data[1-HEADER_SIZE], MAX_MESSAGE_LENGTH-1-(MAX_PAYLOAD-mGetLength(msg)));
|
||||
DEBUG_SIGNING_PRINTBUF(F("Current nonce: "), _signing_current_nonce, 32);
|
||||
DEBUG_SIGNING_PRINTBUF(F("Current nonce: "), signing ? _signing_signing_nonce : _signing_verifying_nonce, 32);
|
||||
|
||||
// ATSHA204 calculates the HMAC with a PSK and a SHA256 digest of the following data:
|
||||
// 32 bytes zeroes
|
||||
@@ -275,9 +277,9 @@ static void signerCalculateSignature(MyMessage &msg) {
|
||||
_signing_sha256.write(0x01); // SN[0]
|
||||
_signing_sha256.write(0x23); // SN[1]
|
||||
for (int i=0; i<25; i++) _signing_sha256.write(0x00);
|
||||
for (int i=0; i<32; i++) _signing_sha256.write(_signing_current_nonce[i]);
|
||||
for (int i=0; i<32; i++) _signing_sha256.write(signing ? _signing_signing_nonce[i] : _signing_verifying_nonce[i]);
|
||||
// Purge nonce when used
|
||||
memset(_signing_current_nonce, 0xAA, 32);
|
||||
memset(signing ? _signing_signing_nonce : _signing_verifying_nonce, 0xAA, 32);
|
||||
memcpy(_signing_temp_message, _signing_sha256.result(), 32);
|
||||
|
||||
// Feed "message" to HMAC calculator
|
||||
@@ -0,0 +1,720 @@
|
||||
/*
|
||||
* 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 <henrik.ekblad@mysensors.org>
|
||||
* 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"
|
||||
|
||||
// SM: transitions and update states
|
||||
static transportState stInit = { stInitTransition, NULL };
|
||||
static transportState stParent = { stParentTransition, stParentUpdate };
|
||||
static transportState stID = { stIDTransition, stIDUpdate };
|
||||
static transportState stUplink = { stUplinkTransition, NULL };
|
||||
static transportState stReady = { stReadyTransition, stReadyUpdate };
|
||||
static transportState stFailure = { stFailureTransition, stFailureUpdate };
|
||||
|
||||
// transport SM variables
|
||||
static transportSM _transportSM;
|
||||
|
||||
// stInit: initialize transport HW
|
||||
void stInitTransition() {
|
||||
TRANSPORT_DEBUG(PSTR("TSM:INIT\n"));
|
||||
// initialize status variables
|
||||
_transportSM.pingActive = false;
|
||||
_transportSM.transportActive = false;
|
||||
#if defined(MY_TRANSPORT_SANITY_CHECK) || defined(MY_REPEATER_FEATURE)
|
||||
_transportSM.lastSanityCheck = hwMillis();
|
||||
#endif
|
||||
_transportSM.lastUplinkCheck = 0;
|
||||
// Read node settings (ID, parentId, GW distance) from EEPROM
|
||||
hwReadConfigBlock((void*)&_nc, (void*)EEPROM_NODE_ID_ADDRESS, sizeof(NodeConfig));
|
||||
|
||||
// initialize radio
|
||||
if (!transportInit()) {
|
||||
TRANSPORT_DEBUG(PSTR("!TSM:INIT:TSP FAIL\n"));
|
||||
setIndication(INDICATION_ERR_INIT_TRANSPORT);
|
||||
transportSwitchSM(stFailure);
|
||||
}
|
||||
else {
|
||||
TRANSPORT_DEBUG(PSTR("TSM:INIT:TSP OK\n"));
|
||||
_transportSM.transportActive = true;
|
||||
#if defined(MY_GATEWAY_FEATURE)
|
||||
// Set configuration for gateway
|
||||
TRANSPORT_DEBUG(PSTR("TSM:INIT:GW MODE\n"));
|
||||
_nc.parentNodeId = GATEWAY_ADDRESS;
|
||||
_nc.distance = 0;
|
||||
_nc.nodeId = GATEWAY_ADDRESS;
|
||||
transportSetAddress(GATEWAY_ADDRESS);
|
||||
// GW mode: skip FPAR,ID,UPL states
|
||||
transportSwitchSM(stReady);
|
||||
#else
|
||||
if ((uint8_t)MY_NODE_ID != AUTO) {
|
||||
TRANSPORT_DEBUG(PSTR("TSM:INIT:STATID,ID=%d\n"),MY_NODE_ID);
|
||||
// Set static id
|
||||
_nc.nodeId = MY_NODE_ID;
|
||||
// Save static id in eeprom
|
||||
hwWriteConfig(EEPROM_NODE_ID_ADDRESS, MY_NODE_ID);
|
||||
}
|
||||
// set ID if static or set in EEPROM
|
||||
if (_nc.nodeId == AUTO || transportAssignNodeID(_nc.nodeId)) {
|
||||
// if node ID > 0, proceed to next state
|
||||
transportSwitchSM(stParent);
|
||||
}
|
||||
else {
|
||||
// ID invalid (=0), nothing we can do
|
||||
transportSwitchSM(stFailure);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// stParent: find parent
|
||||
void stParentTransition() {
|
||||
TRANSPORT_DEBUG(PSTR("TSM:FPAR\n")); // find parent
|
||||
setIndication(INDICATION_FIND_PARENT);
|
||||
_transportSM.uplinkOk = false;
|
||||
_transportSM.preferredParentFound = false;
|
||||
#if defined(MY_PARENT_NODE_IS_STATIC)
|
||||
TRANSPORT_DEBUG(PSTR("TSM:FPAR:STATP=%d\n"),MY_PARENT_NODE_ID); // static parent
|
||||
_transportSM.findingParentNode = false;
|
||||
_nc.distance = 1; // assumption, CHKUPL:GWDC will update this variable
|
||||
_nc.parentNodeId = MY_PARENT_NODE_ID;
|
||||
// skipping find parent
|
||||
setIndication(INDICATION_GOT_PARENT);
|
||||
transportSwitchSM(stID);
|
||||
#else
|
||||
_transportSM.findingParentNode = true;
|
||||
_nc.distance = DISTANCE_INVALID; // Set distance to max and invalidate parent node ID
|
||||
_nc.parentNodeId = AUTO;
|
||||
// Broadcast find parent request
|
||||
transportRouteMessage(build(_msgTmp, _nc.nodeId, BROADCAST_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_FIND_PARENT, false).set(""));
|
||||
#endif
|
||||
}
|
||||
|
||||
// stParentUpdate
|
||||
void stParentUpdate() {
|
||||
#if !defined(MY_PARENT_NODE_IS_STATIC)
|
||||
if (transportTimeInState() > STATE_TIMEOUT || _transportSM.preferredParentFound) {
|
||||
// timeout or preferred parent found
|
||||
if (_nc.parentNodeId != AUTO) {
|
||||
// parent assigned
|
||||
TRANSPORT_DEBUG(PSTR("TSM:FPAR:OK\n")); // find parent ok
|
||||
_transportSM.findingParentNode = false;
|
||||
setIndication(INDICATION_GOT_PARENT);
|
||||
// go to next state
|
||||
transportSwitchSM(stID);
|
||||
}
|
||||
else if (transportTimeInState() > STATE_TIMEOUT) {
|
||||
// timeout w/o reply or valid parent
|
||||
if (_transportSM.retries < STATE_RETRIES) {
|
||||
// retries left
|
||||
TRANSPORT_DEBUG(PSTR("!TSM:FPAR:NO REPLY\n")); // find parent, no reply
|
||||
// reenter state
|
||||
transportSwitchSM(stParent);
|
||||
}
|
||||
else {
|
||||
// no retries left, finding parent failed
|
||||
TRANSPORT_DEBUG(PSTR("!TSM:FPAR:FAIL\n")); // find parent fail
|
||||
setIndication(INDICATION_ERR_FIND_PARENT);
|
||||
transportSwitchSM(stFailure);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// stID: verify and request ID if necessary
|
||||
void stIDTransition() {
|
||||
TRANSPORT_DEBUG(PSTR("TSM:ID\n")); // verify/request node ID
|
||||
if (_nc.nodeId == AUTO) {
|
||||
// send ID request
|
||||
setIndication(INDICATION_REQ_NODEID);
|
||||
TRANSPORT_DEBUG(PSTR("TSM:ID:REQ\n")); // request node ID
|
||||
transportRouteMessage(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_ID_REQUEST, false).set(""));
|
||||
}
|
||||
}
|
||||
|
||||
void stIDUpdate() {
|
||||
if (_nc.nodeId != AUTO) {
|
||||
// current node ID is valid
|
||||
TRANSPORT_DEBUG(PSTR("TSM:ID:OK,ID=%d\n"), _nc.nodeId);
|
||||
setIndication(INDICATION_GOT_NODEID);
|
||||
// proceed to next state
|
||||
transportSwitchSM(stUplink);
|
||||
}
|
||||
else if (transportTimeInState() > STATE_TIMEOUT) {
|
||||
// timeout
|
||||
if (_transportSM.retries < STATE_RETRIES) {
|
||||
// reenter
|
||||
transportSwitchSM(stID);
|
||||
}
|
||||
else {
|
||||
// no retries left
|
||||
TRANSPORT_DEBUG(PSTR("!TSM:ID:FAIL,ID=%d\n"), _nc.nodeId);
|
||||
setIndication(INDICATION_ERR_GET_NODEID);
|
||||
transportSwitchSM(stFailure);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void stUplinkTransition() {
|
||||
TRANSPORT_DEBUG(PSTR("TSM:UPL\n"));
|
||||
// check uplink
|
||||
if(transportCheckUplink(true)) {
|
||||
// uplink ok, i.e. GW replied
|
||||
TRANSPORT_DEBUG(PSTR("TSM:UPL:OK\n")); // uplink ok
|
||||
// proceed to next state
|
||||
transportSwitchSM(stReady);
|
||||
}
|
||||
else {
|
||||
// uplink failed, at this point, no retries or timeout
|
||||
TRANSPORT_DEBUG(PSTR("!TSM:UPL:FAIL\n")); // uplink failed
|
||||
// go back to stParent
|
||||
transportSwitchSM(stParent);
|
||||
}
|
||||
}
|
||||
|
||||
void stReadyTransition() {
|
||||
// transport is ready and fully operational
|
||||
TRANSPORT_DEBUG(PSTR("TSM:READY\n")); // transport is ready
|
||||
_transportSM.uplinkOk = true;
|
||||
_transportSM.failedUplinkTransmissions = 0; // reset counter
|
||||
}
|
||||
|
||||
// stReadyUpdate: monitors uplink failures
|
||||
void stReadyUpdate() {
|
||||
#if !defined(MY_GATEWAY_FEATURE)
|
||||
if (_transportSM.failedUplinkTransmissions > TRANSMISSION_FAILURES) {
|
||||
// too many uplink transmissions failed, find new parent (if non-static)
|
||||
#if !defined(MY_PARENT_NODE_IS_STATIC)
|
||||
TRANSPORT_DEBUG(PSTR("!TSM:READY:UPL FAIL,SNP\n")); // uplink failed, search new parent
|
||||
transportSwitchSM(stParent);
|
||||
#else
|
||||
TRANSPORT_DEBUG(PSTR("!TSM:READY:UPL FAIL,STATP\n")); // uplink failed, static parent
|
||||
// reset counter
|
||||
_transportSM.failedUplinkTransmissions = 0;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// stFailure: entered upon HW init failure or max retries exceeded
|
||||
void stFailureTransition() {
|
||||
TRANSPORT_DEBUG(PSTR("TSM:FAILURE\n"));
|
||||
_transportSM.uplinkOk = false; // uplink nok
|
||||
_transportSM.transportActive = false; // transport inactive
|
||||
setIndication(INDICATION_ERR_INIT_TRANSPORT);
|
||||
// power down transport, no need until re-init
|
||||
TRANSPORT_DEBUG(PSTR("TSM:FAILURE:PDT\n")); // power down transport
|
||||
transportPowerDown();
|
||||
}
|
||||
|
||||
void stFailureUpdate() {
|
||||
if (transportTimeInState()> TIMEOUT_FAILURE_STATE) {
|
||||
TRANSPORT_DEBUG(PSTR("TSM:FAILURE:RE-INIT\n")); // attempt to re-initialize transport
|
||||
transportSwitchSM(stInit);
|
||||
}
|
||||
}
|
||||
|
||||
void transportSwitchSM(transportState& newState) {
|
||||
if (_transportSM.currentState != &newState) {
|
||||
// state change, reset retry counter
|
||||
_transportSM.retries = 0;
|
||||
// change state
|
||||
_transportSM.currentState = &newState;
|
||||
}
|
||||
else {
|
||||
_transportSM.retries++; // increment retries
|
||||
}
|
||||
// Transition event
|
||||
if (_transportSM.currentState->Transition) _transportSM.currentState->Transition();
|
||||
// save time
|
||||
_transportSM.stateEnter = hwMillis();
|
||||
}
|
||||
|
||||
uint32_t transportTimeInState() {
|
||||
return hwMillis() - _transportSM.stateEnter;
|
||||
}
|
||||
|
||||
void transportUpdateSM(){
|
||||
if (_transportSM.currentState->Update) _transportSM.currentState->Update();
|
||||
}
|
||||
|
||||
bool isTransportReady() {
|
||||
return _transportSM.uplinkOk;
|
||||
}
|
||||
|
||||
bool isTransportSearchingParent() {
|
||||
return _transportSM.findingParentNode;
|
||||
}
|
||||
|
||||
void transportInitialize() {
|
||||
// intial state
|
||||
_transportSM.currentState = &stFailure;
|
||||
transportSwitchSM(stInit);
|
||||
}
|
||||
|
||||
// update TSM and process incoming messages
|
||||
void transportProcess() {
|
||||
// update state machine
|
||||
transportUpdateSM();
|
||||
// process transport FIFO
|
||||
transportProcessFIFO();
|
||||
}
|
||||
|
||||
|
||||
bool transportCheckUplink(bool force) {
|
||||
if (!force && (hwMillis() - _transportSM.lastUplinkCheck) < CHKUPL_INTERVAL) {
|
||||
TRANSPORT_DEBUG(PSTR("TSF:CHKUPL:OK,FCTRL\n")); // flood control
|
||||
return true;
|
||||
}
|
||||
// ping GW
|
||||
uint8_t hopsCount = transportPingNode(GATEWAY_ADDRESS);
|
||||
// verify hops
|
||||
if (hopsCount != INVALID_HOPS) {
|
||||
// update
|
||||
_transportSM.lastUplinkCheck = hwMillis();
|
||||
TRANSPORT_DEBUG(PSTR("TSF:CHKUPL:OK\n"));
|
||||
// did distance to GW change upstream, eg. re-routing of uplink nodes
|
||||
if (hopsCount != _nc.distance) {
|
||||
TRANSPORT_DEBUG(PSTR("TSF:CHKUPL:DGWC,O=%d,N=%d\n"), _nc.distance, hopsCount); // distance to GW changed
|
||||
_nc.distance = hopsCount;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
TRANSPORT_DEBUG(PSTR("TSF:CHKUPL:FAIL\n"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool transportAssignNodeID(uint8_t newNodeId) {
|
||||
// verify if ID valid
|
||||
if (newNodeId != GATEWAY_ADDRESS && newNodeId != AUTO) {
|
||||
_nc.nodeId = newNodeId;
|
||||
transportSetAddress(newNodeId);
|
||||
// Write ID to EEPROM
|
||||
hwWriteConfig(EEPROM_NODE_ID_ADDRESS, newNodeId);
|
||||
TRANSPORT_DEBUG(PSTR("TSF:ASID:OK,ID=%d\n"),newNodeId);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
TRANSPORT_DEBUG(PSTR("!TSF:ASID:FAIL,ID=%d\n"),newNodeId);
|
||||
setIndication(INDICATION_ERR_NET_FULL);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool transportRouteMessage(MyMessage &message) {
|
||||
uint8_t destination = message.destination;
|
||||
uint8_t route;
|
||||
|
||||
if (_transportSM.findingParentNode && destination != BROADCAST_ADDRESS) {
|
||||
TRANSPORT_DEBUG(PSTR("!TSF:ROUTE:FPAR ACTIVE\n")); // find parent active, message not sent
|
||||
// request to send a non-BC message while finding parent active, abort
|
||||
return false;
|
||||
}
|
||||
|
||||
if (destination == GATEWAY_ADDRESS) {
|
||||
route = _nc.parentNodeId; // message to GW always routes via parent
|
||||
}
|
||||
else if (destination == BROADCAST_ADDRESS) {
|
||||
route = BROADCAST_ADDRESS; // message to BC does not require routing
|
||||
}
|
||||
else {
|
||||
#if defined(MY_REPEATER_FEATURE)
|
||||
// destination not GW & not BC, get route
|
||||
route = hwReadConfig(EEPROM_ROUTES_ADDRESS + destination);
|
||||
if (route == AUTO) {
|
||||
// route unknown
|
||||
if (message.last != _nc.parentNodeId) {
|
||||
// message not from parent, i.e. child node - route it to parent
|
||||
TRANSPORT_DEBUG(PSTR("!TSF:ROUTE:%d UNKNOWN\n"), destination);
|
||||
route = _nc.parentNodeId;
|
||||
}
|
||||
else {
|
||||
// route unknown and msg received from parent, send it to destination assuming in rx radius
|
||||
route = destination;
|
||||
}
|
||||
}
|
||||
#else
|
||||
route = _nc.parentNodeId; // not a repeater, all traffic routed via parent
|
||||
#endif
|
||||
}
|
||||
// send message
|
||||
bool ok = transportSendWrite(route, message);
|
||||
#if !defined(MY_GATEWAY_FEATURE)
|
||||
// update counter
|
||||
if (route == _nc.parentNodeId) {
|
||||
if (!ok) {
|
||||
setIndication(INDICATION_ERR_TX);
|
||||
_transportSM.failedUplinkTransmissions++;
|
||||
}
|
||||
else _transportSM.failedUplinkTransmissions = 0;
|
||||
}
|
||||
#else
|
||||
if(!ok) setIndication(INDICATION_ERR_TX);
|
||||
#endif
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool transportSendRoute(MyMessage &message) {
|
||||
if (isTransportReady()) {
|
||||
return transportRouteMessage(message);
|
||||
}
|
||||
else {
|
||||
// TNR: transport not ready
|
||||
TRANSPORT_DEBUG(PSTR("!TSF:SEND:TNR\n"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// only be used inside transport
|
||||
bool transportWait(uint32_t ms, uint8_t cmd, uint8_t msgtype){
|
||||
uint32_t enter = hwMillis();
|
||||
// invalidate msg type
|
||||
_msg.type = !msgtype;
|
||||
bool expectedResponse = false;
|
||||
while ((hwMillis() - enter < ms) && !expectedResponse) {
|
||||
// process incoming messages
|
||||
transportProcessFIFO();
|
||||
#if defined(ARDUINO_ARCH_ESP8266)
|
||||
yield();
|
||||
#endif
|
||||
expectedResponse = (mGetCommand(_msg) == cmd && _msg.type == msgtype);
|
||||
}
|
||||
return expectedResponse;
|
||||
}
|
||||
|
||||
uint8_t transportPingNode(uint8_t targetId) {
|
||||
if(!_transportSM.pingActive){
|
||||
if(targetId == _nc.nodeId) {
|
||||
// ping to ourself, pingActive remains false
|
||||
return 0;
|
||||
}
|
||||
_transportSM.pingActive = true;
|
||||
_transportSM.pingResponse = INVALID_HOPS;
|
||||
TRANSPORT_DEBUG(PSTR("TSF:PING:SEND,TO=%d\n"), targetId);
|
||||
transportRouteMessage(build(_msgTmp, _nc.nodeId, targetId, NODE_SENSOR_ID, C_INTERNAL, I_PING, false).set((uint8_t)0x01));
|
||||
// Wait for ping reply or timeout
|
||||
transportWait(2000, C_INTERNAL, I_PONG);
|
||||
// make sure missing I_PONG msg does not block pinging function by leaving pignActive=true
|
||||
_transportSM.pingActive = false;
|
||||
return _transportSM.pingResponse;
|
||||
}
|
||||
else {
|
||||
return INVALID_HOPS;
|
||||
}
|
||||
}
|
||||
|
||||
void transportClearRoutingTable() {
|
||||
for (uint8_t i = 0; i != 255; i++) {
|
||||
hwWriteConfig(EEPROM_ROUTES_ADDRESS + i, BROADCAST_ADDRESS);
|
||||
}
|
||||
TRANSPORT_DEBUG(PSTR("TSF:CRT:OK\n")); // clear routing table
|
||||
}
|
||||
|
||||
uint32_t transportGetHeartbeat() {
|
||||
return transportTimeInState();
|
||||
}
|
||||
|
||||
void transportProcessMessage() {
|
||||
(void)signerCheckTimer(); // Manage signing timeout
|
||||
|
||||
uint8_t payloadLength = transportReceive((uint8_t *)&_msg);
|
||||
(void)payloadLength; // currently not used
|
||||
|
||||
setIndication(INDICATION_RX);
|
||||
|
||||
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;
|
||||
|
||||
TRANSPORT_DEBUG(PSTR("TSF:MSG:READ,%d-%d-%d,s=%d,c=%d,t=%d,pt=%d,l=%d,sg=%d:%s\n"),
|
||||
sender, last, destination, _msg.sensor, mGetCommand(_msg), type, mGetPayloadType(_msg), mGetLength(_msg), mGetSigned(_msg), _msg.getString(_convBuf));
|
||||
|
||||
// verify protocol version
|
||||
if(mGetVersion(_msg) != PROTOCOL_VERSION) {
|
||||
setIndication(INDICATION_ERR_VERSION);
|
||||
TRANSPORT_DEBUG(PSTR("!TSF:MSG:PVER,%d!=%d\n"), mGetVersion(_msg),PROTOCOL_VERSION); // protocol version mismatch
|
||||
return;
|
||||
}
|
||||
|
||||
// Reject messages that do not pass verification
|
||||
if (!signerVerifyMsg(_msg)) {
|
||||
setIndication(INDICATION_ERR_SIGN);
|
||||
TRANSPORT_DEBUG(PSTR("!TSF:MSG:SIGN VERIFY FAIL\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Is message addressed to this node?
|
||||
if (destination == _nc.nodeId) {
|
||||
// prevent buffer overflow by limiting max. possible message length (5 bits=31 bytes max) to MAX_PAYLOAD (25 bytes)
|
||||
mSetLength(_msg, min(mGetLength(_msg),MAX_PAYLOAD));
|
||||
// null terminate data
|
||||
_msg.data[mGetLength(_msg)] = 0x00;
|
||||
|
||||
// update routing table if msg not from parent
|
||||
#if defined(MY_REPEATER_FEATURE)
|
||||
if (last != _nc.parentNodeId) {
|
||||
// Message is from one of the child nodes. Add it to routing table.
|
||||
hwWriteConfig(EEPROM_ROUTES_ADDRESS+sender, last);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Check if sender requests an ack back.
|
||||
if (mGetRequestAck(_msg)) {
|
||||
_msgTmp = _msg; // Copy message
|
||||
mSetRequestAck(_msgTmp, false); // Reply without ack flag (otherwise we would end up in an eternal loop)
|
||||
mSetAck(_msgTmp, true); // set ACK flag
|
||||
_msgTmp.sender = _nc.nodeId;
|
||||
_msgTmp.destination = sender;
|
||||
// send ACK
|
||||
TRANSPORT_DEBUG(PSTR("TSF:MSG:ACK REQ\n")); // ACK requested
|
||||
// use transportSendRoute since ACK reply is not internal, i.e. if !transportOK do not reply
|
||||
transportSendRoute(_msgTmp);
|
||||
}
|
||||
if(!mGetAck(_msg)) {
|
||||
// only process if not ACK
|
||||
if (command == C_INTERNAL) {
|
||||
// Process signing related internal messages
|
||||
if (signerProcessInternal(_msg)) {
|
||||
return; // Signer processing indicated no further action needed
|
||||
}
|
||||
#if !defined(MY_GATEWAY_FEATURE)
|
||||
if (type == I_ID_RESPONSE) {
|
||||
#if (MY_NODE_ID == AUTO)
|
||||
// only active if node ID dynamic
|
||||
transportAssignNodeID(_msg.getByte());
|
||||
#endif
|
||||
return; // no further processing required
|
||||
}
|
||||
if (type == I_FIND_PARENT_RESPONSE) {
|
||||
#if !defined(MY_GATEWAY_FEATURE) && !defined(MY_PARENT_NODE_IS_STATIC)
|
||||
if (_transportSM.findingParentNode) { // only process if find parent active
|
||||
// Reply to a I_FIND_PARENT message. Check if the distance is shorter than we already have.
|
||||
uint8_t distance = _msg.getByte();
|
||||
TRANSPORT_DEBUG(PSTR("TSF:MSG:FPAR RES,ID=%d,D=%d\n"), sender, distance); // find parent response
|
||||
if (isValidDistance(distance)) {
|
||||
// Distance to gateway is one more for us w.r.t. parent
|
||||
distance++;
|
||||
// update settings if distance shorter or preferred parent found
|
||||
if (((isValidDistance(distance) && distance < _nc.distance) || (!_autoFindParent && sender == MY_PARENT_NODE_ID)) && !_transportSM.preferredParentFound) {
|
||||
// Found a neighbor closer to GW than previously found
|
||||
if (!_autoFindParent && sender == MY_PARENT_NODE_ID) {
|
||||
_transportSM.preferredParentFound = true;
|
||||
TRANSPORT_DEBUG(PSTR("TSF:MSG:FPAR PREF FOUND\n")); // find parent, preferred parent found
|
||||
}
|
||||
_nc.distance = distance;
|
||||
_nc.parentNodeId = sender;
|
||||
TRANSPORT_DEBUG(PSTR("TSF:MSG:FPAR OK,ID=%d,D=%d\n"), _nc.parentNodeId, _nc.distance);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
TRANSPORT_DEBUG(PSTR("TSF:MSG:FPAR INACTIVE\n")); // find parent response received, but inactive
|
||||
}
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
// general
|
||||
if (type == I_PING) {
|
||||
TRANSPORT_DEBUG(PSTR("TSF:MSG:PINGED,ID=%d,HP=%d\n"), sender, _msg.getByte()); // node pinged
|
||||
transportRouteMessage(build(_msgTmp, _nc.nodeId, sender, NODE_SENSOR_ID, C_INTERNAL, I_PONG, false).set((uint8_t)0x01));
|
||||
return; // no further processing required
|
||||
}
|
||||
if (type == I_PONG) {
|
||||
if (_transportSM.pingActive) {
|
||||
_transportSM.pingActive = false;
|
||||
_transportSM.pingResponse = _msg.getByte();
|
||||
TRANSPORT_DEBUG(PSTR("TSF:MSG:PONG RECV,HP=%d\n"), _transportSM.pingResponse); // pong received
|
||||
}
|
||||
return; // no further processing required
|
||||
}
|
||||
|
||||
if (_processInternalMessages()) {
|
||||
return; // no further processing required
|
||||
}
|
||||
} else if (command == C_STREAM) {
|
||||
#if defined(MY_OTA_FIRMWARE_FEATURE)
|
||||
if(firmwareOTAUpdateProcess()){
|
||||
return; // OTA FW update processing indicated no further action needed
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else {
|
||||
TRANSPORT_DEBUG(PSTR("TSF:MSG:ACK\n")); // received message is ACK, no internal processing, handover to msg callback
|
||||
}
|
||||
#if defined(MY_GATEWAY_FEATURE)
|
||||
// Hand over message to controller
|
||||
gatewayTransportSend(_msg);
|
||||
#endif
|
||||
// Call incoming message callback if available
|
||||
if (receive) {
|
||||
receive(_msg);
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if (destination == BROADCAST_ADDRESS) {
|
||||
TRANSPORT_DEBUG(PSTR("TSF:MSG:BC\n")); // broadcast msg
|
||||
if (command == C_INTERNAL) {
|
||||
if (isTransportReady()) {
|
||||
// only reply if node is fully operational
|
||||
if (type == I_FIND_PARENT) {
|
||||
#if defined(MY_REPEATER_FEATURE)
|
||||
if (sender != _nc.parentNodeId) { // no circular reference
|
||||
TRANSPORT_DEBUG(PSTR("TSF:MSG:FPAR REQ,ID=%d\n"), sender); // FPR: find parent request
|
||||
// node is in our range, update routing table - important if node has new repeater as parent
|
||||
hwWriteConfig(EEPROM_ROUTES_ADDRESS + sender, sender);
|
||||
// check if uplink functional - node can only be parent node if link to GW functional
|
||||
// this also prevents circular references in case GW ooo
|
||||
if(transportCheckUplink(false)){
|
||||
_transportSM.lastUplinkCheck = hwMillis();
|
||||
TRANSPORT_DEBUG(PSTR("TSF:MSG:GWL OK\n")); // GW uplink ok
|
||||
// delay minimizes collisions
|
||||
delay(hwMillis() & 0x3ff);
|
||||
transportRouteMessage(build(_msgTmp, _nc.nodeId, sender, NODE_SENSOR_ID, C_INTERNAL, I_FIND_PARENT_RESPONSE, false).set(_nc.distance));
|
||||
}
|
||||
else {
|
||||
TRANSPORT_DEBUG(PSTR("!TSF:MSG:GWL FAIL\n")); // GW uplink fail, do not respond to parent request
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
return; // no further processing required
|
||||
}
|
||||
}
|
||||
if (type == I_DISCOVER) {
|
||||
if (last == _nc.parentNodeId) {
|
||||
// random wait to minimize collisions
|
||||
delay(hwMillis() & 0x3ff);
|
||||
transportRouteMessage(build(_msgTmp, _nc.nodeId, sender, NODE_SENSOR_ID, C_INTERNAL, I_DISCOVER_RESPONSE, false).set(_nc.parentNodeId));
|
||||
// no return here (for fwd if repeater)
|
||||
}
|
||||
}
|
||||
}
|
||||
// controlled BC relay
|
||||
#if defined(MY_REPEATER_FEATURE)
|
||||
// controlled BC repeating: forward only if message received from parent and sender not self to prevent circular fwds
|
||||
if(last == _nc.parentNodeId && sender != _nc.nodeId && isTransportReady()){
|
||||
TRANSPORT_DEBUG(PSTR("TSF:MSG:FWD BC MSG\n")); // controlled broadcast msg forwarding
|
||||
transportRouteMessage(_msg);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Call incoming message callback if available, but only if message received from parent
|
||||
if (command != C_INTERNAL && last == _nc.parentNodeId && receive) {
|
||||
receive(_msg);
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
// msg not to us and not BC, relay msg
|
||||
#if defined(MY_REPEATER_FEATURE)
|
||||
if (isTransportReady()) {
|
||||
TRANSPORT_DEBUG(PSTR("TSF:MSG:REL MSG\n")); // relay msg
|
||||
// update routing table if message not received from parent
|
||||
if (last != _nc.parentNodeId) {
|
||||
hwWriteConfig(EEPROM_ROUTES_ADDRESS + sender, last);
|
||||
}
|
||||
if (command == C_INTERNAL) {
|
||||
if (type == I_PING || type == I_PONG) {
|
||||
uint8_t hopsCnt = _msg.getByte();
|
||||
if (hopsCnt != MAX_HOPS) {
|
||||
TRANSPORT_DEBUG(PSTR("TSF:MSG:REL PxNG,HP=%d\n"), hopsCnt);
|
||||
_msg.set((uint8_t)(hopsCnt + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
// Relay this message to another node
|
||||
transportRouteMessage(_msg);
|
||||
}
|
||||
#else
|
||||
TRANSPORT_DEBUG(PSTR("!TSF:MSG:REL MSG,NORP\n")); // message relaying request, but not a repeater
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void transportInvokeSanityCheck() {
|
||||
if (!transportSanityCheck()) {
|
||||
TRANSPORT_DEBUG(PSTR("!TSF:SANCHK:FAIL\n")); // sanity check fail
|
||||
transportSwitchSM(stFailure);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
TRANSPORT_DEBUG(PSTR("TSF:SANCHK:OK\n")); // sanity check ok
|
||||
}
|
||||
}
|
||||
|
||||
inline void transportProcessFIFO() {
|
||||
if (_transportSM.transportActive) {
|
||||
#if defined(MY_TRANSPORT_SANITY_CHECK) || defined(MY_REPEATER_FEATURE)
|
||||
if (hwMillis() - _transportSM.lastSanityCheck > MY_TRANSPORT_SANITY_CHECK_INTERVAL) {
|
||||
_transportSM.lastSanityCheck = hwMillis();
|
||||
transportInvokeSanityCheck();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
// transport not active, nothing to be done
|
||||
return;
|
||||
}
|
||||
uint8_t _processedMessages = MAX_SUBSEQ_MSGS;
|
||||
// process all msgs in FIFO or counter exit
|
||||
while (transportAvailable() && _processedMessages--) {
|
||||
transportProcessMessage();
|
||||
}
|
||||
#if defined(MY_OTA_FIRMWARE_FEATURE)
|
||||
if (isTransportReady()) {
|
||||
// only process if transport ok
|
||||
firmwareOTAUpdateRequest();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool transportSendWrite(uint8_t to, MyMessage &message) {
|
||||
// set protocol version and update last
|
||||
mSetVersion(message, PROTOCOL_VERSION);
|
||||
message.last = _nc.nodeId;
|
||||
|
||||
// sign message if required
|
||||
if (!signerSignMsg(message)) {
|
||||
TRANSPORT_DEBUG(PSTR("!TSF:MSG:SIGN FAIL\n"));
|
||||
setIndication(INDICATION_ERR_SIGN);
|
||||
return false;
|
||||
}
|
||||
|
||||
// msg length changes if signed
|
||||
uint8_t length = mGetSigned(message) ? MAX_MESSAGE_LENGTH : mGetLength(message);
|
||||
|
||||
// send
|
||||
setIndication(INDICATION_TX);
|
||||
bool ok = transportSend(to, &message, min(MAX_MESSAGE_LENGTH, HEADER_SIZE + length));
|
||||
|
||||
TRANSPORT_DEBUG(PSTR("%sTSF:MSG:SEND,%d-%d-%d-%d,s=%d,c=%d,t=%d,pt=%d,l=%d,sg=%d,ft=%d,st=%s:%s\n"),
|
||||
(ok || to == BROADCAST_ADDRESS ? "" : "!"),message.sender,message.last, to, message.destination, message.sensor, mGetCommand(message), message.type,
|
||||
mGetPayloadType(message), mGetLength(message), mGetSigned(message), _transportSM.failedUplinkTransmissions, to==BROADCAST_ADDRESS ? "bc" : (ok ? "OK":"NACK"), message.getString(_convBuf));
|
||||
|
||||
return (ok || to==BROADCAST_ADDRESS);
|
||||
}
|
||||
|
||||
// EOF MyTransport.cpp
|
||||
@@ -0,0 +1,391 @@
|
||||
/*
|
||||
* 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 <henrik.ekblad@mysensors.org>
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file MyTransport.h
|
||||
*
|
||||
* @defgroup MyTransportgrp MyTransport
|
||||
* @ingroup internals
|
||||
* @{
|
||||
*
|
||||
* Transport-related log messages, format: [!]SYSTEM:[SUB SYSTEM:]MESSAGE
|
||||
* - [!] Exclamation mark is prepended in case of error
|
||||
* - SYSTEM:
|
||||
* - <b>TSM</b>: messages emitted by the transport state machine
|
||||
* - <b>TSF</b>: messages emitted by transport support functions
|
||||
* - SUB SYSTEMS:
|
||||
* - Transport state machine (<b>TSM</b>)
|
||||
* - TSM:INIT from <b>stInit</b> Initialize transport and radio
|
||||
* - TSM:FPAR from <b>stParent</b> Find parent
|
||||
* - TSM:ID from <b>stID</b> Check/request node ID, if dynamic node ID set
|
||||
* - TSM:UPL from <b>stUplink</b> Verify uplink connection by pinging GW
|
||||
* - TSM:READY from <b>stReady</b> Transport is ready and fully operational
|
||||
* - TSM:FAILURE from <b>stFailure</b> Failure in transport link or transport HW
|
||||
* - Transport support function (<b>TSF</b>)
|
||||
* - TSF:CHKUPL from @ref transportCheckUplink(), checks connection to GW
|
||||
* - TSF:ASID from @ref transportAssignNodeID(), assigns node ID
|
||||
* - TSF:PING from @ref transportPingNode(), pings a node
|
||||
* - TSF:CRT from @ref transportClearRoutingTable(), clears routing table stored in EEPROM
|
||||
* - TSF:MSG from @ref transportProcessMessage(), processes incoming message
|
||||
* - TSF:SANCHK from @ref transportInvokeSanityCheck(), calls transport-specific sanity check
|
||||
* - TSF:ROUTE from @ref transportRouteMessage(), sends message
|
||||
* - TSF:SEND from @ref transportSendRoute(), sends message if transport is ready (exposed)
|
||||
*
|
||||
* Transport debug log messages:
|
||||
*
|
||||
* |E| SYS | SUB | Message | Comment
|
||||
* |-|------|-----------|-----------------------|---------------------------------------------------------------------
|
||||
* | | TSM | INIT | | <b>Transition to stInit state</b>
|
||||
* | | TSM | INIT | STATID,ID=%%d | Node ID is static
|
||||
* | | TSM | INIT | TSP OK | Transport device configured and fully operational
|
||||
* | | TSM | INIT | GW MODE | Node is set up as GW, thus omitting ID and findParent states
|
||||
* |!| TSM | INIT | TSP FAIL | Transport device initialization failed
|
||||
* | | TSM | FPAR | | <b>Transition to stParent state</b>
|
||||
* | | TSM | FPAR | STATP=%%d | Static parent set, skip finding parent
|
||||
* | | TSM | FPAR | OK | Parent node identified
|
||||
* |!| TSM | FPAR | NO REPLY | No potential parents replied to find parent request
|
||||
* |!| TSM | FPAR | FAIL | Finding parent failed
|
||||
* | | TSM | ID | | <b>Transition to stID state</b>
|
||||
* | | TSM | ID | OK,ID=%%d | Node ID is valid
|
||||
* | | TSM | ID | REQ | Request node ID from controller
|
||||
* |!| TSM | ID | FAIL,ID=%%d | ID verification failed, ID invalid
|
||||
* | | TSM | UPL | | <b>Transition to stUplink state</b>
|
||||
* | | TSM | UPL | OK | Uplink OK, GW returned ping
|
||||
* |!| TSM | UPL | FAIL | Uplink check failed, i.e. GW could not be pinged
|
||||
* | | TSM | READY | | <b>Transition to stReady</b>, i.e. transport is ready and fully operational
|
||||
* |!| TSM | READY | UPL FAIL,SNP | Too many failed uplink transmissions, search new parent
|
||||
* |!| TSM | READY | FAIL,STATP | Too many failed uplink transmissions, static parent enforced
|
||||
* | | TSM | FAILURE | | <b>Transition to stFailure state</b>
|
||||
* | | TSM | FAILURE | PDT | Power-down transport
|
||||
* | | TSM | FAILURE | RE-INIT | Attempt to re-initialize transport
|
||||
* | | TSF | CHKUPL | OK | Uplink OK
|
||||
* | | TSF | CHKUPL | OK,FCTRL | Uplink OK, flood control prevents pinging GW in too short intervals
|
||||
* | | TSF | CHKUPL | DGWC,O=%%d,N=%%d | Uplink check revealed changed network topology, old distance (O), new distance (N)
|
||||
* | | TSF | CHKUPL | FAIL | No reply received when checking uplink
|
||||
* | | TSF | ASID | OK,ID=%%d | Node ID assigned
|
||||
* |!| TSF | ASID | FAIL,ID=%%d | Assigned ID is invalid
|
||||
* | | TSF | PING | SEND,TO=%%d | Send ping to destination (TO)
|
||||
* | | TSF | MSG | ACK REQ | ACK message requested
|
||||
* | | TSF | MSG | ACK | ACK message, do not proceed but forward to callback
|
||||
* | | TSF | MSG | FPAR RES,ID=%%d,D=%%d | Response to find parent received from node (ID) with distance (D) to GW
|
||||
* | | TSF | MSG | FPAR PREF FOUND | Preferred parent found, i.e. parent defined via MY_PARENT_NODE_ID
|
||||
* | | TSF | MSG | FPAR OK,ID=%%d,D=%%d | Find parent response from node (ID) is valid, distance (D) to GW
|
||||
* | | TSF | MSG | FPAR INACTIVE | Find parent response received, but no find parent request active, skip response
|
||||
* | | TSF | MSG | FPAR REQ,ID=%%d | Find parent request from node (ID)
|
||||
* | | TSF | MSG | PINGED,ID=%%d,HP=%%d | Node pinged by node (ID) with (HP) hops
|
||||
* | | TSF | MSG | PONG RECV,HP=%%d | Pinged node replied with (HP) hops
|
||||
* | | TSF | MSG | BC | Broadcast message received
|
||||
* | | TSF | MSG | GWL OK | Link to GW ok
|
||||
* | | TSF | MSG | FWD BC MSG | Controlled broadcast message forwarding
|
||||
* | | TSF | MSG | REL MSG | Relay message
|
||||
* | | TSF | MSG | REL PxNG,HP=%%d | Relay PING/PONG message, increment hop counter (HP)
|
||||
* |!| TSF | MSG | PVER,%%d!=%%d | Message protocol version mismatch
|
||||
* |!| TSF | MSG | SIGN VERIFY FAIL | Signing verification failed
|
||||
* |!| TSF | MSG | REL MSG,NORP | Node received a message for relaying, but node is not a repeater, message skipped
|
||||
* |!| TSF | MSG | SIGN FAIL | Signing message failed
|
||||
* |!| TSF | MSG | GWL FAIL | GW uplink failed
|
||||
* | | TSF | SANCHK | OK | Sanity check passed
|
||||
* |!| TSF | SANCHK | FAIL | Sanity check failed, attempt to re-initialize radio
|
||||
* | | TSF | CRT | OK | Clearing routing table successful
|
||||
* |!| TSF | ROUTE | FPAR ACTIVE | Finding parent active, message not sent
|
||||
* |!| TSF | ROUTE | DST %%d UNKNOWN | Routing for destination (DST) unknown, send message to parent
|
||||
* |!| TSF | SEND | TNR | Transport not ready, message cannot be sent
|
||||
*
|
||||
* Incoming / outgoing messages:
|
||||
*
|
||||
* See <a href="https://www.mysensors.org/download/serial_api_20">here</a> for more detail on the format and definitons.
|
||||
*
|
||||
* Receiving a message
|
||||
* - TSF:MSG:READ,sender-last-destination,s=%%d,c=%%d,t=%%d,pt=%%d,l=%%d,sg=%%d:%%s
|
||||
*
|
||||
* Sending a message
|
||||
* - [!]TSF:MSG:SEND,sender-last-next-destination,s=%%d,c=%%d,t=%%d,pt=%%d,l=%%d,sg=%%d,ft=%%d,st=%%s:%%s
|
||||
*
|
||||
* Message fields:
|
||||
* - <b>s</b>=sensor ID
|
||||
* - <b>c</b>=command
|
||||
* - <b>t</b>=msg type
|
||||
* - <b>pt</b>=payload type
|
||||
* - <b>l</b>=length
|
||||
* - <b>ft</b>=failed uplink transmission counter
|
||||
* - <b>sg</b>=signing flag
|
||||
*
|
||||
* @brief API declaration for MyTransport
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MyTransport_h
|
||||
#define MyTransport_h
|
||||
|
||||
#include "MySensorsCore.h"
|
||||
|
||||
// debug
|
||||
#if defined(MY_DEBUG)
|
||||
#define TRANSPORT_DEBUG(x,...) debug(x, ##__VA_ARGS__) //!< debug
|
||||
#else
|
||||
#define TRANSPORT_DEBUG(x,...) //!< debug NULL
|
||||
#endif
|
||||
|
||||
#if defined(MY_REPEATER_FEATURE)
|
||||
#define TRANSMISSION_FAILURES 10 //!< search for a new parent node after this many transmission failures, higher threshold for repeating nodes
|
||||
#else
|
||||
#define TRANSMISSION_FAILURES 5 //!< search for a new parent node after this many transmission failures, lower threshold for non-repeating nodes
|
||||
#endif
|
||||
#define TIMEOUT_FAILURE_STATE 10000 //!< duration failure state
|
||||
#define STATE_TIMEOUT 2000 //!< general state timeout
|
||||
#define STATE_RETRIES 3 //!< retries before switching to FAILURE
|
||||
#define AUTO 255 //!< ID 255 is reserved for auto initialization of nodeId.
|
||||
#define BROADCAST_ADDRESS ((uint8_t)255) //!< broadcasts are addressed to ID 255
|
||||
#define DISTANCE_INVALID ((uint8_t)255) //!< invalid distance when searching for parent
|
||||
#define MAX_HOPS ((uint8_t)254) //!< maximal mumber of hops for ping/pong
|
||||
#define INVALID_HOPS ((uint8_t)255) //!< invalid hops
|
||||
#define MAX_SUBSEQ_MSGS 5 //!< Maximum number of subsequentially processed messages in FIFO (to prevent transport deadlock if HW issue)
|
||||
#define CHKUPL_INTERVAL ((uint32_t)10000) //!< Minimum time interval to re-check uplink
|
||||
|
||||
#define _autoFindParent (bool)(MY_PARENT_NODE_ID == AUTO) //!< returns true if static parent id is undefined
|
||||
#define isValidDistance(distance) (bool)(distance!=DISTANCE_INVALID) //!< returns true if distance is valid
|
||||
#define isValidParent(parent) (bool)(parent != AUTO) //!< returns true if parent is valid
|
||||
|
||||
/**
|
||||
* @brief SM state
|
||||
*
|
||||
* This structure stores SM state definitions
|
||||
*/
|
||||
struct transportState {
|
||||
void(*Transition)(); //!< state transition function
|
||||
void(*Update)(); //!< state update function
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Status variables and SM state
|
||||
*
|
||||
* This structure stores transport status and SM variables
|
||||
*/
|
||||
typedef struct {
|
||||
transportState* currentState; //!< pointer to current fsm state
|
||||
uint32_t stateEnter; //!< state enter timepoint
|
||||
uint32_t lastUplinkCheck; //!< last uplink check, required to prevent GW flooding
|
||||
uint32_t lastSanityCheck; //!< last sanity check
|
||||
// 8 bits
|
||||
bool findingParentNode : 1; //!< flag finding parent node is active
|
||||
bool preferredParentFound : 1; //!< flag preferred parent found
|
||||
bool uplinkOk : 1; //!< flag uplink ok
|
||||
bool pingActive : 1; //!< flag ping active
|
||||
bool transportActive : 1; //!< flag transport active
|
||||
uint8_t reserved : 3; //!< reserved
|
||||
// 8 bits
|
||||
uint8_t retries : 4; //!< retries / state re-enter
|
||||
uint8_t failedUplinkTransmissions : 4; //!< counter failed uplink transmissions
|
||||
// 8 bits
|
||||
uint8_t pingResponse; //!< stores hops received in I_PONG
|
||||
} __attribute__((packed)) transportSM;
|
||||
|
||||
|
||||
// PRIVATE functions
|
||||
|
||||
/**
|
||||
* @brief Initialize SM variables and transport HW
|
||||
*/
|
||||
void stInitTransition();
|
||||
/**
|
||||
* @brief Find parent
|
||||
*/
|
||||
void stParentTransition();
|
||||
/**
|
||||
* @brief Verify find parent responses
|
||||
*/
|
||||
void stParentUpdate();
|
||||
/**
|
||||
* @brief Send ID request
|
||||
*/
|
||||
void stIDTransition();
|
||||
/**
|
||||
* @brief Verify ID response and GW link
|
||||
*/
|
||||
void stIDUpdate();
|
||||
/**
|
||||
* @brief Send uplink ping request
|
||||
*/
|
||||
void stUplinkTransition();
|
||||
/**
|
||||
* @brief Set transport OK
|
||||
*/
|
||||
void stReadyTransition();
|
||||
/**
|
||||
* @brief Monitor transport link
|
||||
*/
|
||||
void stReadyUpdate();
|
||||
/**
|
||||
* @brief Transport failure and power down radio
|
||||
*/
|
||||
void stFailureTransition();
|
||||
/**
|
||||
* @brief Re-initialize transport after timeout
|
||||
*/
|
||||
void stFailureUpdate();
|
||||
/**
|
||||
* @brief Switch SM state
|
||||
* @param newState New state to switch SM to
|
||||
*/
|
||||
void transportSwitchSM(transportState& newState);
|
||||
/**
|
||||
* @brief Update SM state
|
||||
*/
|
||||
void transportUpdateSM();
|
||||
/**
|
||||
* @brief Request time in current SM state
|
||||
* @return ms in current state
|
||||
*/
|
||||
uint32_t transportTimeInState();
|
||||
/**
|
||||
* @brief Call transport driver sanity check
|
||||
*/
|
||||
void transportInvokeSanityCheck();
|
||||
/**
|
||||
* @brief Process all pending messages in RX FIFO
|
||||
*/
|
||||
void transportProcessFIFO();
|
||||
/**
|
||||
* @brief Receive message from RX FIFO and process
|
||||
*/
|
||||
void transportProcessMessage();
|
||||
/**
|
||||
* @brief Assign node ID
|
||||
* @param newNodeId New node ID
|
||||
* @return true if node ID valid and successfully assigned
|
||||
*/
|
||||
bool transportAssignNodeID(uint8_t newNodeId);
|
||||
/**
|
||||
* @brief Wait and process messages for a defined amount of time until specified message received
|
||||
* @param ms Time to wait and process incoming messages in ms
|
||||
* @param cmd Specific command
|
||||
* @param msgtype Specific message type
|
||||
* @return true if specified command received within waiting time
|
||||
*/
|
||||
bool transportWait(uint32_t ms, uint8_t cmd, uint8_t msgtype);
|
||||
/**
|
||||
* @brief Ping node
|
||||
* @param targetId Node to be pinged
|
||||
* @return hops from pinged node or 255 if no answer received within 2000ms
|
||||
*/
|
||||
uint8_t transportPingNode(uint8_t targetId);
|
||||
/**
|
||||
* @brief Send and route message according to destination
|
||||
*
|
||||
* This function is used in MyTransport and omits the transport state check, i.e. message can be sent even if transport is not ready
|
||||
*
|
||||
* @param message
|
||||
* @return true if message sent successfully
|
||||
*/
|
||||
bool transportRouteMessage(MyMessage &message);
|
||||
/**
|
||||
* @brief Send and route message according to destination with transport state check
|
||||
* @param message
|
||||
* @return true if message sent successfully and false if sending error or transport !OK
|
||||
*/
|
||||
bool transportSendRoute(MyMessage &message);
|
||||
/**
|
||||
* @brief Send message to recipient
|
||||
* @param to Recipient of message
|
||||
* @param message
|
||||
* @return true if message sent successfully
|
||||
*/
|
||||
bool transportSendWrite(uint8_t to, MyMessage &message);
|
||||
/**
|
||||
* @brief Check uplink to GW, includes flooding control
|
||||
* @param force to override flood control timer
|
||||
* @return true if uplink ok
|
||||
*/
|
||||
bool transportCheckUplink(bool force);
|
||||
|
||||
// PUBLIC functions
|
||||
|
||||
/**
|
||||
* @brief Initialize transport and SM
|
||||
*/
|
||||
void transportInitialize();
|
||||
/**
|
||||
* @brief Process FIFO msg and update SM
|
||||
*/
|
||||
void transportProcess();
|
||||
/**
|
||||
* @brief Flag transport ready
|
||||
* @return true if transport is initialize and ready
|
||||
*/
|
||||
bool isTransportReady();
|
||||
/**
|
||||
* @brief Flag searching parent ongoing
|
||||
* @return true if transport is searching for parent
|
||||
*/
|
||||
bool isTransportSearchingParent();
|
||||
/**
|
||||
* @brief Clear routing table
|
||||
*/
|
||||
void transportClearRoutingTable();
|
||||
/**
|
||||
* @brief Return heart beat, i.e. ms in current state
|
||||
*/
|
||||
uint32_t transportGetHeartbeat();
|
||||
|
||||
// interface functions for radio driver
|
||||
|
||||
/**
|
||||
* @brief Initialize transport HW
|
||||
* @return true if initialization successful
|
||||
*/
|
||||
bool transportInit();
|
||||
/**
|
||||
* @brief Set node address
|
||||
*/
|
||||
void transportSetAddress(uint8_t address);
|
||||
/**
|
||||
* @brief Retrieve node address
|
||||
*/
|
||||
uint8_t transportGetAddress();
|
||||
/**
|
||||
* @brief Send message
|
||||
* @param to recipient
|
||||
* @param data message to be sent
|
||||
* @param len length of message (header + payload)
|
||||
* @return true if message sent successfully
|
||||
*/
|
||||
bool transportSend(uint8_t to, const void* data, uint8_t len);
|
||||
/**
|
||||
* @brief Verify if RX FIFO has pending messages
|
||||
* @return true if message available in RX FIFO
|
||||
*/
|
||||
bool transportAvailable();
|
||||
/**
|
||||
* @brief Sanity check for transport: is transport still responsive?
|
||||
* @return true transport ok
|
||||
*/
|
||||
bool transportSanityCheck();
|
||||
/**
|
||||
* @brief Receive message from FIFO
|
||||
* @return length of recevied message (header + payload)
|
||||
*/
|
||||
uint8_t transportReceive(void* data);
|
||||
/**
|
||||
* @brief Power down transport HW
|
||||
*/
|
||||
void transportPowerDown();
|
||||
|
||||
|
||||
#endif // MyTransport_h
|
||||
/** @}*/
|
||||
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* 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 <henrik.ekblad@mysensors.org>
|
||||
* 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 "drivers/RF24/RF24.h"
|
||||
#include "drivers/CircularBuffer/CircularBuffer.h"
|
||||
|
||||
#if defined(MY_RF24_ENABLE_ENCRYPTION)
|
||||
#include "drivers/AES/AES.h"
|
||||
#endif
|
||||
|
||||
#ifdef MY_RF24_IRQ_PIN
|
||||
typedef struct _transportQueuedMessage
|
||||
{
|
||||
uint8_t m_len; // Length of the data
|
||||
uint8_t m_data[MAX_MESSAGE_LENGTH]; // The raw data
|
||||
} transportQueuedMessage;
|
||||
|
||||
/** Buffer to store queued messages in. */
|
||||
static transportQueuedMessage transportRxQueueStorage[MY_RX_MESSAGE_BUFFER_SIZE];
|
||||
/** Circular buffer, which uses the transportRxQueueStorage and administers stored messages. */
|
||||
static CircularBuffer<transportQueuedMessage> transportRxQueue(transportRxQueueStorage, MY_RX_MESSAGE_BUFFER_SIZE);
|
||||
|
||||
static volatile uint8_t transportLostMessageCount = 0;
|
||||
|
||||
static void transportRxCallback(void)
|
||||
{
|
||||
// Called for each message received by radio, from interrupt context.
|
||||
// This function _must_ call RF24_readMessage() to de-assert interrupt line!
|
||||
if (!transportRxQueue.full())
|
||||
{
|
||||
transportQueuedMessage* msg = transportRxQueue.getFront();
|
||||
msg->m_len = RF24_readMessage(msg->m_data); // Read payload & clear RX_DR
|
||||
(void)transportRxQueue.pushFront(msg);
|
||||
} else {
|
||||
// Queue is full. Discard message.
|
||||
(void)RF24_readMessage(NULL); // Read payload & clear RX_DR
|
||||
// Keep track of messages lost. Max 255, prevent wrapping.
|
||||
if (transportLostMessageCount < 255)
|
||||
{
|
||||
++transportLostMessageCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(MY_RF24_ENABLE_ENCRYPTION)
|
||||
AES _aes;
|
||||
uint8_t _dataenc[32] = {0};
|
||||
uint8_t _psk[16];
|
||||
#endif
|
||||
|
||||
bool transportInit() {
|
||||
#if defined(MY_RF24_ENABLE_ENCRYPTION)
|
||||
hwReadConfigBlock((void*)_psk, (void*)EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, 16);
|
||||
//set up AES-key
|
||||
_aes.set_key(_psk, 16);
|
||||
// Make sure it is purged from memory when set
|
||||
memset(_psk, 0, 16);
|
||||
#endif
|
||||
|
||||
#ifdef MY_RF24_IRQ_PIN
|
||||
RF24_registerReceiveCallback( transportRxCallback );
|
||||
#endif
|
||||
return RF24_initialize();
|
||||
}
|
||||
|
||||
void transportSetAddress(uint8_t address) {
|
||||
RF24_setNodeAddress(address);
|
||||
RF24_startListening();
|
||||
}
|
||||
|
||||
uint8_t transportGetAddress() {
|
||||
return RF24_getNodeID();
|
||||
}
|
||||
|
||||
bool transportSend(uint8_t recipient, const void* data, uint8_t len) {
|
||||
#if defined(MY_RF24_ENABLE_ENCRYPTION)
|
||||
// copy input data because it is read-only
|
||||
memcpy(_dataenc,data,len);
|
||||
// has to be adjusted, WIP!
|
||||
_aes.set_IV(0);
|
||||
len = len > 16 ? 32 : 16;
|
||||
//encrypt data
|
||||
_aes.cbc_encrypt(_dataenc, _dataenc, len/16);
|
||||
bool status = RF24_sendMessage( recipient, _dataenc, len );
|
||||
#else
|
||||
bool status = RF24_sendMessage( recipient, data, len );
|
||||
#endif
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
bool transportAvailable() {
|
||||
#ifdef MY_RF24_IRQ_PIN
|
||||
(void)RF24_isDataAvailable; // Prevent 'defined but not used' warning
|
||||
return !transportRxQueue.empty();
|
||||
#else
|
||||
return RF24_isDataAvailable();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool transportSanityCheck() {
|
||||
return RF24_sanityCheck();
|
||||
}
|
||||
|
||||
uint8_t transportReceive(void* data) {
|
||||
uint8_t len = 0;
|
||||
#ifdef MY_RF24_IRQ_PIN
|
||||
transportQueuedMessage* msg = transportRxQueue.getBack();
|
||||
if (msg)
|
||||
{
|
||||
len = msg->m_len;
|
||||
(void)memcpy(data, msg->m_data, len);
|
||||
(void)transportRxQueue.popBack();
|
||||
}
|
||||
#else
|
||||
len = RF24_readMessage(data);
|
||||
#endif
|
||||
#if defined(MY_RF24_ENABLE_ENCRYPTION)
|
||||
// has to be adjusted, WIP!
|
||||
_aes.set_IV(0);
|
||||
// decrypt data
|
||||
_aes.cbc_decrypt((byte*)(data), (byte*)(data), len>16?2:1);
|
||||
#endif
|
||||
return len;
|
||||
}
|
||||
|
||||
void transportPowerDown() {
|
||||
RF24_powerDown();
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
/**
|
||||
/*
|
||||
* 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
|
||||
@@ -32,7 +32,7 @@ bool transportInit() {
|
||||
#ifdef MY_RFM69_ENABLE_ENCRYPTION
|
||||
uint8_t _psk[16];
|
||||
hwReadConfigBlock((void*)_psk, (void*)EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, 16);
|
||||
_radio.encrypt(_psk);
|
||||
_radio.encrypt((const char*)_psk);
|
||||
memset(_psk, 0, 16); // Make sure it is purged from memory when set
|
||||
#endif
|
||||
return true;
|
||||
@@ -53,14 +53,15 @@ bool transportSend(uint8_t to, const void* data, uint8_t len) {
|
||||
return _radio.sendWithRetry(to,data,len);
|
||||
}
|
||||
|
||||
bool transportAvailable(uint8_t *to) {
|
||||
if (_radio.TARGETID == BROADCAST_ADDRESS)
|
||||
*to = BROADCAST_ADDRESS;
|
||||
else
|
||||
*to = _address;
|
||||
bool transportAvailable() {
|
||||
return _radio.receiveDone();
|
||||
}
|
||||
|
||||
bool transportSanityCheck() {
|
||||
// not implemented yet
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t transportReceive(void* data) {
|
||||
memcpy(data,(const void *)_radio.DATA, _radio.DATALEN);
|
||||
// Send ack back if this message wasn't a broadcast
|
||||
@@ -1,4 +1,4 @@
|
||||
/**
|
||||
/*
|
||||
* 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
|
||||
@@ -126,7 +126,7 @@ void _serialReset(){
|
||||
// our station ID, then look for a registered command that matches the
|
||||
// command code. If all the above is true, execute the command's
|
||||
// function.
|
||||
boolean _serialProcess()
|
||||
bool _serialProcess()
|
||||
{
|
||||
char inch;
|
||||
unsigned char i;
|
||||
@@ -332,17 +332,16 @@ uint8_t transportGetAddress() {
|
||||
}
|
||||
|
||||
|
||||
bool transportAvailable(uint8_t *to) {
|
||||
bool transportAvailable() {
|
||||
_serialProcess();
|
||||
if (_packet_received == true) {
|
||||
if (_recStation==BROADCAST_ADDRESS) {
|
||||
*to = BROADCAST_ADDRESS;
|
||||
}
|
||||
else { *to = _nodeId; }
|
||||
}
|
||||
return _packet_received;
|
||||
}
|
||||
|
||||
bool transportSanityCheck() {
|
||||
// not implemented yet
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t transportReceive(void* data) {
|
||||
if (_packet_received) {
|
||||
memcpy(data,_data,_packet_len);
|
||||
@@ -1,11 +1,11 @@
|
||||
/***
|
||||
* This file defines the Sensor library version number
|
||||
* This file defines the Sensor library version number
|
||||
* Normally, contributors should not modify this directly
|
||||
* as it is managed by the MySensors Bot.
|
||||
*/
|
||||
#ifndef Version_h
|
||||
#define Version_h
|
||||
|
||||
#define LIBRARY_VERSION "2.0.0-beta"
|
||||
#define MYSENSORS_LIBRARY_VERSION "2.0.1-beta"
|
||||
|
||||
#endif
|
||||
@@ -31,6 +31,8 @@
|
||||
#define PSTR(x) (x)
|
||||
#elif defined(ARDUINO_ARCH_ESP8266)
|
||||
#include <pgmspace.h>
|
||||
#elif defined(ARDUINO_ARCH_SAMD)
|
||||
#define printf_P printf
|
||||
#else
|
||||
#include <avr/pgmspace.h>
|
||||
#endif
|
||||
+172
-152
@@ -1,58 +1,26 @@
|
||||
#include "Arduino.h"
|
||||
#include "ATSHA204.h"
|
||||
|
||||
// atsha204Class Constructor
|
||||
// Feed this function the Arduino-ized pin number you want to assign to the ATSHA204's SDA pin
|
||||
// This will find the DDRX, PORTX, and PINX registrs it'll need to point to to control that pin
|
||||
// As well as the bit value for each of those registers
|
||||
ATSHA204Class::ATSHA204Class(uint8_t pin)
|
||||
{
|
||||
#if defined(ARDUINO_ARCH_AVR)
|
||||
device_pin = digitalPinToBitMask(pin); // Find the bit value of the pin
|
||||
uint8_t port = digitalPinToPort(pin); // temoporarily used to get the next three registers
|
||||
|
||||
// Point to data direction register port of pin
|
||||
device_port_DDR = portModeRegister(port);
|
||||
// Point to output register of pin
|
||||
device_port_OUT = portOutputRegister(port);
|
||||
// Point to input register of pin
|
||||
device_port_IN = portInputRegister(port);
|
||||
#else
|
||||
device_pin = pin;
|
||||
/* Local data and function prototypes */
|
||||
|
||||
static uint8_t device_pin;
|
||||
#ifdef ARDUINO_ARCH_AVR
|
||||
static volatile uint8_t *device_port_DDR, *device_port_OUT, *device_port_IN;
|
||||
#endif
|
||||
}
|
||||
|
||||
void ATSHA204Class::getSerialNumber(uint8_t * response)
|
||||
{
|
||||
uint8_t readCommand[READ_COUNT];
|
||||
uint8_t readResponse[READ_4_RSP_SIZE];
|
||||
|
||||
/* read from bytes 0->3 of config zone */
|
||||
uint8_t returnCode = sha204m_read(readCommand, readResponse, SHA204_ZONE_CONFIG, ADDRESS_SN03);
|
||||
if (!returnCode)
|
||||
{
|
||||
for (int i=0; i<4; i++) // store bytes 0-3 into respones array
|
||||
response[i] = readResponse[SHA204_BUFFER_POS_DATA+i];
|
||||
|
||||
/* read from bytes 8->11 of config zone */
|
||||
returnCode = sha204m_read(readCommand, readResponse, SHA204_ZONE_CONFIG, ADDRESS_SN47);
|
||||
|
||||
for (int i=4; i<8; i++) // store bytes 4-7 of SN into response array
|
||||
response[i] = readResponse[SHA204_BUFFER_POS_DATA+(i-4)];
|
||||
|
||||
if (!returnCode)
|
||||
{ /* Finally if last two reads were successful, read byte 8 of the SN */
|
||||
returnCode = sha204m_read(readCommand, readResponse, SHA204_ZONE_CONFIG, ADDRESS_SN8);
|
||||
response[8] = readResponse[SHA204_BUFFER_POS_DATA]; // Byte 8 of SN should always be 0xEE
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
static void sha204c_calculate_crc(uint8_t length, uint8_t *data, uint8_t *crc);
|
||||
static uint8_t sha204c_check_crc(uint8_t *response);
|
||||
static void swi_set_signal_pin(uint8_t is_high);
|
||||
static uint8_t swi_receive_bytes(uint8_t count, uint8_t *buffer);
|
||||
static uint8_t swi_send_bytes(uint8_t count, uint8_t *buffer);
|
||||
static uint8_t swi_send_byte(uint8_t value);
|
||||
static uint8_t sha204p_receive_response(uint8_t size, uint8_t *response);
|
||||
static uint8_t sha204m_read(uint8_t *tx_buffer, uint8_t *rx_buffer, uint8_t zone, uint16_t address);
|
||||
static uint8_t sha204c_resync(uint8_t size, uint8_t *response);
|
||||
static uint8_t sha204c_send_and_receive(uint8_t *tx_buffer, uint8_t rx_size, uint8_t *rx_buffer, uint8_t execution_delay, uint8_t execution_timeout);
|
||||
|
||||
/* SWI bit bang functions */
|
||||
|
||||
void ATSHA204Class::swi_set_signal_pin(uint8_t is_high)
|
||||
static void swi_set_signal_pin(uint8_t is_high)
|
||||
{
|
||||
SHA204_SET_OUTPUT();
|
||||
|
||||
@@ -62,7 +30,7 @@ void ATSHA204Class::swi_set_signal_pin(uint8_t is_high)
|
||||
SHA204_POUT_LOW();
|
||||
}
|
||||
|
||||
uint8_t ATSHA204Class::swi_send_bytes(uint8_t count, uint8_t *buffer)
|
||||
static uint8_t swi_send_bytes(uint8_t count, uint8_t *buffer)
|
||||
{
|
||||
uint8_t i, bit_mask;
|
||||
|
||||
@@ -72,23 +40,23 @@ uint8_t ATSHA204Class::swi_send_bytes(uint8_t count, uint8_t *buffer)
|
||||
// Set signal pin as output.
|
||||
SHA204_POUT_HIGH();
|
||||
SHA204_SET_OUTPUT();
|
||||
|
||||
|
||||
|
||||
// Wait turn around time.
|
||||
delayMicroseconds(RX_TX_DELAY); //RX_TX_DELAY;
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
for (bit_mask = 1; bit_mask > 0; bit_mask <<= 1)
|
||||
for (bit_mask = 1; bit_mask > 0; bit_mask <<= 1)
|
||||
{
|
||||
if (bit_mask & buffer[i])
|
||||
if (bit_mask & buffer[i])
|
||||
{
|
||||
SHA204_POUT_LOW(); //*device_port_OUT &= ~device_pin;
|
||||
delayMicroseconds(BIT_DELAY); //BIT_DELAY_1;
|
||||
SHA204_POUT_HIGH(); //*device_port_OUT |= device_pin;
|
||||
delayMicroseconds(7*BIT_DELAY); //BIT_DELAY_7;
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
// Send a zero bit.
|
||||
SHA204_POUT_LOW(); //*device_port_OUT &= ~device_pin;
|
||||
@@ -106,12 +74,12 @@ uint8_t ATSHA204Class::swi_send_bytes(uint8_t count, uint8_t *buffer)
|
||||
return SWI_FUNCTION_RETCODE_SUCCESS;
|
||||
}
|
||||
|
||||
uint8_t ATSHA204Class::swi_send_byte(uint8_t value)
|
||||
static uint8_t swi_send_byte(uint8_t value)
|
||||
{
|
||||
return swi_send_bytes(1, &value);
|
||||
}
|
||||
|
||||
uint8_t ATSHA204Class::swi_receive_bytes(uint8_t count, uint8_t *buffer)
|
||||
static uint8_t swi_receive_bytes(uint8_t count, uint8_t *buffer)
|
||||
{
|
||||
uint8_t status = SWI_FUNCTION_RETCODE_SUCCESS;
|
||||
uint8_t i;
|
||||
@@ -124,11 +92,11 @@ uint8_t ATSHA204Class::swi_receive_bytes(uint8_t count, uint8_t *buffer)
|
||||
|
||||
// Configure signal pin as input.
|
||||
SHA204_SET_INPUT();
|
||||
|
||||
|
||||
// Receive bits and store in buffer.
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
for (bit_mask = 1; bit_mask > 0; bit_mask <<= 1)
|
||||
for (bit_mask = 1; bit_mask > 0; bit_mask <<= 1)
|
||||
{
|
||||
pulse_count = 0;
|
||||
|
||||
@@ -138,23 +106,23 @@ uint8_t ATSHA204Class::swi_receive_bytes(uint8_t count, uint8_t *buffer)
|
||||
timeout_count = START_PULSE_TIME_OUT;
|
||||
|
||||
// Detect start bit.
|
||||
while (--timeout_count > 0)
|
||||
while (--timeout_count > 0)
|
||||
{
|
||||
// Wait for falling edge.
|
||||
if (SHA204_PIN_READ() == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (timeout_count == 0)
|
||||
if (timeout_count == 0)
|
||||
{
|
||||
status = SWI_FUNCTION_RETCODE_TIMEOUT;
|
||||
break;
|
||||
}
|
||||
|
||||
do
|
||||
do
|
||||
{
|
||||
// Wait for rising edge.
|
||||
if (SHA204_PIN_READ() != 0)
|
||||
if (SHA204_PIN_READ() != 0)
|
||||
{
|
||||
// For an Atmel microcontroller this might be faster than "pulse_count++".
|
||||
pulse_count = 1;
|
||||
@@ -162,7 +130,7 @@ uint8_t ATSHA204Class::swi_receive_bytes(uint8_t count, uint8_t *buffer)
|
||||
}
|
||||
} while (--timeout_count > 0);
|
||||
|
||||
if (pulse_count == 0)
|
||||
if (pulse_count == 0)
|
||||
{
|
||||
status = SWI_FUNCTION_RETCODE_TIMEOUT;
|
||||
break;
|
||||
@@ -175,9 +143,9 @@ uint8_t ATSHA204Class::swi_receive_bytes(uint8_t count, uint8_t *buffer)
|
||||
timeout_count = ZERO_PULSE_TIME_OUT;
|
||||
|
||||
// Detect possible edge indicating zero bit.
|
||||
do
|
||||
do
|
||||
{
|
||||
if (SHA204_PIN_READ() == 0)
|
||||
if (SHA204_PIN_READ() == 0)
|
||||
{
|
||||
// For an Atmel microcontroller this might be faster than "pulse_count++".
|
||||
pulse_count = 2;
|
||||
@@ -187,9 +155,9 @@ uint8_t ATSHA204Class::swi_receive_bytes(uint8_t count, uint8_t *buffer)
|
||||
|
||||
// Wait for rising edge of zero pulse before returning. Otherwise we might interpret
|
||||
// its rising edge as the next start pulse.
|
||||
if (pulse_count == 2)
|
||||
if (pulse_count == 2)
|
||||
{
|
||||
do
|
||||
do
|
||||
{
|
||||
if (SHA204_PIN_READ() != 0)
|
||||
break;
|
||||
@@ -206,7 +174,7 @@ uint8_t ATSHA204Class::swi_receive_bytes(uint8_t count, uint8_t *buffer)
|
||||
}
|
||||
interrupts(); //swi_enable_interrupts();
|
||||
|
||||
if (status == SWI_FUNCTION_RETCODE_TIMEOUT)
|
||||
if (status == SWI_FUNCTION_RETCODE_TIMEOUT)
|
||||
{
|
||||
if (i > 0)
|
||||
// Indicate that we timed out after having received at least one byte.
|
||||
@@ -217,12 +185,7 @@ uint8_t ATSHA204Class::swi_receive_bytes(uint8_t count, uint8_t *buffer)
|
||||
|
||||
/* Physical functions */
|
||||
|
||||
void ATSHA204Class::sha204c_sleep()
|
||||
{
|
||||
swi_send_byte(SHA204_SWI_FLAG_SLEEP);
|
||||
}
|
||||
|
||||
uint8_t ATSHA204Class::sha204p_receive_response(uint8_t size, uint8_t *response)
|
||||
static uint8_t sha204p_receive_response(uint8_t size, uint8_t *response)
|
||||
{
|
||||
uint8_t count_byte;
|
||||
uint8_t i;
|
||||
@@ -234,7 +197,7 @@ uint8_t ATSHA204Class::sha204p_receive_response(uint8_t size, uint8_t *response)
|
||||
(void) swi_send_byte(SHA204_SWI_FLAG_TX);
|
||||
|
||||
ret_code = swi_receive_bytes(size, response);
|
||||
if (ret_code == SWI_FUNCTION_RETCODE_SUCCESS || ret_code == SWI_FUNCTION_RETCODE_RX_FAIL)
|
||||
if (ret_code == SWI_FUNCTION_RETCODE_SUCCESS || ret_code == SWI_FUNCTION_RETCODE_RX_FAIL)
|
||||
{
|
||||
count_byte = response[SHA204_BUFFER_POS_COUNT];
|
||||
if ((count_byte < SHA204_RSP_SIZE_MIN) || (count_byte > size))
|
||||
@@ -254,35 +217,7 @@ uint8_t ATSHA204Class::sha204p_receive_response(uint8_t size, uint8_t *response)
|
||||
|
||||
/* Communication functions */
|
||||
|
||||
uint8_t ATSHA204Class::sha204c_wakeup(uint8_t *response)
|
||||
{
|
||||
swi_set_signal_pin(0);
|
||||
delayMicroseconds(10*SHA204_WAKEUP_PULSE_WIDTH);
|
||||
swi_set_signal_pin(1);
|
||||
delay(SHA204_WAKEUP_DELAY);
|
||||
|
||||
uint8_t ret_code = sha204p_receive_response(SHA204_RSP_SIZE_MIN, response);
|
||||
if (ret_code != SHA204_SUCCESS)
|
||||
return ret_code;
|
||||
|
||||
// Verify status response.
|
||||
if (response[SHA204_BUFFER_POS_COUNT] != SHA204_RSP_SIZE_MIN)
|
||||
ret_code = SHA204_INVALID_SIZE;
|
||||
else if (response[SHA204_BUFFER_POS_STATUS] != SHA204_STATUS_BYTE_WAKEUP)
|
||||
ret_code = SHA204_COMM_FAIL;
|
||||
else
|
||||
{
|
||||
if ((response[SHA204_RSP_SIZE_MIN - SHA204_CRC_SIZE] != 0x33)
|
||||
|| (response[SHA204_RSP_SIZE_MIN + 1 - SHA204_CRC_SIZE] != 0x43))
|
||||
ret_code = SHA204_BAD_CRC;
|
||||
}
|
||||
if (ret_code != SHA204_SUCCESS)
|
||||
delay(SHA204_COMMAND_EXEC_MAX);
|
||||
|
||||
return ret_code;
|
||||
}
|
||||
|
||||
uint8_t ATSHA204Class::sha204c_resync(uint8_t size, uint8_t *response)
|
||||
static uint8_t sha204c_resync(uint8_t size, uint8_t *response)
|
||||
{
|
||||
// Try to re-synchronize without sending a Wake token
|
||||
// (step 1 of the re-synchronization process).
|
||||
@@ -294,8 +229,8 @@ uint8_t ATSHA204Class::sha204c_resync(uint8_t size, uint8_t *response)
|
||||
// We lost communication. Send a Wake pulse and try
|
||||
// to receive a response (steps 2 and 3 of the
|
||||
// re-synchronization process).
|
||||
sha204c_sleep();
|
||||
ret_code = sha204c_wakeup(response);
|
||||
atsha204_sleep();
|
||||
ret_code = atsha204_wakeup(response);
|
||||
|
||||
// Translate a return value of success into one
|
||||
// that indicates that the device had to be woken up
|
||||
@@ -303,7 +238,7 @@ uint8_t ATSHA204Class::sha204c_resync(uint8_t size, uint8_t *response)
|
||||
return (ret_code == SHA204_SUCCESS ? SHA204_RESYNC_WITH_WAKEUP : ret_code);
|
||||
}
|
||||
|
||||
uint8_t ATSHA204Class::sha204c_send_and_receive(uint8_t *tx_buffer, uint8_t rx_size, uint8_t *rx_buffer, uint8_t execution_delay, uint8_t execution_timeout)
|
||||
static uint8_t sha204c_send_and_receive(uint8_t *tx_buffer, uint8_t rx_size, uint8_t *rx_buffer, uint8_t execution_delay, uint8_t execution_timeout)
|
||||
{
|
||||
uint8_t ret_code = SHA204_FUNC_FAIL;
|
||||
uint8_t ret_code_resync;
|
||||
@@ -322,7 +257,7 @@ uint8_t ATSHA204Class::sha204c_send_and_receive(uint8_t *tx_buffer, uint8_t rx_s
|
||||
// Retry loop for sending a command and receiving a response.
|
||||
n_retries_send = SHA204_RETRY_COUNT + 1;
|
||||
|
||||
while ((n_retries_send-- > 0) && (ret_code != SHA204_SUCCESS))
|
||||
while ((n_retries_send-- > 0) && (ret_code != SHA204_SUCCESS))
|
||||
{
|
||||
// Send command.
|
||||
ret_code = swi_send_byte(SHA204_SWI_FLAG_CMD);
|
||||
@@ -331,7 +266,7 @@ uint8_t ATSHA204Class::sha204c_send_and_receive(uint8_t *tx_buffer, uint8_t rx_s
|
||||
else
|
||||
ret_code = swi_send_bytes(count, tx_buffer);
|
||||
|
||||
if (ret_code != SHA204_SUCCESS)
|
||||
if (ret_code != SHA204_SUCCESS)
|
||||
{
|
||||
if (sha204c_resync(rx_size, rx_buffer) == SHA204_RX_NO_RESPONSE)
|
||||
return ret_code; // The device seems to be dead in the water.
|
||||
@@ -344,7 +279,7 @@ uint8_t ATSHA204Class::sha204c_send_and_receive(uint8_t *tx_buffer, uint8_t rx_s
|
||||
|
||||
// Retry loop for receiving a response.
|
||||
n_retries_receive = SHA204_RETRY_COUNT + 1;
|
||||
while (n_retries_receive-- > 0)
|
||||
while (n_retries_receive-- > 0)
|
||||
{
|
||||
// Reset response buffer.
|
||||
for (i = 0; i < rx_size; i++)
|
||||
@@ -352,14 +287,14 @@ uint8_t ATSHA204Class::sha204c_send_and_receive(uint8_t *tx_buffer, uint8_t rx_s
|
||||
|
||||
// Poll for response.
|
||||
timeout_countdown = execution_timeout_us;
|
||||
do
|
||||
do
|
||||
{
|
||||
ret_code = sha204p_receive_response(rx_size, rx_buffer);
|
||||
timeout_countdown -= SHA204_RESPONSE_TIMEOUT;
|
||||
}
|
||||
}
|
||||
while ((timeout_countdown > SHA204_RESPONSE_TIMEOUT) && (ret_code == SHA204_RX_NO_RESPONSE));
|
||||
|
||||
if (ret_code == SHA204_RX_NO_RESPONSE)
|
||||
if (ret_code == SHA204_RX_NO_RESPONSE)
|
||||
{
|
||||
// We did not receive a response. Re-synchronize and send command again.
|
||||
if (sha204c_resync(rx_size, rx_buffer) == SHA204_RX_NO_RESPONSE)
|
||||
@@ -389,7 +324,7 @@ uint8_t ATSHA204Class::sha204c_send_and_receive(uint8_t *tx_buffer, uint8_t rx_s
|
||||
// We received a response of valid size.
|
||||
// Check the consistency of the response.
|
||||
ret_code = sha204c_check_crc(rx_buffer);
|
||||
if (ret_code == SHA204_SUCCESS)
|
||||
if (ret_code == SHA204_SUCCESS)
|
||||
{
|
||||
// Received valid response.
|
||||
if (rx_buffer[SHA204_BUFFER_POS_COUNT] > SHA204_RSP_SIZE_MIN)
|
||||
@@ -405,7 +340,7 @@ uint8_t ATSHA204Class::sha204c_send_and_receive(uint8_t *tx_buffer, uint8_t rx_s
|
||||
return SHA204_PARSE_ERROR;
|
||||
if (status_byte == SHA204_STATUS_BYTE_EXEC)
|
||||
return SHA204_CMD_FAIL;
|
||||
if (status_byte == SHA204_STATUS_BYTE_COMM)
|
||||
if (status_byte == SHA204_STATUS_BYTE_COMM)
|
||||
{
|
||||
// In case of the device status byte indicating a communication
|
||||
// error this function exits the retry loop for receiving a response
|
||||
@@ -420,7 +355,7 @@ uint8_t ATSHA204Class::sha204c_send_and_receive(uint8_t *tx_buffer, uint8_t rx_s
|
||||
return ret_code;
|
||||
}
|
||||
|
||||
else
|
||||
else
|
||||
{
|
||||
// Received response with incorrect CRC.
|
||||
ret_code_resync = sha204c_resync(rx_size, rx_buffer);
|
||||
@@ -445,7 +380,8 @@ uint8_t ATSHA204Class::sha204c_send_and_receive(uint8_t *tx_buffer, uint8_t rx_s
|
||||
|
||||
|
||||
/* Marshaling functions */
|
||||
uint8_t ATSHA204Class::sha204m_read(uint8_t *tx_buffer, uint8_t *rx_buffer, uint8_t zone, uint16_t address)
|
||||
|
||||
static uint8_t sha204m_read(uint8_t *tx_buffer, uint8_t *rx_buffer, uint8_t zone, uint16_t address)
|
||||
{
|
||||
uint8_t rx_size;
|
||||
|
||||
@@ -462,16 +398,113 @@ uint8_t ATSHA204Class::sha204m_read(uint8_t *tx_buffer, uint8_t *rx_buffer, uint
|
||||
return sha204c_send_and_receive(&tx_buffer[0], rx_size, &rx_buffer[0], READ_DELAY, READ_EXEC_MAX - READ_DELAY);
|
||||
}
|
||||
|
||||
uint8_t ATSHA204Class::sha204m_execute(uint8_t op_code, uint8_t param1, uint16_t param2,
|
||||
/* CRC Calculator and Checker */
|
||||
|
||||
static void sha204c_calculate_crc(uint8_t length, uint8_t *data, uint8_t *crc)
|
||||
{
|
||||
uint8_t counter;
|
||||
uint16_t crc_register = 0;
|
||||
uint16_t polynom = 0x8005;
|
||||
uint8_t shift_register;
|
||||
uint8_t data_bit, crc_bit;
|
||||
|
||||
for (counter = 0; counter < length; counter++)
|
||||
{
|
||||
for (shift_register = 0x01; shift_register > 0x00; shift_register <<= 1)
|
||||
{
|
||||
data_bit = (data[counter] & shift_register) ? 1 : 0;
|
||||
crc_bit = crc_register >> 15;
|
||||
|
||||
// Shift CRC to the left by 1.
|
||||
crc_register <<= 1;
|
||||
|
||||
if ((data_bit ^ crc_bit) != 0)
|
||||
crc_register ^= polynom;
|
||||
}
|
||||
}
|
||||
crc[0] = (uint8_t) (crc_register & 0x00FF);
|
||||
crc[1] = (uint8_t) (crc_register >> 8);
|
||||
}
|
||||
|
||||
static uint8_t sha204c_check_crc(uint8_t *response)
|
||||
{
|
||||
uint8_t crc[SHA204_CRC_SIZE];
|
||||
uint8_t count = response[SHA204_BUFFER_POS_COUNT];
|
||||
|
||||
count -= SHA204_CRC_SIZE;
|
||||
sha204c_calculate_crc(count, response, crc);
|
||||
|
||||
return (crc[0] == response[count] && crc[1] == response[count + 1])
|
||||
? SHA204_SUCCESS : SHA204_BAD_CRC;
|
||||
}
|
||||
|
||||
/* Public functions */
|
||||
|
||||
void atsha204_init(uint8_t pin)
|
||||
{
|
||||
#if defined(ARDUINO_ARCH_AVR)
|
||||
device_pin = digitalPinToBitMask(pin); // Find the bit value of the pin
|
||||
uint8_t port = digitalPinToPort(pin); // temoporarily used to get the next three registers
|
||||
|
||||
// Point to data direction register port of pin
|
||||
device_port_DDR = portModeRegister(port);
|
||||
// Point to output register of pin
|
||||
device_port_OUT = portOutputRegister(port);
|
||||
// Point to input register of pin
|
||||
device_port_IN = portInputRegister(port);
|
||||
#else
|
||||
device_pin = pin;
|
||||
#endif
|
||||
}
|
||||
|
||||
void atsha204_idle(void)
|
||||
{
|
||||
swi_send_byte(SHA204_SWI_FLAG_IDLE);
|
||||
}
|
||||
|
||||
void atsha204_sleep(void)
|
||||
{
|
||||
swi_send_byte(SHA204_SWI_FLAG_SLEEP);
|
||||
}
|
||||
|
||||
uint8_t atsha204_wakeup(uint8_t *response)
|
||||
{
|
||||
swi_set_signal_pin(0);
|
||||
delayMicroseconds(10*SHA204_WAKEUP_PULSE_WIDTH);
|
||||
swi_set_signal_pin(1);
|
||||
delay(SHA204_WAKEUP_DELAY);
|
||||
|
||||
uint8_t ret_code = sha204p_receive_response(SHA204_RSP_SIZE_MIN, response);
|
||||
if (ret_code != SHA204_SUCCESS)
|
||||
return ret_code;
|
||||
|
||||
// Verify status response.
|
||||
if (response[SHA204_BUFFER_POS_COUNT] != SHA204_RSP_SIZE_MIN)
|
||||
ret_code = SHA204_INVALID_SIZE;
|
||||
else if (response[SHA204_BUFFER_POS_STATUS] != SHA204_STATUS_BYTE_WAKEUP)
|
||||
ret_code = SHA204_COMM_FAIL;
|
||||
else
|
||||
{
|
||||
if ((response[SHA204_RSP_SIZE_MIN - SHA204_CRC_SIZE] != 0x33)
|
||||
|| (response[SHA204_RSP_SIZE_MIN + 1 - SHA204_CRC_SIZE] != 0x43))
|
||||
ret_code = SHA204_BAD_CRC;
|
||||
}
|
||||
if (ret_code != SHA204_SUCCESS)
|
||||
delay(SHA204_COMMAND_EXEC_MAX);
|
||||
|
||||
return ret_code;
|
||||
}
|
||||
|
||||
uint8_t atsha204_execute(uint8_t op_code, uint8_t param1, uint16_t param2,
|
||||
uint8_t datalen1, uint8_t *data1, uint8_t tx_size, uint8_t *tx_buffer, uint8_t rx_size, uint8_t *rx_buffer)
|
||||
{
|
||||
uint8_t poll_delay, poll_timeout, response_size;
|
||||
uint8_t *p_buffer;
|
||||
uint8_t len;
|
||||
(void)tx_size;
|
||||
|
||||
|
||||
// Supply delays and response size.
|
||||
switch (op_code)
|
||||
switch (op_code)
|
||||
{
|
||||
case SHA204_GENDIG:
|
||||
poll_delay = GENDIG_DELAY;
|
||||
@@ -538,43 +571,30 @@ uint8_t ATSHA204Class::sha204m_execute(uint8_t op_code, uint8_t param1, uint16_t
|
||||
&rx_buffer[0], poll_delay, poll_timeout);
|
||||
}
|
||||
|
||||
/* CRC Calculator and Checker */
|
||||
|
||||
void ATSHA204Class::sha204c_calculate_crc(uint8_t length, uint8_t *data, uint8_t *crc)
|
||||
uint8_t atsha204_getSerialNumber(uint8_t * response)
|
||||
{
|
||||
uint8_t counter;
|
||||
uint16_t crc_register = 0;
|
||||
uint16_t polynom = 0x8005;
|
||||
uint8_t shift_register;
|
||||
uint8_t data_bit, crc_bit;
|
||||
uint8_t readCommand[READ_COUNT];
|
||||
uint8_t readResponse[READ_4_RSP_SIZE];
|
||||
|
||||
for (counter = 0; counter < length; counter++)
|
||||
/* read from bytes 0->3 of config zone */
|
||||
uint8_t returnCode = sha204m_read(readCommand, readResponse, SHA204_ZONE_CONFIG, ADDRESS_SN03);
|
||||
if (!returnCode)
|
||||
{
|
||||
for (shift_register = 0x01; shift_register > 0x00; shift_register <<= 1)
|
||||
{
|
||||
data_bit = (data[counter] & shift_register) ? 1 : 0;
|
||||
crc_bit = crc_register >> 15;
|
||||
for (int i=0; i<4; i++) // store bytes 0-3 into respones array
|
||||
response[i] = readResponse[SHA204_BUFFER_POS_DATA+i];
|
||||
|
||||
// Shift CRC to the left by 1.
|
||||
crc_register <<= 1;
|
||||
/* read from bytes 8->11 of config zone */
|
||||
returnCode = sha204m_read(readCommand, readResponse, SHA204_ZONE_CONFIG, ADDRESS_SN47);
|
||||
|
||||
if ((data_bit ^ crc_bit) != 0)
|
||||
crc_register ^= polynom;
|
||||
for (int i=4; i<8; i++) // store bytes 4-7 of SN into response array
|
||||
response[i] = readResponse[SHA204_BUFFER_POS_DATA+(i-4)];
|
||||
|
||||
if (!returnCode)
|
||||
{ /* Finally if last two reads were successful, read byte 8 of the SN */
|
||||
returnCode = sha204m_read(readCommand, readResponse, SHA204_ZONE_CONFIG, ADDRESS_SN8);
|
||||
response[8] = readResponse[SHA204_BUFFER_POS_DATA]; // Byte 8 of SN should always be 0xEE
|
||||
}
|
||||
}
|
||||
crc[0] = (uint8_t) (crc_register & 0x00FF);
|
||||
crc[1] = (uint8_t) (crc_register >> 8);
|
||||
|
||||
return returnCode;
|
||||
}
|
||||
|
||||
uint8_t ATSHA204Class::sha204c_check_crc(uint8_t *response)
|
||||
{
|
||||
uint8_t crc[SHA204_CRC_SIZE];
|
||||
uint8_t count = response[SHA204_BUFFER_POS_COUNT];
|
||||
|
||||
count -= SHA204_CRC_SIZE;
|
||||
sha204c_calculate_crc(count, response, crc);
|
||||
|
||||
return (crc[0] == response[count] && crc[1] == response[count + 1])
|
||||
? SHA204_SUCCESS : SHA204_BAD_CRC;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#if !DOXYGEN
|
||||
#include "Arduino.h"
|
||||
|
||||
/* This is a scaled down variant of the ATSHA204 library, tweaked to meet the specific needs of MySensors. */
|
||||
/* This is a scaled down variant of the ATSHA204 library, tweaked to meet the specific needs of the MySensors library. */
|
||||
|
||||
/* Library return codes */
|
||||
#define SHA204_SUCCESS ((uint8_t) 0x00) //!< Function succeeded.
|
||||
@@ -239,31 +239,14 @@
|
||||
#define SHA204_PIN_READ() (*device_port_IN & device_pin)
|
||||
#endif
|
||||
|
||||
class ATSHA204Class
|
||||
{
|
||||
private:
|
||||
uint8_t device_pin;
|
||||
#ifdef ARDUINO_ARCH_AVR
|
||||
volatile uint8_t *device_port_DDR, *device_port_OUT, *device_port_IN;
|
||||
#endif
|
||||
void sha204c_calculate_crc(uint8_t length, uint8_t *data, uint8_t *crc);
|
||||
uint8_t sha204c_check_crc(uint8_t *response);
|
||||
void swi_set_signal_pin(uint8_t is_high);
|
||||
uint8_t swi_receive_bytes(uint8_t count, uint8_t *buffer);
|
||||
uint8_t swi_send_bytes(uint8_t count, uint8_t *buffer);
|
||||
uint8_t swi_send_byte(uint8_t value);
|
||||
uint8_t sha204p_receive_response(uint8_t size, uint8_t *response);
|
||||
uint8_t sha204m_read(uint8_t *tx_buffer, uint8_t *rx_buffer, uint8_t zone, uint16_t address);
|
||||
uint8_t sha204c_wakeup(uint8_t *response);
|
||||
uint8_t sha204c_resync(uint8_t size, uint8_t *response);
|
||||
uint8_t sha204c_send_and_receive(uint8_t *tx_buffer, uint8_t rx_size, uint8_t *rx_buffer, uint8_t execution_delay, uint8_t execution_timeout);
|
||||
public:
|
||||
ATSHA204Class(uint8_t pin); // Constructor
|
||||
void sha204c_sleep();
|
||||
uint8_t sha204m_execute(uint8_t op_code, uint8_t param1, uint16_t param2,
|
||||
uint8_t datalen1, uint8_t *data1, uint8_t tx_size, uint8_t *tx_buffer, uint8_t rx_size, uint8_t *rx_buffer);
|
||||
void getSerialNumber(uint8_t *response);
|
||||
};
|
||||
void atsha204_init(uint8_t pin);
|
||||
void atsha204_idle(void);
|
||||
void atsha204_sleep(void);
|
||||
uint8_t atsha204_wakeup(uint8_t *response);
|
||||
uint8_t atsha204_execute(uint8_t op_code, uint8_t param1, uint16_t param2,
|
||||
uint8_t datalen1, uint8_t *data1, uint8_t tx_size,
|
||||
uint8_t *tx_buffer, uint8_t rx_size, uint8_t *rx_buffer);
|
||||
uint8_t atsha204_getSerialNumber(uint8_t *response);
|
||||
|
||||
#endif
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user