Split out some more components to make core class cleaner

This commit is contained in:
Henrik Ekblad
2015-09-15 00:28:47 +02:00
parent ba92885064
commit 4f4f0605c0
21 changed files with 1096 additions and 933 deletions
+2 -2
View File
@@ -95,9 +95,9 @@
// but now can be used in any sensor node. Also the LEDs can now be
// disabled in the gateway.
//#define MY_WITH_LEDS_BLINKING
//#define MY_LEDS_BLINKING_FEATURE
// The following setting allows you to inverse the blinking feature WITH_LEDS_BLINKING
// The following setting allows you to inverse the blinking feature MY_LEDS_BLINKING_FEATURE
// When WITH_LEDS_BLINKING_INVERSE is enabled LEDSs are normally turned on and switches
// off when blinking
+49 -26
View File
@@ -45,6 +45,19 @@
#include "core/MyHwATMega328.cpp"
#endif
// LEDS
#if defined(MY_LEDS_BLINKING_FEATURE)
#include "core/MyLeds.cpp"
#else
#include "core/MyLeds.h"
#endif
// INCLUSION MODE
#if defined(MY_INCLUSION_MODE_FEATURE)
#include "core/MyInclusionMode.cpp"
#endif
// SIGNING
#if defined(MY_SIGNING_ATSHA204) || defined(MY_SIGNING_SOFT)
#include "MySigning.cpp"
@@ -60,42 +73,21 @@
#endif
#endif
// RADIO
#if defined(MY_RADIO_NRF24) || defined(MY_RADIO_RFM69)
#define MY_RADIO_FEATURE
// SOFTSPI
#ifdef MY_SOFTSPI
#if defined(ARDUINO_ARCH_ESP8266)
#error Soft SPI is not available on ESP8266
#endif
#include "drivers/AVR/DigitalIO/SoftI2cMaster.cpp"
#include "drivers/AVR/DigitalIO/PinIO.cpp"
#endif
// FLASH
#ifdef MY_OTA_FIRMWARE_FEATURE
#include "drivers/SPIFlash/SPIFlash.cpp"
SPIFlash _flash(MY_OTA_FLASH_SS, MY_OTA_FLASH_JDECID);
#endif
#if defined(MY_RADIO_NRF24)
#include "drivers/RF24/RF24.cpp"
#include "core/MyTransportNRF24.cpp"
#elif defined(MY_RADIO_RFM69)
#include "drivers/RFM69/RFM69.cpp"
#include "core/MyTransportRFM69.cpp"
#endif
#endif
// GATEWAY - TRANSPORT
#if defined(MY_GATEWAY_FEATURE)
// GATEWAY - COMMON FUNCTIONS
#include "core/MyGatewayTransport.cpp"
// GATEWAY - PROTOCOL
#if defined(MY_GATEWAY_PROTOCOL_DEFAULT)
#include "core/MyProtocolDefault.cpp"
#else
#error No gateway protocol specified!
#endif
// GATEWAY - CONFIGURATION
#if defined(MY_RADIO_FEATURE)
// We assume that a gateway having a radio also should act as repeater
#define MY_REPEATER_FEATURE
@@ -108,6 +100,7 @@
#endif
#if defined(MY_GATEWAY_W5100)
// GATEWAY - W5100
#include "drivers/AVR/Ethernet_W5100/utility/socket.cpp"
#include "drivers/AVR/Ethernet_W5100/utility/w5100.cpp"
#include "drivers/AVR/Ethernet_W5100/DNS.cpp"
@@ -126,6 +119,7 @@
// What do we do here?
#endif
#elif defined(MY_GATEWAY_ENC28J60)
// GATEWAY - ENC28J60
#undef MY_USE_UDP // Will not fit (or compile) on ENC28J60
#include "drivers/AVR/Ethernet_UIP/src/utility/uipethernet-conf.h"
#include "drivers/AVR/Ethernet_UIP/src/utility/uip-conf.h"
@@ -143,6 +137,7 @@
#include "core/MyGatewayTransportEthernet.cpp"
#elif defined(MY_GATEWAY_SERIAL)
// GATEWAY - SERIAL
#include "core/MyGatewayTransportSerial.cpp"
#endif
#else
@@ -175,6 +170,34 @@
#define MY_IS_GATEWAY false
#endif
// RADIO
#if defined(MY_RADIO_NRF24) || defined(MY_RADIO_RFM69)
#define MY_RADIO_FEATURE
// SOFTSPI
#ifdef MY_SOFTSPI
#if defined(ARDUINO_ARCH_ESP8266)
#error Soft SPI is not available on ESP8266
#endif
#include "drivers/AVR/DigitalIO/SoftI2cMaster.cpp"
#include "drivers/AVR/DigitalIO/PinIO.cpp"
#endif
// FLASH
#ifdef MY_OTA_FIRMWARE_FEATURE
#include "drivers/SPIFlash/SPIFlash.cpp"
SPIFlash _flash(MY_OTA_FLASH_SS, MY_OTA_FLASH_JDECID);
#endif
#include "core/MyTransport.cpp"
#if defined(MY_RADIO_NRF24)
#include "drivers/RF24/RF24.cpp"
#include "core/MyTransportNRF24.cpp"
#elif defined(MY_RADIO_RFM69)
#include "drivers/RFM69/RFM69.cpp"
#include "core/MyTransportRFM69.cpp"
#endif
#endif
#include "core/MyMessage.cpp"
#include "core/MySensorCore.cpp"
@@ -0,0 +1,56 @@
/**
* The MySensors Arduino library handles the wireless radio link and protocol
* between your home built sensors/actuators and HA controller of choice.
* The sensors forms a self healing radio network with optional repeaters. Each
* repeater and gateway builds a routing tables in EEPROM which keeps track of the
* network topology allowing messages to be routed to nodes.
*
* Created by Tomas Hozza <thozza@gmail.com>
* Copyright (C) 2015 Tomas Hozza
* Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
*
* Documentation: http://www.mysensors.org
* Support Forum: http://forum.mysensors.org
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*/
#include "MyGatewayTransport.h"
inline void gatewayTransportProcess() {
if (gatewayTransportAvailable()) {
MyMessage &_msg = gatewayTransportReceive();
if (_msg.destination == GATEWAY_ADDRESS) {
// Check if sender requests an ack back.
if (mGetRequestAck(_msg)) {
// Copy message
_msgTmp = _msg;
mSetRequestAck(_msgTmp, false); // Reply without ack flag (otherwise we would end up in an eternal loop)
mSetAck(_msgTmp, true);
_msgTmp.sender = _nc.nodeId;
_msgTmp.destination = _msg.sender;
gatewayTransportSend(_msgTmp);
}
if (mGetCommand(_msg) == C_INTERNAL) {
if (_msg.type == I_VERSION) {
// Request for version. Create the response
gatewayTransportSend(buildGw(_msg, I_VERSION).set(LIBRARY_VERSION));
#ifdef MY_INCLUSION_MODE_FEATURE
} else if (_msg.type == I_INCLUSION_MODE) {
// Request to change inclusion mode
inclusionModeSet(atoi(_msg.data) == 1);
#endif
} else {
processInternalMessages();
}
} else {
// Call incoming message callback if available
if (_msgCallback != NULL) {
_msgCallback(_msg);
}
}
}
}
}
+19 -5
View File
@@ -23,19 +23,33 @@
#include "MyConfig.h"
#include "MyMessage.h"
#include "MyProtocol.h"
#include "MySensorCore.h"
// Common gateway functions
void gatewayTransportProcess();
// Gateway "interface" functions
// initialize the driver
bool gatewayTransportBegin();
/**
* initialize the driver
*/
bool gatewayTransportInit();
// Send message to controller
/**
* Send message to controller
*/
bool gatewayTransportSend(MyMessage &message);
// Check if a new message is available from controller
/*
* Check if a new message is available from controller
*/
bool gatewayTransportAvailable();
// Pick up last message received from controller
/*
* Pick up last message received from controller
*/
MyMessage& gatewayTransportReceive();
#endif /* MyGatewayTransportEthernet_h */
@@ -35,7 +35,7 @@ MyMessage _ethernetMsg;
#endif /* USE_UDP */
bool gatewayTransportBegin() {
bool gatewayTransportInit() {
#ifdef MY_IP_ADDRESS
Ethernet.begin(_ethernetGatewayMAC, _ethernetGatewayIP);
#else
@@ -26,7 +26,7 @@ MyMessage _serialMsg;
bool gatewayTransportBegin() {
bool gatewayTransportInit() {
return true;
}
@@ -0,0 +1,59 @@
/**
* The MySensors Arduino library handles the wireless radio link and protocol
* between your home built sensors/actuators and HA controller of choice.
* The sensors forms a self healing radio network with optional repeaters. Each
* repeater and gateway builds a routing tables in EEPROM which keeps track of the
* network topology allowing messages to be routed to nodes.
*
* Created by Henrik Ekblad <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 "MyInclusionMode.h"
unsigned long _inclusionStartTime;
bool _inclusionMode;
inline void inclusionInit() {
_inclusionMode = false;
#if defined(MY_INCLUSION_BUTTON_FEATURE)
// Setup digital in that triggers inclusion mode
pinMode(MY_INCLUSION_MODE_BUTTON_PIN, INPUT);
digitalWrite(MY_INCLUSION_MODE_BUTTON_PIN, HIGH);
#endif
}
void inclusionModeSet(boolean newMode) {
if (newMode != inclusionMode) {
_inclusionMode = newMode;
// Send back mode change to controller
gatewayTransportSend(buildGw(_msg, I_INCLUSION_MODE).set(_inclusionMode?1:0));
if (_inclusionMode) {
_inclusionStartTime = hwMillis();
}
}
}
inline void inclusionProcess() {
#ifdef MY_INCLUSION_BUTTON_FEATURE
if (!inclusionMode && digitalRead(MY_INCLUSION_MODE_BUTTON_PIN) == LOW) {
// Start inclusion mode
inclusionModeSet(true);
}
#endif
if (_inclusionMode && hwMillis()-_inclusionStartTime>MY_INCLUSION_MODE_DURATION*1000L) {
// inclusionTimeInMinutes minute(s) has passed.. stop inclusion mode
inclusionModeSet(false);
}
}
@@ -0,0 +1,35 @@
/**
* 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.
*/
#ifndef MyInclusionMode_h
#define MyInclusionMode_h
#include "MySensorCore.h"
unsigned long inclusionStartTime;
bool inclusionMode;
extern bool gatewayTransportSend(MyMessage &message);
void inclusionInit();
void inclusionModeSet(boolean newMode);
void inclusionProcess();
#endif
+99
View File
@@ -0,0 +1,99 @@
/**
* The MySensors Arduino library handles the wireless radio link and protocol
* between your home built sensors/actuators and HA controller of choice.
* The sensors forms a self healing radio network with optional repeaters. Each
* repeater and gateway builds a routing tables in EEPROM which keeps track of the
* network topology allowing messages to be routed to nodes.
*
* Created by Henrik Ekblad <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"
// these variables don't need to be volatile, since we are not using interrupts
uint8_t _countRx;
uint8_t _countTx;
uint8_t _countErr;
unsigned long _blink_next_time;
inline void ledsInit() {
// Setup led pins
pinMode(MY_DEFAULT_RX_LED_PIN, OUTPUT);
pinMode(MY_DEFAULT_TX_LED_PIN, OUTPUT);
pinMode(MY_DEFAULT_ERR_LED_PIN, OUTPUT);
// Set initial state of leds
hwDigitalWrite(MY_DEFAULT_RX_LED_PIN, LED_OFF);
hwDigitalWrite(MY_DEFAULT_TX_LED_PIN, LED_OFF);
hwDigitalWrite(MY_DEFAULT_ERR_LED_PIN, LED_OFF);
// initialize counters
_countRx = 0;
_countTx = 0;
_countErr = 0;
}
inline void ledsProcess() {
// Just return if it is not the time...
// http://playground.arduino.cc/Code/TimingRollover
if ((long)(hwMillis() - _blink_next_time) < 0)
return;
else
_blink_next_time = hwMillis() + MY_DEFAULT_LED_BLINK_PERIOD;
// do the actual blinking
if(_countRx && _countRx != 255) {
// switch led on
hwDigitalWrite(MY_DEFAULT_RX_LED_PIN, LED_ON);
}
else if(!_countRx) {
// switching off
hwDigitalWrite(MY_DEFAULT_RX_LED_PIN, LED_OFF);
}
if(_countRx != 255)
--_countRx;
if(_countTx && _countTx != 255) {
// switch led on
hwDigitalWrite(MY_DEFAULT_TX_LED_PIN, LED_ON);
}
else if(!_countTx) {
// switching off
hwDigitalWrite(MY_DEFAULT_TX_LED_PIN, LED_OFF);
}
if(_countTx != 255)
--_countTx;
if(_countErr && _countErr != 255) {
// switch led on
hwDigitalWrite(MY_DEFAULT_ERR_LED_PIN, LED_ON);
}
else if(!_countErr) {
// switching off
hwDigitalWrite(MY_DEFAULT_ERR_LED_PIN, LED_OFF);
}
if(_countErr != 255)
--_countErr;
}
void ledsBlinkRx(uint8_t cnt) {
if(_countRx == 255) { _countRx = cnt; }
}
void ledsBlinkTx(uint8_t cnt) {
if(_countTx == 255) { _countTx = cnt; }
}
void ledsBlinkErr(uint8_t cnt) {
if(_countErr == 255) { _countErr = cnt; }
}
+54
View File
@@ -0,0 +1,54 @@
/**
* 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.
*/
#ifndef MyLeds_h
#define MyLeds_h
#ifdef WITH_LEDS_BLINKING_INVERSE
#define LED_ON 0x1
#define LED_OFF 0x0
#else
#define LED_ON 0x0
#define LED_OFF 0x1
#endif
#ifdef MY_LEDS_BLINKING_FEATURE
#define ledBlinkTx(x,...) ledsBlinkTx(x)
#define ledBlinkRx(x,...) ledsBlinkRx(x)
#define ledBlinkErr(x,...) ledsBlinkErr(x)
/**
* Blink with LEDs
* @param cnt how many blink cycles to keep the LED on. Default cycle is 300ms
*/
void ledsInit();
void ledsBlinkRx(uint8_t cnt);
void ledsBlinkTx(uint8_t cnt);
void ledsBlinkErr(uint8_t cnt);
void ledsProcess(); // do the actual blinking
#else
// Remove led functions if feature is disabled
#define ledBlinkTx(x,...)
#define ledBlinkRx(x,...)
#define ledBlinkErr(x,...)
#endif
#endif
+43 -777
View File
@@ -17,41 +17,12 @@
* version 2 as published by the Free Software Foundation.
*/
#include "MySensorCore.h"
NodeConfig _nc; // Essential settings for node to work
ControllerConfig _cc; // Configuration coming from controller
#ifdef MY_OTA_FIRMWARE_FEATURE
NodeFirmwareConfig _fc;
bool _fwUpdateOngoing;
unsigned long _fwLastRequestTime;
uint16_t _fwBlock;
uint8_t _fwRetry;
#endif
#ifdef MY_RADIO_FEATURE
bool _autoFindParent;
uint8_t _failedTransmissions;
#endif
NodeConfig _nc; // Essential settings for node to work
MyMessage _msg; // Buffer for incoming messages.
MyMessage _msgTmp ; // Buffer for temporary messages (acks and nonces among others).
#ifdef MY_LEDS_BLINKING_FEATURE
// these variables don't need to be volatile, since we are not using interrupts
uint8_t _countRx;
uint8_t _countTx;
uint8_t _countErr;
unsigned long _blink_next_time;
#endif
#ifdef MY_SIGNING_FEATURE
uint16_t _doSign[16]; // Bitfield indicating which sensors require signed communication
MyMessage _msgSign; // Buffer for message to sign.
#endif
MyMessage _msgTmp; // Buffer for temporary messages (acks and nonces among others).
#ifdef MY_DEBUG
char convBuf[MAX_PAYLOAD*2+1];
@@ -62,124 +33,30 @@ void (*_timeCallback)(unsigned long); // Callback for requested time messages
void (*_msgCallback)(const MyMessage &); // Callback for incoming messages from other nodes and gateway.
#define DISTANCE_INVALID (0xFF)
void process() {
hwWatchdogReset();
#ifdef MY_SIGNING_FEATURE
// Macros for manipulating signing requirement table
#define DO_SIGN(node) (node == 0 ? (~_doSign[0]&1) : (~_doSign[node>>4]&(node%16)))
#define SET_SIGN(node) (node == 0 ? (_doSign[0]&=~1) : (_doSign[node>>4]&=~(node%16)))
#define CLEAR_SIGN(node) (node == 0 ? (_doSign[0]|=1) : (_doSign[node>>4]|=(node%16)))
#endif
#if defined (MY_LEDS_BLINKING_FEATURE)
ledsProcess();
#endif
// Inline function and macros
static inline MyMessage& build(MyMessage &msg, uint8_t sender, uint8_t destination, uint8_t sensor, uint8_t command, uint8_t type, bool enableAck) {
msg.sender = sender;
msg.destination = destination;
msg.sensor = sensor;
msg.type = type;
mSetCommand(msg,command);
mSetRequestAck(msg,enableAck);
mSetAck(msg,false);
return msg;
#if defined(MY_INCLUSION_MODE_FEATURE)
inclusionProcess();
#endif
#if defined(MY_GATEWAY_FEATURE)
gatewayTransportProcess();
#endif
#if defined(MY_RADIO_FEATURE)
transportProcess();
#endif
}
static inline MyMessage& buildGw(MyMessage &msg, uint8_t type) {
msg.sender = GATEWAY_ADDRESS;
msg.destination = GATEWAY_ADDRESS;
msg.sensor = 0;
msg.type = type;
mSetCommand(msg, C_INTERNAL);
mSetRequestAck(msg, false);
mSetAck(msg, false);
return msg;
}
static inline bool isValidParent( const uint8_t parent ) {
return parent != AUTO;
}
static inline bool isValidDistance( const uint8_t distance ) {
return distance != DISTANCE_INVALID;
}
#ifdef MY_OTA_FIRMWARE_FEATURE
// do a crc16 on the whole received firmware
bool isValidFirmware() {
// init crc
uint16_t crc = ~0;
for (uint16_t i = 0; i < _fc.blocks * FIRMWARE_BLOCK_SIZE; ++i) {
crc ^= _flash.readByte(i + FIRMWARE_START_OFFSET);
for (int8_t j = 0; j < 8; ++j) {
if (crc & 1)
crc = (crc >> 1) ^ 0xA001;
else
crc = (crc >> 1);
}
}
return crc == _fc.crc;
}
#endif
#ifdef MY_LEDS_BLINKING_FEATURE
void handleLedsBlinking() {
static unsigned long next_time = hwMillis() + MY_DEFAULT_LED_BLINK_PERIOD;
// Just return if it is not the time...
// http://playground.arduino.cc/Code/TimingRollover
if ((long)(hwMillis() - _blink_next_time) < 0)
return;
else
_blink_next_time = hwMillis() + ledBlinkPeriod;
// do the actual blinking
if(_countRx && _countRx != 255) {
// switch led on
hwDigitalWrite(MY_DEFAULT_RX_LED_PIN, LED_ON);
}
else if(!_countRx) {
// switching off
hwDigitalWrite(MY_DEFAULT_RX_LED_PIN, LED_OFF);
}
if(_countRx != 255)
--_countRx;
if(_countTx && _countTx != 255) {
// switch led on
hwDigitalWrite(MY_DEFAULT_TX_LED_PIN, LED_ON);
}
else if(!_countTx) {
// switching off
hwDigitalWrite(MY_DEFAULT_TX_LED_PIN, LED_OFF);
}
if(_countTx != 255)
--_countTx;
if(_countErr && _countErr != 255) {
// switch led on
hwDigitalWrite(MY_DEFAULT_ERR_LED_PIN, LED_ON);
}
else if(!_countErr) {
// switching off
hwDigitalWrite(MY_DEFAULT_ERR_LED_PIN, LED_OFF);
}
if(_countErr != 255)
--_countErr;
}
void rxBlink(uint8_t cnt) {
if(_countRx == 255) { _countRx = cnt; }
}
void txBlink(uint8_t cnt) {
if(_countTx == 255) { _countTx = cnt; }
}
void errBlink(uint8_t cnt) {
if(_countErr == 255) { _countErr = cnt; }
}
#endif
void dataCallback(void (*msgCallback)(const MyMessage &)) {
@@ -187,29 +64,21 @@ void dataCallback(void (*msgCallback)(const MyMessage &)) {
}
void begin() {
#if defined(MY_INCLUSION_MODE_FEATURE)
inclusionMode = false;
#endif
#ifndef MY_DISABLED_SERIAL
#if defined(MY_DISABLED_SERIAL)
hwInit();
#endif
#ifdef MY_GATEWAY_FEATURE
#if defined(MY_INCLUSION_MODE_FEATURE) && defined(MY_INCLUSION_BUTTON_FEATURE)
// Setup digital in that triggers inclusion mode
pinMode(MY_INCLUSION_MODE_BUTTON_PIN, INPUT);
digitalWrite(MY_INCLUSION_MODE_BUTTON_PIN, HIGH);
#if defined(MY_GATEWAY_FEATURE)
#if defined(MY_INCLUSION_BUTTON_FEATURE)
inclusionInit();
#endif
// initialize the transport driver
if (!gatewayTransportBegin()) {
// initialize the transport driver
if (!gatewayTransportInit()) {
debug(PSTR("transport driver init fail\n"));
while(1); // Nothing more we can do
}
#endif
#if defined(MY_GATEWAY_FEATURE)
gatewayTransportSend(buildGw(_msg, I_GATEWAY_READY).set("Gateway startup complete."));
#endif
@@ -223,27 +92,14 @@ void begin() {
}
#endif
#ifdef MY_SIGNING_FEATURE
#if defined(MY_SIGNING_FEATURE)
// Read out the signing requirements from EEPROM
hwReadConfigBlock((void*)_doSign, (void*)EEPROM_SIGNING_REQUIREMENT_TABLE_ADDRESS, sizeof(_doSign));
#endif
#ifdef MY_LEDS_BLINKING_FEATURE
// Setup led pins
pinMode(MY_DEFAULT_RX_LED_PIN, OUTPUT);
pinMode(MY_DEFAULT_TX_LED_PIN, OUTPUT);
pinMode(MY_DEFAULT_ERR_LED_PIN, OUTPUT);
// Set initial state of leds
hwDigitalWrite(MY_DEFAULT_RX_LED_PIN, LED_OFF);
hwDigitalWrite(MY_DEFAULT_TX_LED_PIN, LED_OFF);
hwDigitalWrite(MY_DEFAULT_ERR_LED_PIN, LED_OFF);
// initialize counters
_countRx = 0;
_countTx = 0;
_countErr = 0;
#endif
#if defined(MY_LEDS_BLINKING_FEATURE)
ledsInit();
#endif
// Read latest received controller configuration from EEPROM
hwReadConfigBlock((void*)&_cc, (void*)EEPROM_CONTROLLER_CONFIG_ADDRESS, sizeof(ControllerConfig));
@@ -277,7 +133,7 @@ void begin() {
_nc.distance = 1;
} else if (!isValidParent(_nc.parentNodeId)) {
// Auto find parent, but parent in eeprom is invalid. Try find one.
findParentNode();
transportFindParentNode();
}
if (MY_NODE_ID != AUTO) {
@@ -287,12 +143,12 @@ void begin() {
hwWriteConfig(EEPROM_NODE_ID_ADDRESS, MY_NODE_ID);
} else if (_nc.nodeId == AUTO && isValidParent(_nc.parentNodeId)) {
// Try to fetch node-id from gateway
requestNodeId();
transportRequestNodeId();
}
#endif
#if defined(MY_RADIO_FEATURE)
setupNode();
transportSetupNode();
#endif
debug(PSTR("%s started, id=%d, parent=%d, distance=%d\n"), MY_NODE_TYPE, _nc.nodeId, _nc.parentNodeId, _nc.distance);
}
@@ -308,271 +164,23 @@ ControllerConfig getConfig() {
return _cc;
}
#if defined(MY_RADIO_FEATURE)
void requestNodeId() {
debug(PSTR("req id\n"));
transportSetAddress(_nc.nodeId);
build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_ID_REQUEST, false).set("");
sendWrite(_nc.parentNodeId, _msg);
wait(2000);
}
#endif
#if defined(MY_RADIO_FEATURE)
void setupNode() {
// Open reading pipe for messages directed to this node (set write pipe to same)
transportSetAddress(_nc.nodeId);
// Present node and request config
#ifndef MY_GATEWAY_FEATURE
if (_nc.nodeId != AUTO) {
#ifdef MY_SIGNING_FEATURE
// Notify gateway (and possibly controller) about the signing preferences of this node
sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_REQUEST_SIGNING, false).set(signer.requestSignatures()));
// If we do require signing, wait for the gateway to tell us how it prefer us to transmit our messages
if (signer.requestSignatures()) {
wait(2000);
}
#endif
// Send presentation for this radio node
#ifdef MY_REPEATER_FEATURE
present(NODE_SENSOR_ID, S_ARDUINO_REPEATER_NODE);
#else
present(NODE_SENSOR_ID, S_ARDUINO_NODE);
#endif
// Send a configuration exchange request to controller
// Node sends parent node. Controller answers with latest node configuration
// which is picked up in process()
sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_CONFIG, false).set(_nc.parentNodeId));
// Wait configuration reply.
wait(2000);
#ifdef MY_OTA_FIRMWARE_FEATURE
RequestFirmwareConfig *reqFWConfig = (RequestFirmwareConfig *)_msg.data;
mSetLength(_msg, sizeof(RequestFirmwareConfig));
mSetCommand(_msg, C_STREAM);
mSetPayloadType(_msg,P_CUSTOM);
// copy node settings to reqFWConfig
memcpy(reqFWConfig,&_fc,sizeof(NodeFirmwareConfig));
// add bootloader information
reqFWConfig->BLVersion = MY_OTA_BOOTLOADER_VERSION;
_fwUpdateOngoing = false;
sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_STREAM, ST_FIRMWARE_CONFIG_REQUEST, false));
#endif
}
#endif
}
#endif
#if defined(MY_RADIO_FEATURE)
void findParentNode() {
static boolean findingParentNode = false;
if (findingParentNode)
return;
findingParentNode = true;
_failedTransmissions = 0;
// Set distance to max
_nc.distance = 255;
// Send ping message to BROADCAST_ADDRESS (to which all relaying nodes and gateway listens and should reply to)
debug(PSTR("find parent\n"));
build(_msg, _nc.nodeId, BROADCAST_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_FIND_PARENT, false).set("");
// Write msg, but suppress recursive parent search
sendWrite(BROADCAST_ADDRESS, _msg);
// Wait for ping response.
wait(2000);
findingParentNode = false;
}
#endif
boolean sendRoute(MyMessage &message) {
#if defined(MY_GATEWAY_FEATURE)
if (message.destination == _nc.nodeId) {
// This is a message sent from a sensor attached on the gateway node.
// Pass it directly to the gateway transport layer.
#ifdef MY_LEDS_BLINKING_FEATURE
txBlink(1);
#endif
ledBlinkTx(1);
return gatewayTransportSend(message);
}
#endif
#if defined(MY_RADIO_FEATURE)
uint8_t sender = message.sender;
uint8_t dest = message.destination;
uint8_t last = message.last;
bool ok;
// If we still don't have any parent id, re-request and skip this message.
if (_nc.parentNodeId == AUTO) {
findParentNode();
#ifdef MY_LEDS_BLINKING_FEATURE
errBlink(1);
#endif
return false;
}
// If we still don't have any node id, re-request and skip this message.
if (_nc.nodeId == AUTO) {
requestNodeId();
#ifdef MY_LEDS_BLINKING_FEATURE
errBlink(1);
#endif
return false;
}
mSetVersion(message, PROTOCOL_VERSION);
#ifdef MY_SIGNING_FEATURE
// If destination is known to require signed messages and we are the sender, sign this message unless it is an ACK or a handshake message
if (DO_SIGN(message.destination) && message.sender == _nc.nodeId && !mGetAck(message) && mGetLength(message) &&
(mGetCommand(message) != C_INTERNAL ||
(message.type != I_GET_NONCE && message.type != I_GET_NONCE_RESPONSE && message.type != I_REQUEST_SIGNING &&
message.type != I_ID_REQUEST && message.type != I_ID_RESPONSE &&
message.type != I_FIND_PARENT && message.type != I_FIND_PARENT_RESPONSE))) {
bool signOk = false;
// Send nonce-request
if (!sendRoute(build(_msgTmp, _nc.nodeId, message.destination, message.sensor, C_INTERNAL, I_GET_NONCE, false).set(""))) {
debug(PSTR("nonce tr err\n"));
return false;
}
// We have to wait for the nonce to arrive before we can sign our original message
// Other messages could come in-between. We trust process() takes care of them
unsigned long enter = hwMillis();
_msgSign = message; // Copy the message to sign since message buffer might be touched in process()
while (hwMillis() - enter < MY_VERIFICATION_TIMEOUT_MS) {
if (process()) {
if (mGetCommand(getLastMessage()) == C_INTERNAL && getLastMessage().type == I_GET_NONCE_RESPONSE) {
// Proceed with signing if nonce has been received
if (signer.putNonce(getLastMessage()) && signer.signMsg(_msgSign)) {
message = _msgSign; // Write the signed message back
signOk = true;
}
break;
}
}
}
if (hwMillis() - enter > MY_VERIFICATION_TIMEOUT_MS) {
debug(PSTR("nonce tmo\n"));
#ifdef MY_LEDS_BLINKING_FEATURE
errBlink(1);
#endif
return false;
}
if (!signOk) {
debug(PSTR("sign fail\n"));
#ifdef MY_LEDS_BLINKING_FEATURE
errBlink(1);
#endif
return false;
}
// After this point, only the 'last' member of the message structure is allowed to be altered if the message has been signed,
// or signature will become invalid and the message rejected by the receiver
} else mSetSigned(message, 0); // Message is not supposed to be signed, make sure it is marked unsigned
#endif
if (dest == GATEWAY_ADDRESS || !MY_IS_REPEATER) {
// Store this address in routing table (if repeater)
#if defined (MY_IS_REPEATER)
hwWriteConfig(EEPROM_ROUTES_ADDRESS+sender, last);
#endif
// If destination is the gateway or if we aren't a repeater, let
// our parent take care of the message
ok = sendWrite(_nc.parentNodeId, message);
} else {
// Relay the message
uint8_t route = hwReadConfig(EEPROM_ROUTES_ADDRESS+dest);
if (route > GATEWAY_ADDRESS && route < BROADCAST_ADDRESS) {
// This message should be forwarded to a child node. If we send message
// to this nodes pipe then all children will receive it because the are
// all listening to this nodes pipe.
//
// +----B
// -A
// +----C------D
//
// We're node C, Message comes from A and has destination D
//
// Message destination is not gateway and is in routing table for this node.
// Send it downstream
return sendWrite(route, message);
} else if (sender == GATEWAY_ADDRESS && dest == BROADCAST_ADDRESS) {
// Node has not yet received any id. We need to send it
// by doing a broadcast sending,
return sendWrite(BROADCAST_ADDRESS, message);
} else if (MY_IS_GATEWAY) {
// Destination isn't in our routing table and isn't a broadcast address
// Nothing to do here
return false;
} else {
// A message comes from a child node and we have no
// route for it.
//
// +----B
// -A
// +----C------D <-- Message comes from D
//
// We're node C
//
// Message should be passed to node A (this nodes relay)
// This message should be routed back towards sensor net gateway
ok = sendWrite(_nc.parentNodeId, message);
// Add this child to our "routing table" if it not already exist
hwWriteConfig(EEPROM_ROUTES_ADDRESS+sender, last);
}
}
if (!ok) {
// Failure when sending to parent node. The parent node might be down and we
// need to find another route to gateway.
#ifdef MY_LEDS_BLINKING_FEATURE
errBlink(1);
#endif
_failedTransmissions++;
if (_autoFindParent && _failedTransmissions > SEARCH_FAILURES) {
findParentNode();
}
} else {
_failedTransmissions = 0;
}
return ok;
transportSendRoute(message);
#else
return false;
#endif
}
#ifdef MY_RADIO_FEATURE
boolean sendWrite(uint8_t to, MyMessage &message) {
mSetVersion(message, PROTOCOL_VERSION);
uint8_t length = mGetSigned(message) ? MAX_MESSAGE_LENGTH : mGetLength(message);
message.last = _nc.nodeId;
#ifdef MY_LEDS_BLINKING_FEATURE
txBlink(1);
#endif
bool ok = transportSend(to, &message, min(MAX_MESSAGE_LENGTH, HEADER_SIZE + length));
debug(PSTR("send: %d-%d-%d-%d s=%d,c=%d,t=%d,pt=%d,l=%d,sg=%d,st=%s:%s\n"),
message.sender,message.last, to, message.destination, message.sensor, mGetCommand(message), message.type,
mGetPayloadType(message), mGetLength(message), mGetSigned(message), to==BROADCAST_ADDRESS ? "bc" : (ok ? "ok":"fail"), message.getString(convBuf));
return ok;
}
#endif
bool send(MyMessage &message, bool enableAck) {
message.sender = _nc.nodeId;
mSetCommand(message,C_SET);
@@ -611,320 +219,7 @@ void requestTime(void (* timeCallback)(unsigned long)) {
sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_TIME, false).set(""));
}
void process() {
hwWatchdogReset();
#ifdef MY_LEDS_BLINKING_FEATURE
handleLedsBlinking();
#endif
#ifdef MY_INCLUSION_MODE_FEATURE
checkInclusionMode();
#endif
#if defined (MY_GATEWAY_FEATURE)
if (gatewayTransportAvailable()) {
MyMessage &_msg = gatewayTransportReceive();
if (_msg.destination == GATEWAY_ADDRESS) {
// Check if sender requests an ack back.
if (mGetRequestAck(_msg)) {
// Copy message
_msgTmp = _msg;
mSetRequestAck(_msgTmp, false); // Reply without ack flag (otherwise we would end up in an eternal loop)
mSetAck(_msgTmp, true);
_msgTmp.sender = _nc.nodeId;
_msgTmp.destination = _msg.sender;
gatewayTransportSend(_msgTmp);
}
if (mGetCommand(_msg) == C_INTERNAL) {
if (_msg.type == I_VERSION) {
// Request for version. Create the response
gatewayTransportSend(buildGw(_msg, I_VERSION).set(LIBRARY_VERSION));
#ifdef MY_INCLUSION_MODE_FEATURE
} else if (_msg.type == I_INCLUSION_MODE) {
// Request to change inclusion mode
setInclusionMode(atoi(_msg.data) == 1);
#endif
} else {
processInternalMessages();
}
} else {
// Call incoming message callback if available
if (_msgCallback != NULL) {
_msgCallback(_msg);
}
}
}
}
#endif
#if defined(MY_RADIO_FEATURE)
uint8_t to = 0;
if (!transportAvailable(&to))
{
#ifdef MY_OTA_FIRMWARE_FEATURE
unsigned long enter = hwMillis();
if (_fwUpdateOngoing && (enter - _fwLastRequestTime > MY_OTA_RETRY_DELAY)) {
if (!_fwRetry) {
debug(PSTR("fw upd fail\n"));
// Give up. We have requested MY_OTA_RETRY times without any packet in return.
_fwUpdateOngoing = false;
#ifdef MY_LEDS_BLINKING_FEATURE
errBlink(1);
#endif
return;
}
_fwRetry--;
_fwLastRequestTime = enter;
// Time to (re-)request firmware block from controller
RequestFWBlock *firmwareRequest = (RequestFWBlock *)_msg.data;
mSetLength(_msg, sizeof(RequestFWBlock));
firmwareRequest->type = _fc.type;
firmwareRequest->version = _fc.version;
firmwareRequest->block = (_fwBlock - 1);
sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_STREAM, ST_FIRMWARE_REQUEST, false));
}
#endif
return;
}
#ifdef MY_SIGNING_FEATURE
(void)signer.checkTimer(); // Manage signing timeout
#endif
uint8_t len = transportReceive((uint8_t *)&_msg);
(void)len; //until somebody makes use of 'len'
#ifdef MY_LEDS_BLINKING_FEATURE
rxBlink(1);
#endif
#ifdef MY_SIGNING_FEATURE
// Before processing message, reject unsigned messages if signing is required and check signature (if it is signed and addressed to us)
// Note that we do not care at all about any signature found if we do not require signing, nor do we care about ACKs (they are never signed)
if (signer.requestSignatures() && _msg.destination == _nc.nodeId && mGetLength(_msg) && !mGetAck(_msg) &&
(mGetCommand(_msg) != C_INTERNAL ||
(_msg.type != I_GET_NONCE_RESPONSE && _msg.type != I_GET_NONCE && _msg.type != I_REQUEST_SIGNING &&
_msg.type != I_ID_REQUEST && _msg.type != I_ID_RESPONSE &&
_msg.type != I_FIND_PARENT && _msg.type != I_FIND_PARENT_RESPONSE))) {
if (!mGetSigned(_msg)) {
// Got unsigned message that should have been signed
debug(PSTR("no sign\n"));
#ifdef MY_LEDS_BLINKING_FEATURE
errBlink(1);
#endif
return;
}
else if (!signer.verifyMsg(_msg)) {
debug(PSTR("verify fail\n"));
#ifdef MY_LEDS_BLINKING_FEATURE
errBlink(1);
#endif
return; // This signed message has been tampered with!
}
}
#endif
// Add string termination, good if we later would want to print it.
_msg.data[mGetLength(_msg)] = '\0';
debug(PSTR("read: %d-%d-%d s=%d,c=%d,t=%d,pt=%d,l=%d,sg=%d:%s\n"),
_msg.sender, _msg.last, _msg.destination, _msg.sensor, mGetCommand(_msg), _msg.type, mGetPayloadType(_msg), mGetLength(_msg), mGetSigned(_msg), _msg.getString(convBuf));
mSetSigned(_msg,0); // Clear the sign-flag now as verification (and debug printing) is completed
if(!(mGetVersion(_msg) == PROTOCOL_VERSION)) {
debug(PSTR("ver mismatch\n"));
#ifdef MY_LEDS_BLINKING_FEATURE
errBlink(1);
#endif
return;
}
uint8_t command = mGetCommand(_msg);
uint8_t type = _msg.type;
uint8_t sender = _msg.sender;
uint8_t last = _msg.last;
uint8_t destination = _msg.destination;
if (destination == _nc.nodeId) {
// This message is addressed to this node
if (MY_IS_REPEATER && last != _nc.parentNodeId) {
// Message is from one of the child nodes. Add it to routing table.
hwWriteConfig(EEPROM_ROUTES_ADDRESS+sender, last);
}
// Check if sender requests an ack back.
if (mGetRequestAck(_msg)) {
// Copy message
_msgTmp = _msg;
mSetRequestAck(_msgTmp,false); // Reply without ack flag (otherwise we would end up in an eternal loop)
mSetAck(_msgTmp,true);
_msgTmp.sender = _nc.nodeId;
_msgTmp.destination = _msg.sender;
sendRoute(_msgTmp);
}
if (command == C_INTERNAL) {
if (type == I_FIND_PARENT_RESPONSE) {
if (_autoFindParent) {
// We've received a reply to a FIND_PARENT message. Check if the distance is
// shorter than we already have.
uint8_t distance = _msg.getByte();
if (isValidDistance(distance))
{
// Distance to gateway is one more for us w.r.t. parent
distance++;
if (isValidDistance(distance) && (distance < _nc.distance)) {
// Found a neighbor closer to GW than previously found
_nc.distance = distance;
_nc.parentNodeId = _msg.sender;
hwWriteConfig(EEPROM_PARENT_NODE_ID_ADDRESS, _nc.parentNodeId);
hwWriteConfig(EEPROM_DISTANCE_ADDRESS, _nc.distance);
debug(PSTR("parent=%d, d=%d\n"), _nc.parentNodeId, _nc.distance);
}
}
}
return;
#ifdef MY_SIGNING_FEATURE
} else if (type == I_GET_NONCE) {
if (signer.getNonce(_msg)) {
sendRoute(build(_msg, _nc.nodeId, _msg.sender, NODE_SENSOR_ID, C_INTERNAL, I_GET_NONCE_RESPONSE, false));
}
return false; // Nonce exchange is an internal MySensor protocol message, no need to inform caller about this
} else if (type == I_REQUEST_SIGNING) {
if (_msg.getBool()) {
// We received an indicator that the sender require us to sign all messages we send to it
SET_SIGN(_msg.sender);
} else {
// We received an indicator that the sender does not require us to sign all messages we send to it
CLEAR_SIGN(_msg.sender);
}
// Save updated table
hwWriteConfigBlock((void*)_doSign, (void*)EEPROM_SIGNING_REQUIREMENT_TABLE_ADDRESS, sizeof(_doSign));
// Inform sender about our preference if we are a gateway, but only require signing if the sender required signing
// We do not currently want a gateway to require signing from all nodes in a network just because it wants one node
// to sign it's messages
if (MY_GATEWAY_FEATURE) {
if (signer.requestSignatures() && DO_SIGN(_msg.sender))
sendRoute(build(_msg, _nc.nodeId, _msg.sender, NODE_SENSOR_ID, C_INTERNAL, I_REQUEST_SIGNING, false).set(true));
else
sendRoute(build(_msg, _nc.nodeId, _msg.sender, NODE_SENSOR_ID, C_INTERNAL, I_REQUEST_SIGNING, false).set(false));
}
return; // Signing request is an internal MySensor protocol message, no need to inform caller about this
} else if (type == I_GET_NONCE_RESPONSE) {
return; // Just pass along nonce silently (no need to call callback for these)
#endif
} else if (sender == GATEWAY_ADDRESS) {
if (type == I_ID_RESPONSE && _nc.nodeId == AUTO) {
_nc.nodeId = _msg.getByte();
if (_nc.nodeId == AUTO) {
// sensor net gateway will return max id if all sensor id are taken
debug(PSTR("full\n"));
while (1); // Wait here. Nothing else we can do...
}
setupNode();
// Write id to EEPROM
hwWriteConfig(EEPROM_NODE_ID_ADDRESS, _nc.nodeId);
debug(PSTR("id=%d\n"), _nc.nodeId);
} else {
processInternalMessages();
}
return;
}
}
#ifdef MY_OTA_FIRMWARE_FEATURE
else if (command == C_STREAM) {
if (type == ST_FIRMWARE_CONFIG_RESPONSE) {
NodeFirmwareConfig *firmwareConfigResponse = (NodeFirmwareConfig *)_msg.data;
// compare with current node configuration, if they differ, start fw fetch process
if (memcmp(&_fc,firmwareConfigResponse,sizeof(NodeFirmwareConfig))) {
debug(PSTR("fw update\n"));
// copy new FW config
memcpy(&_fc,firmwareConfigResponse,sizeof(NodeFirmwareConfig));
// Init flash
if (!_flash.initialize()) {
debug(PSTR("flash init fail\n"));
_fwUpdateOngoing = false;
} else {
// erase lower 32K -> max flash size for ATMEGA328
_flash.blockErase32K(0);
// wait until flash erased
while ( _flash.busy() );
_fwBlock = _fc.blocks;
_fwUpdateOngoing = true;
// reset flags
_fwRetry = MY_OTA_RETRY+1;
_fwLastRequestTime = 0;
}
return ;
} else debug(PSTR("fw update skipped\n"));
} else if (type == ST_FIRMWARE_RESPONSE) {
if (_fwUpdateOngoing) {
// Save block to flash
debug(PSTR("fw block %d\n"), _fwBlock);
// extract FW block
ReplyFWBlock *firmwareResponse = (ReplyFWBlock *)_msg.data;
// write to flash
_flash.writeBytes( ((_fwBlock - 1) * FIRMWARE_BLOCK_SIZE) + FIRMWARE_START_OFFSET, firmwareResponse->data, FIRMWARE_BLOCK_SIZE);
// wait until flash written
while ( _flash.busy() );
_fwBlock--;
if (!_fwBlock) {
// We're finished! Do a checksum and reboot.
_fwUpdateOngoing = false;
if (isValidFirmware()) {
debug(PSTR("fw checksum ok\n"));
// All seems ok, write size and signature to flash (DualOptiboot will pick this up and flash it)
uint16_t fwsize = FIRMWARE_BLOCK_SIZE * _fc.blocks;
uint8_t OTAbuffer[10] = {'F','L','X','I','M','G',':',(fwsize >> 8),fwsize,':'};
_flash.writeBytes(0, OTAbuffer, 10);
// Write the new firmware config to eeprom
hwWriteConfigBlock((void*)&_fc, (void*)EEPROM_FIRMWARE_TYPE_ADDRESS, sizeof(NodeFirmwareConfig));
hwReboot();
} else {
debug(PSTR("fw checksum fail\n"));
}
}
// reset flags
_fwRetry = MY_OTA_RETRY+1;
_fwLastRequestTime = 0;
} else {
debug(PSTR("No fw update ongoing\n"));
}
return;
}
}
#endif
// Call incoming message callback if available
if (_msgCallback != NULL) {
_msgCallback(_msg);
}
return;
} else if (MY_IS_REPEATER && _nc.nodeId != AUTO) {
// If this node have an id, relay the message
if (command == C_INTERNAL && type == I_FIND_PARENT) {
if (sender != _nc.parentNodeId) {
if (_nc.distance == DISTANCE_INVALID)
findParentNode();
if (_nc.distance != DISTANCE_INVALID) {
// Relaying nodes should always answer ping messages
// Wait a random delay of 0-2 seconds to minimize collision
// between ping ack messages from other relaying nodes
wait(hwMillis() & 0x3ff);
sendWrite(sender, build(_msg, _nc.nodeId, sender, NODE_SENSOR_ID, C_INTERNAL, I_FIND_PARENT_RESPONSE, false).set(_nc.distance));
}
}
} else if (to == _nc.nodeId) {
// We should try to relay this message to another node
sendRoute(_msg);
}
}
#endif
}
// Message delivered through _msg
void processInternalMessages() {
bool isMetric;
uint8_t type = _msg.type;
@@ -938,8 +233,12 @@ void processInternalMessages() {
isMetric = _msg.getString()[0] == 'M' ;
_cc.isMetric = isMetric;
hwWriteConfig(EEPROM_CONTROLLER_CONFIG_ADDRESS, isMetric);
} else if (type == I_CHILDREN) {
if (MY_IS_REPEATER && _msg.getString()[0] == 'C') {
} else if (type == I_TIME && _timeCallback != NULL) {
// Deliver time to callback
_timeCallback(_msg.getULong());
}
#if defined(MY_REPEATER_FEATURE)
if (type == I_CHILDREN && _msg.getString()[0] == 'C') {
// Clears child relay data for this node
debug(PSTR("clear\n"));
uint8_t i = 255;
@@ -950,26 +249,19 @@ void processInternalMessages() {
hwWriteConfig(EEPROM_PARENT_NODE_ID_ADDRESS, 0xFF);
hwWriteConfig(EEPROM_DISTANCE_ADDRESS, 0xFF);
// Find parent node
findParentNode();
transportFindParentNode();
sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_CHILDREN,false).set(""));
}
} else if (type == I_TIME) {
if (_timeCallback != NULL) {
// Deliver time to callback
_timeCallback(_msg.getULong());
}
}
#endif
}
#if defined (MY_REPEATER_FEATURE)
void saveState(uint8_t pos, uint8_t value) {
hwWriteConfig(EEPROM_LOCAL_CONFIG_ADDRESS+pos, value);
}
uint8_t loadState(uint8_t pos) {
return hwReadConfig(EEPROM_LOCAL_CONFIG_ADDRESS+pos);
}
#endif
void wait(unsigned long ms) {
unsigned long enter = hwMillis();
@@ -1029,31 +321,5 @@ int8_t sleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode
}
#ifdef MY_INCLUSION_MODE_FEATURE
void setInclusionMode(boolean newMode) {
if (newMode != inclusionMode) {
inclusionMode = newMode;
// Send back mode change to controller
gatewayTransportSend(buildGw(_msg, I_INCLUSION_MODE).set(inclusionMode?1:0));
if (inclusionMode) {
inclusionStartTime = hwMillis();
}
}
}
void checkInclusionMode() {
#ifdef MY_INCLUSION_BUTTON_FEATURE
if (!inclusionMode && digitalRead(MY_INCLUSION_MODE_BUTTON_PIN) == LOW) {
// Start inclusion mode
setInclusionMode(true);
}
#endif
if (inclusionMode && hwMillis()-inclusionStartTime>MY_INCLUSION_MODE_DURATION*1000L) {
// inclusionTimeInMinutes minute(s) has passed.. stop inclusion mode
setInclusionMode(false);
}
}
#endif
+35 -98
View File
@@ -35,24 +35,12 @@
#include <stddef.h>
#include <stdarg.h>
#ifdef MY_DEBUG
#define debug(x,...) hwDebugPrint(x, ##__VA_ARGS__)
#else
#define debug(x,...)
#endif
#ifdef WITH_LEDS_BLINKING_INVERSE
#define LED_ON 0x1
#define LED_OFF 0x0
#else
#define LED_ON 0x0
#define LED_OFF 0x1
#endif
// EEPROM start address for mysensors library data
#define EEPROM_START 0
// EEPROM location of node id
@@ -70,8 +58,8 @@
#define EEPROM_SIGNING_REQUIREMENT_TABLE_ADDRESS (EEPROM_FIRMWARE_CRC_ADDRESS+2)
#define EEPROM_LOCAL_CONFIG_ADDRESS (EEPROM_SIGNING_REQUIREMENT_TABLE_ADDRESS+32) // First free address for sketch static configuration
// Search for a new parent node after this many transmission failures
#define SEARCH_FAILURES 5
// This is the nodeId for sensor net gateway receiver sketch (where all sensors should send their data).
#define GATEWAY_ADDRESS ((uint8_t)0)
struct NodeConfig
@@ -86,65 +74,15 @@ struct ControllerConfig {
};
// Size of each firmware block
#define FIRMWARE_BLOCK_SIZE 16
// Number of times a firmware block should be requested before giving up
#define FIRMWARE_MAX_REQUESTS 5
// Number of times to request a fw block before giving up
#define MY_OTA_RETRY 5
// Number of millisecons before re-request a fw block
#define MY_OTA_RETRY_DELAY 500
// Start offset for firmware in flash (DualOptiboot wants to keeps a signature first)
#define FIRMWARE_START_OFFSET 10
// Bootloader version
#define MY_OTA_BOOTLOADER_MAJOR_VERSION 3
#define MY_OTA_BOOTLOADER_MINOR_VERSION 0
#define MY_OTA_BOOTLOADER_VERSION (MY_OTA_BOOTLOADER_MINOR_VERSION * 256 + MY_OTA_BOOTLOADER_MAJOR_VERSION)
// FW config structure, stored in eeprom
typedef struct {
uint16_t type;
uint16_t version;
uint16_t blocks;
uint16_t crc;
} __attribute__((packed)) NodeFirmwareConfig;
typedef struct {
uint16_t type;
uint16_t version;
uint16_t blocks;
uint16_t crc;
uint16_t BLVersion;
} __attribute__((packed)) RequestFirmwareConfig;
typedef struct {
uint16_t type;
uint16_t version;
uint16_t block;
} __attribute__((packed)) RequestFWBlock;
typedef struct {
uint16_t type;
uint16_t version;
uint16_t block;
uint8_t data[FIRMWARE_BLOCK_SIZE];
} __attribute__((packed)) ReplyFWBlock;
/**
* Begin operation of the MySensors library
* Set the callback method for incoming messages
*
* Call this in setup(), before calling any other sensor net library methods.
* @param incomingMessageCallback Callback function for incoming messages from other nodes or controller and request responses. Default is NULL.
* @param incomingMessageCallback Callback function for incoming messages from other nodes or controller and request responses.
*/
void dataCallback(void (* msgCallback)(const MyMessage &)=NULL);
/**
* Return the nodes nodeId.
* Return this nodes id.
*/
uint8_t getNodeId();
@@ -284,47 +222,46 @@ bool sleep(uint8_t interrupt, uint8_t mode, unsigned long ms=0);
int8_t sleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2, unsigned long ms=0);
/****** PRIVATE ********/
void begin();
void process(void);
void processInternalMessages();
#ifdef MY_LEDS_BLINKING_FEATURE
/**
* Blink with LEDs
* @param cnt how many blink cycles to keep the LED on. Default cycle is 300ms
*/
void rxBlink(uint8_t cnt);
void txBlink(uint8_t cnt);
void errBlink(uint8_t cnt);
void handleLedsBlinking(); // do the actual blinking
#endif
#ifdef MY_OTA_FIRMWARE_FEATURE
// do a crc16 on the whole received firmware
bool isValidFirmware();
#endif
#ifdef MY_INCLUSION_MODE_FEATURE
void setInclusionMode(bool newMode);
void checkInclusionMode();
unsigned long inclusionStartTime;
bool inclusionMode;
#endif
boolean sendRoute(MyMessage &message);
#if defined(MY_RADIO_FEATURE)
boolean sendWrite(uint8_t dest, MyMessage &message);
void requestNodeId();
void setupNode();
void findParentNode();
extern NodeConfig _nc;
extern MyMessage _msg; // Buffer for incoming messages.
extern MyMessage _msgTmp; // Buffer for temporary messages (acks and nonces among others).
extern void (*_msgCallback)(const MyMessage &); // Callback for incoming messages from other nodes and gateway.
#ifdef MY_DEBUG
extern char convBuf[MAX_PAYLOAD*2+1];
#endif
// Inline function and macros
static inline MyMessage& build(MyMessage &msg, uint8_t sender, uint8_t destination, uint8_t sensor, uint8_t command, uint8_t type, bool enableAck) {
msg.sender = sender;
msg.destination = destination;
msg.sensor = sensor;
msg.type = type;
mSetCommand(msg,command);
mSetRequestAck(msg,enableAck);
mSetAck(msg,false);
return msg;
}
static inline MyMessage& buildGw(MyMessage &msg, uint8_t type) {
msg.sender = GATEWAY_ADDRESS;
msg.destination = GATEWAY_ADDRESS;
msg.sensor = 0;
msg.type = type;
mSetCommand(msg, C_INTERNAL);
mSetRequestAck(msg, false);
mSetAck(msg, false);
return msg;
}
#endif
+5
View File
@@ -19,6 +19,11 @@
#include "MySigning.h"
uint16_t _doSign[16]; // Bitfield indicating which sensors require signed communication
MyMessage _msgSign; // Buffer for message to sign.
MySigning::MySigning(bool requestSignatures) : _requestSignatures(requestSignatures) {
}
+6
View File
@@ -30,6 +30,12 @@
#include <stdint.h>
#include "MyMessage.h"
// Macros for manipulating signing requirement table
#define DO_SIGN(node) (node == 0 ? (~_doSign[0]&1) : (~_doSign[node>>4]&(node%16)))
#define SET_SIGN(node) (node == 0 ? (_doSign[0]&=~1) : (_doSign[node>>4]&=~(node%16)))
#define CLEAR_SIGN(node) (node == 0 ? (_doSign[0]|=1) : (_doSign[node>>4]|=(node%16)))
class MySigning
{
public:
+547
View File
@@ -0,0 +1,547 @@
/**
* 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"
bool _autoFindParent;
uint8_t _failedTransmissions;
#ifdef MY_OTA_FIRMWARE_FEATURE
NodeFirmwareConfig _fc;
bool _fwUpdateOngoing;
unsigned long _fwLastRequestTime;
uint16_t _fwBlock;
uint8_t _fwRetry;
#endif
static inline bool isValidDistance( const uint8_t distance ) {
return distance != DISTANCE_INVALID;
}
inline void transportProcess() {
uint8_t to = 0;
if (!transportAvailable(&to))
{
#ifdef MY_OTA_FIRMWARE_FEATURE
unsigned long enter = hwMillis();
if (_fwUpdateOngoing && (enter - _fwLastRequestTime > MY_OTA_RETRY_DELAY)) {
if (!_fwRetry) {
debug(PSTR("fw upd fail\n"));
// Give up. We have requested MY_OTA_RETRY times without any packet in return.
_fwUpdateOngoing = false;
ledBlinkErr(1);
return;
}
_fwRetry--;
_fwLastRequestTime = enter;
// Time to (re-)request firmware block from controller
RequestFWBlock *firmwareRequest = (RequestFWBlock *)_msg.data;
mSetLength(_msg, sizeof(RequestFWBlock));
firmwareRequest->type = _fc.type;
firmwareRequest->version = _fc.version;
firmwareRequest->block = (_fwBlock - 1);
sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_STREAM, ST_FIRMWARE_REQUEST, false));
}
#endif
return;
}
#ifdef MY_SIGNING_FEATURE
(void)signer.checkTimer(); // Manage signing timeout
#endif
uint8_t len = transportReceive((uint8_t *)&_msg);
(void)len; //until somebody makes use of 'len'
ledBlinkRx(1);
#ifdef MY_SIGNING_FEATURE
// Before processing message, reject unsigned messages if signing is required and check signature (if it is signed and addressed to us)
// Note that we do not care at all about any signature found if we do not require signing, nor do we care about ACKs (they are never signed)
if (signer.requestSignatures() && _msg.destination == _nc.nodeId && mGetLength(_msg) && !mGetAck(_msg) &&
(mGetCommand(_msg) != C_INTERNAL ||
(_msg.type != I_GET_NONCE_RESPONSE && _msg.type != I_GET_NONCE && _msg.type != I_REQUEST_SIGNING &&
_msg.type != I_ID_REQUEST && _msg.type != I_ID_RESPONSE &&
_msg.type != I_FIND_PARENT && _msg.type != I_FIND_PARENT_RESPONSE))) {
if (!mGetSigned(_msg)) {
// Got unsigned message that should have been signed
debug(PSTR("no sign\n"));
ledBlinkErr(1);
return;
}
else if (!signer.verifyMsg(_msg)) {
debug(PSTR("verify fail\n"));
ledBlinkErr(1);
return; // This signed message has been tampered with!
}
}
#endif
// Add string termination, good if we later would want to print it.
_msg.data[mGetLength(_msg)] = '\0';
debug(PSTR("read: %d-%d-%d s=%d,c=%d,t=%d,pt=%d,l=%d,sg=%d:%s\n"),
_msg.sender, _msg.last, _msg.destination, _msg.sensor, mGetCommand(_msg), _msg.type, mGetPayloadType(_msg), mGetLength(_msg), mGetSigned(_msg), _msg.getString(convBuf));
mSetSigned(_msg,0); // Clear the sign-flag now as verification (and debug printing) is completed
if(!(mGetVersion(_msg) == PROTOCOL_VERSION)) {
debug(PSTR("ver mismatch\n"));
ledBlinkErr(1);
return;
}
uint8_t command = mGetCommand(_msg);
uint8_t type = _msg.type;
uint8_t sender = _msg.sender;
uint8_t last = _msg.last;
uint8_t destination = _msg.destination;
if (destination == _nc.nodeId) {
// This message is addressed to this node
#if 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)) {
// Copy message
_msgTmp = _msg;
mSetRequestAck(_msgTmp,false); // Reply without ack flag (otherwise we would end up in an eternal loop)
mSetAck(_msgTmp,true);
_msgTmp.sender = _nc.nodeId;
_msgTmp.destination = _msg.sender;
sendRoute(_msgTmp);
}
if (command == C_INTERNAL) {
if (type == I_FIND_PARENT_RESPONSE) {
if (_autoFindParent) {
// We've received a reply to a FIND_PARENT message. Check if the distance is
// shorter than we already have.
uint8_t distance = _msg.getByte();
if (isValidDistance(distance))
{
// Distance to gateway is one more for us w.r.t. parent
distance++;
if (isValidDistance(distance) && (distance < _nc.distance)) {
// Found a neighbor closer to GW than previously found
_nc.distance = distance;
_nc.parentNodeId = _msg.sender;
hwWriteConfig(EEPROM_PARENT_NODE_ID_ADDRESS, _nc.parentNodeId);
hwWriteConfig(EEPROM_DISTANCE_ADDRESS, _nc.distance);
debug(PSTR("parent=%d, d=%d\n"), _nc.parentNodeId, _nc.distance);
}
}
}
return;
#ifdef MY_SIGNING_FEATURE
} else if (type == I_GET_NONCE) {
if (signer.getNonce(_msg)) {
sendRoute(build(_msg, _nc.nodeId, _msg.sender, NODE_SENSOR_ID, C_INTERNAL, I_GET_NONCE_RESPONSE, false));
}
return false; // Nonce exchange is an internal MySensor protocol message, no need to inform caller about this
} else if (type == I_REQUEST_SIGNING) {
if (_msg.getBool()) {
// We received an indicator that the sender require us to sign all messages we send to it
SET_SIGN(_msg.sender);
} else {
// We received an indicator that the sender does not require us to sign all messages we send to it
CLEAR_SIGN(_msg.sender);
}
// Save updated table
hwWriteConfigBlock((void*)_doSign, (void*)EEPROM_SIGNING_REQUIREMENT_TABLE_ADDRESS, sizeof(_doSign));
// Inform sender about our preference if we are a gateway, but only require signing if the sender required signing
// We do not currently want a gateway to require signing from all nodes in a network just because it wants one node
// to sign it's messages
if (MY_GATEWAY_FEATURE) {
if (signer.requestSignatures() && DO_SIGN(_msg.sender))
sendRoute(build(_msg, _nc.nodeId, _msg.sender, NODE_SENSOR_ID, C_INTERNAL, I_REQUEST_SIGNING, false).set(true));
else
sendRoute(build(_msg, _nc.nodeId, _msg.sender, NODE_SENSOR_ID, C_INTERNAL, I_REQUEST_SIGNING, false).set(false));
}
return; // Signing request is an internal MySensor protocol message, no need to inform caller about this
} else if (type == I_GET_NONCE_RESPONSE) {
return; // Just pass along nonce silently (no need to call callback for these)
#endif
} else if (sender == GATEWAY_ADDRESS) {
if (type == I_ID_RESPONSE && _nc.nodeId == AUTO) {
_nc.nodeId = _msg.getByte();
if (_nc.nodeId == AUTO) {
// sensor net gateway will return max id if all sensor id are taken
debug(PSTR("full\n"));
while (1); // Wait here. Nothing else we can do...
}
transportSetupNode();
// Write id to EEPROM
hwWriteConfig(EEPROM_NODE_ID_ADDRESS, _nc.nodeId);
debug(PSTR("id=%d\n"), _nc.nodeId);
} else {
processInternalMessages();
}
return;
}
}
#ifdef MY_OTA_FIRMWARE_FEATURE
else if (command == C_STREAM) {
if (type == ST_FIRMWARE_CONFIG_RESPONSE) {
NodeFirmwareConfig *firmwareConfigResponse = (NodeFirmwareConfig *)_msg.data;
// compare with current node configuration, if they differ, start fw fetch process
if (memcmp(&_fc,firmwareConfigResponse,sizeof(NodeFirmwareConfig))) {
debug(PSTR("fw update\n"));
// copy new FW config
memcpy(&_fc,firmwareConfigResponse,sizeof(NodeFirmwareConfig));
// Init flash
if (!_flash.initialize()) {
debug(PSTR("flash init fail\n"));
_fwUpdateOngoing = false;
} else {
// erase lower 32K -> max flash size for ATMEGA328
_flash.blockErase32K(0);
// wait until flash erased
while ( _flash.busy() );
_fwBlock = _fc.blocks;
_fwUpdateOngoing = true;
// reset flags
_fwRetry = MY_OTA_RETRY+1;
_fwLastRequestTime = 0;
}
return ;
} else debug(PSTR("fw update skipped\n"));
} else if (type == ST_FIRMWARE_RESPONSE) {
if (_fwUpdateOngoing) {
// Save block to flash
debug(PSTR("fw block %d\n"), _fwBlock);
// extract FW block
ReplyFWBlock *firmwareResponse = (ReplyFWBlock *)_msg.data;
// write to flash
_flash.writeBytes( ((_fwBlock - 1) * FIRMWARE_BLOCK_SIZE) + FIRMWARE_START_OFFSET, firmwareResponse->data, FIRMWARE_BLOCK_SIZE);
// wait until flash written
while ( _flash.busy() );
_fwBlock--;
if (!_fwBlock) {
// We're finished! Do a checksum and reboot.
_fwUpdateOngoing = false;
if (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',':',(fwsize >> 8),fwsize,':'};
_flash.writeBytes(0, OTAbuffer, 10);
// Write the new firmware config to eeprom
hwWriteConfigBlock((void*)&_fc, (void*)EEPROM_FIRMWARE_TYPE_ADDRESS, sizeof(NodeFirmwareConfig));
hwReboot();
} else {
debug(PSTR("fw checksum fail\n"));
}
}
// reset flags
_fwRetry = MY_OTA_RETRY+1;
_fwLastRequestTime = 0;
} else {
debug(PSTR("No fw update ongoing\n"));
}
return;
}
}
#endif
// Call incoming message callback if available
if (_msgCallback != NULL) {
_msgCallback(_msg);
}
return;
}
#if defined(MY_REPEATER_FEATURE)
if (_nc.nodeId != AUTO) {
// If this node have an id, relay the message
if (command == C_INTERNAL && type == I_FIND_PARENT) {
if (sender != _nc.parentNodeId) {
if (_nc.distance == DISTANCE_INVALID)
transportFindParentNode();
if (_nc.distance != DISTANCE_INVALID) {
// Relaying nodes should always answer ping messages
// Wait a random delay of 0-2 seconds to minimize collision
// between ping ack messages from other relaying nodes
wait(hwMillis() & 0x3ff);
sendWrite(sender, build(_msg, _nc.nodeId, sender, NODE_SENSOR_ID, C_INTERNAL, I_FIND_PARENT_RESPONSE, false).set(_nc.distance));
}
}
} else if (to == _nc.nodeId) {
// We should try to relay this message to another node
sendRoute(_msg);
}
}
#endif
}
#ifdef MY_OTA_FIRMWARE_FEATURE
// do a crc16 on the whole received firmware
bool transportIsValidFirmware() {
// init crc
uint16_t crc = ~0;
for (uint16_t i = 0; i < _fc.blocks * FIRMWARE_BLOCK_SIZE; ++i) {
crc ^= _flash.readByte(i + FIRMWARE_START_OFFSET);
for (int8_t j = 0; j < 8; ++j) {
if (crc & 1)
crc = (crc >> 1) ^ 0xA001;
else
crc = (crc >> 1);
}
}
return crc == _fc.crc;
}
#endif
boolean transportSendWrite(uint8_t to, MyMessage &message) {
mSetVersion(message, PROTOCOL_VERSION);
uint8_t length = mGetSigned(message) ? MAX_MESSAGE_LENGTH : mGetLength(message);
message.last = _nc.nodeId;
ledBlinkTx(1);
bool ok = transportSend(to, &message, min(MAX_MESSAGE_LENGTH, HEADER_SIZE + length));
debug(PSTR("send: %d-%d-%d-%d s=%d,c=%d,t=%d,pt=%d,l=%d,sg=%d,st=%s:%s\n"),
message.sender,message.last, to, message.destination, message.sensor, mGetCommand(message), message.type,
mGetPayloadType(message), mGetLength(message), mGetSigned(message), to==BROADCAST_ADDRESS ? "bc" : (ok ? "ok":"fail"), message.getString(convBuf));
return ok;
}
boolean transportSendRoute(MyMessage &message) {
uint8_t sender = message.sender;
uint8_t dest = message.destination;
uint8_t last = message.last;
bool ok;
// If we still don't have any parent id, re-request and skip this message.
if (_nc.parentNodeId == AUTO) {
transportFindParentNode();
ledBlinkErr(1);
return false;
}
// If we still don't have any node id, re-request and skip this message.
if (_nc.nodeId == AUTO) {
transportRequestNodeId();
ledBlinkErr(1);
return false;
}
mSetVersion(message, PROTOCOL_VERSION);
#ifdef MY_SIGNING_FEATURE
// If destination is known to require signed messages and we are the sender, sign this message unless it is an ACK or a handshake message
if (DO_SIGN(message.destination) && message.sender == _nc.nodeId && !mGetAck(message) && mGetLength(message) &&
(mGetCommand(message) != C_INTERNAL ||
(message.type != I_GET_NONCE && message.type != I_GET_NONCE_RESPONSE && message.type != I_REQUEST_SIGNING &&
message.type != I_ID_REQUEST && message.type != I_ID_RESPONSE &&
message.type != I_FIND_PARENT && message.type != I_FIND_PARENT_RESPONSE))) {
bool signOk = false;
// Send nonce-request
if (!sendRoute(build(_msgTmp, _nc.nodeId, message.destination, message.sensor, C_INTERNAL, I_GET_NONCE, false).set(""))) {
debug(PSTR("nonce tr err\n"));
return false;
}
// We have to wait for the nonce to arrive before we can sign our original message
// Other messages could come in-between. We trust process() takes care of them
unsigned long enter = hwMillis();
_msgSign = message; // Copy the message to sign since message buffer might be touched in process()
while (hwMillis() - enter < MY_VERIFICATION_TIMEOUT_MS) {
if (process()) {
if (mGetCommand(getLastMessage()) == C_INTERNAL && getLastMessage().type == I_GET_NONCE_RESPONSE) {
// Proceed with signing if nonce has been received
if (signer.putNonce(getLastMessage()) && signer.signMsg(_msgSign)) {
message = _msgSign; // Write the signed message back
signOk = true;
}
break;
}
}
}
if (hwMillis() - enter > MY_VERIFICATION_TIMEOUT_MS) {
debug(PSTR("nonce tmo\n"));
ledBlinkErr(1);
return false;
}
if (!signOk) {
debug(PSTR("sign fail\n"));
ledBlinkErr(1);
return false;
}
// After this point, only the 'last' member of the message structure is allowed to be altered if the message has been signed,
// or signature will become invalid and the message rejected by the receiver
} else {
mSetSigned(message, 0); // Message is not supposed to be signed, make sure it is marked unsigned
}
#endif
if (dest == GATEWAY_ADDRESS || !MY_IS_REPEATER) {
// Store this address in routing table (if repeater)
#if defined (MY_IS_REPEATER)
hwWriteConfig(EEPROM_ROUTES_ADDRESS+sender, last);
#endif
// If destination is the gateway or if we aren't a repeater, let
// our parent take care of the message
ok = transportSendWrite(_nc.parentNodeId, message);
} else {
// Relay the message
uint8_t route = hwReadConfig(EEPROM_ROUTES_ADDRESS+dest);
if (route > GATEWAY_ADDRESS && route < BROADCAST_ADDRESS) {
// This message should be forwarded to a child node. If we send message
// to this nodes pipe then all children will receive it because the are
// all listening to this nodes pipe.
//
// +----B
// -A
// +----C------D
//
// We're node C, Message comes from A and has destination D
//
// Message destination is not gateway and is in routing table for this node.
// Send it downstream
return transportSendWrite(route, message);
} else if (sender == GATEWAY_ADDRESS && dest == BROADCAST_ADDRESS) {
// Node has not yet received any id. We need to send it
// by doing a broadcast sending,
return transportSendWrite(BROADCAST_ADDRESS, message);
} else if (MY_IS_GATEWAY) {
// Destination isn't in our routing table and isn't a broadcast address
// Nothing to do here
return false;
} else {
// A message comes from a child node and we have no
// route for it.
//
// +----B
// -A
// +----C------D <-- Message comes from D
//
// We're node C
//
// Message should be passed to node A (this nodes relay)
// This message should be routed back towards sensor net gateway
ok = transportSendWrite(_nc.parentNodeId, message);
// Add this child to our "routing table" if it not already exist
hwWriteConfig(EEPROM_ROUTES_ADDRESS+sender, last);
}
}
if (!ok) {
// Failure when sending to parent node. The parent node might be down and we
// need to find another route to gateway.
ledBlinkErr(1);
_failedTransmissions++;
if (_autoFindParent && _failedTransmissions > SEARCH_FAILURES) {
transportFindParentNode();
}
} else {
_failedTransmissions = 0;
}
return ok;
}
void transportRequestNodeId() {
debug(PSTR("req id\n"));
transportSetAddress(_nc.nodeId);
build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_ID_REQUEST, false).set("");
transportSendWrite(_nc.parentNodeId, _msg);
wait(2000);
}
void transportSetupNode() {
// Open reading pipe for messages directed to this node (set write pipe to same)
transportSetAddress(_nc.nodeId);
// Present node and request config
#ifndef MY_GATEWAY_FEATURE
if (_nc.nodeId != AUTO) {
#ifdef MY_SIGNING_FEATURE
// Notify gateway (and possibly controller) about the signing preferences of this node
sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_REQUEST_SIGNING, false).set(signer.requestSignatures()));
// If we do require signing, wait for the gateway to tell us how it prefer us to transmit our messages
if (signer.requestSignatures()) {
wait(2000);
}
#endif
// Send presentation for this radio node
#ifdef MY_REPEATER_FEATURE
present(NODE_SENSOR_ID, S_ARDUINO_REPEATER_NODE);
#else
present(NODE_SENSOR_ID, S_ARDUINO_NODE);
#endif
// Send a configuration exchange request to controller
// Node sends parent node. Controller answers with latest node configuration
// which is picked up in process()
sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_CONFIG, false).set(_nc.parentNodeId));
// Wait configuration reply.
wait(2000);
#ifdef MY_OTA_FIRMWARE_FEATURE
RequestFirmwareConfig *reqFWConfig = (RequestFirmwareConfig *)_msg.data;
mSetLength(_msg, sizeof(RequestFirmwareConfig));
mSetCommand(_msg, C_STREAM);
mSetPayloadType(_msg,P_CUSTOM);
// copy node settings to reqFWConfig
memcpy(reqFWConfig,&_fc,sizeof(NodeFirmwareConfig));
// add bootloader information
reqFWConfig->BLVersion = MY_OTA_BOOTLOADER_VERSION;
_fwUpdateOngoing = false;
sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_STREAM, ST_FIRMWARE_CONFIG_REQUEST, false));
#endif
}
#endif
}
void transportFindParentNode() {
static boolean findingParentNode = false;
if (findingParentNode)
return;
findingParentNode = true;
_failedTransmissions = 0;
// Set distance to max
_nc.distance = 255;
// Send ping message to BROADCAST_ADDRESS (to which all relaying nodes and gateway listens and should reply to)
debug(PSTR("find parent\n"));
build(_msg, _nc.nodeId, BROADCAST_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_FIND_PARENT, false).set("");
// Write msg, but suppress recursive parent search
transportSendWrite(BROADCAST_ADDRESS, _msg);
// Wait for ping response.
wait(2000);
findingParentNode = false;
}
+76 -2
View File
@@ -21,13 +21,87 @@
#define MyTransport_h
#include <stdint.h>
#include "MySensorCore.h"
// Size of each firmware block
#define FIRMWARE_BLOCK_SIZE 16
// Number of times a firmware block should be requested before giving up
#define FIRMWARE_MAX_REQUESTS 5
// Number of times to request a fw block before giving up
#define MY_OTA_RETRY 5
// Number of millisecons before re-request a fw block
#define MY_OTA_RETRY_DELAY 500
// Start offset for firmware in flash (DualOptiboot wants to keeps a signature first)
#define FIRMWARE_START_OFFSET 10
// Bootloader version
#define MY_OTA_BOOTLOADER_MAJOR_VERSION 3
#define MY_OTA_BOOTLOADER_MINOR_VERSION 0
#define MY_OTA_BOOTLOADER_VERSION (MY_OTA_BOOTLOADER_MINOR_VERSION * 256 + MY_OTA_BOOTLOADER_MAJOR_VERSION)
// Search for a new parent node after this many transmission failures
#define SEARCH_FAILURES 5
// FW config structure, stored in eeprom
typedef struct {
uint16_t type;
uint16_t version;
uint16_t blocks;
uint16_t crc;
} __attribute__((packed)) NodeFirmwareConfig;
typedef struct {
uint16_t type;
uint16_t version;
uint16_t blocks;
uint16_t crc;
uint16_t BLVersion;
} __attribute__((packed)) RequestFirmwareConfig;
typedef struct {
uint16_t type;
uint16_t version;
uint16_t block;
} __attribute__((packed)) RequestFWBlock;
typedef struct {
uint16_t type;
uint16_t version;
uint16_t block;
uint8_t data[FIRMWARE_BLOCK_SIZE];
} __attribute__((packed)) ReplyFWBlock;
#define AUTO 0xFF // 0-254. Id 255 is reserved for auto initialization of nodeId.
#define NODE_SENSOR_ID 0xFF // Node child id is always created for when a node
// This is the nodeId for sensor net gateway receiver sketch (where all sensors should send their data).
#define GATEWAY_ADDRESS ((uint8_t)0)
#define BROADCAST_ADDRESS ((uint8_t)0xFF)
// 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 transportSetupNode();
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
@@ -32,12 +32,4 @@
#define CURRENT_NODE_PIPE ((uint8_t)1)
#define BROADCAST_PIPE ((uint8_t)2)
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
@@ -27,12 +27,4 @@
#include "drivers/SPI/SPI.h"
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
Submodule libraries/MySensors/drivers/pubsubclient added at 2f97e4a558
@@ -64,8 +64,8 @@
// Enable gateway type
#define MY_GATEWAY_W5100
//#define MY_GATEWAY_W5100
// Set default gateway protocol (http://www.mysensors.org/download/serial_api_15)
#define MY_GATEWAY_PROTOCOL_DEFAULT
@@ -96,7 +96,7 @@
#define MY_MAC_ADDRESS 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
// Flash leds on rx/tx/err
#define MY_WITH_LEDS_BLINKING
#define MY_LEDS_BLINKING_FEATURE
// Set blinking period
#define MY_DEFAULT_LED_BLINK_PERIOD 300
@@ -37,8 +37,11 @@
//#define MY_SIGNING_SOFT
//#define MY_REPEATER_FEATURE
// #define MY_GATEWAY_SERIAL // REMOVE-ME
// #define MY_GATEWAY_PROTOCOL_DEFAULT // REMOVE-ME
//#define MY_INCLUSION_MODE_FEATURE
//#define MY_INCLUSION_BUTTON_FEATURE
//#define MY_GATEWAY_SERIAL // REMOVE-ME
//#define MY_GATEWAY_PROTOCOL_DEFAULT // REMOVE-ME
//#define MY_LEDS_BLINKING_FEATURE
#include <MySensor.h>