Merge branch 'gateway-refactoring' into development

# Conflicts:
#	libraries/MySensors/MyConfig.h
#	libraries/MySensors/MySensor.cpp
#	libraries/MySensors/MySensor.h
#	libraries/MySensors/MyTransportNRF24.cpp
#	libraries/MySensors/core/MyHwESP8266.h
#	libraries/MySensors/drivers/RF24/RF24.cpp
#	libraries/MySensors/drivers/RF24/RF24.h
#	libraries/MySensors/examples/MQTTClientGateway/MQTTClientGateway.ino
#	libraries/MySensors/examples/MQTTGateway/MQTTGateway.ino
This commit is contained in:
Henrik Ekblad
2015-10-07 00:08:07 +02:00
203 changed files with 23616 additions and 5926 deletions
+268 -57
View File
@@ -22,131 +22,342 @@
#define MyConfig_h
#include <stdint.h>
// Enable debug flag for debug prints. This will add a lot to the size of the final sketch but good
// to see what is actually is happening when developing
#define DEBUG
/**********************************
* Serial and debug options
***********************************/
// Enables this in sketch to show debug prints. This option will add a lot to the size of the
// final sketch but is helpful to see what is actually is happening during development
//#define MY_DEBUG
// Enable MY_DEBUG_VERBOSE flag for verbose debug prints. Requires DEBUG to be enabled.
// This will add even more to the size of the final sketch!
//#define MY_DEBUG_VERBOSE
// Disable this line, If you are using TX(1), RX(0) as normal I/O pin
#define ENABLED_SERIAL
// Enable this in sketch if you want to use TX(1), RX(0) as normal I/O pin
//#define MY_DISABLED_SERIAL
// Serial output baud rate (for debug prints and serial gateway)
#define BAUD_RATE 115200
// Turn off debug if serial pins is used for other stuff
#ifdef MY_DISABLED_SERIAL
#undef MY_DEBUG
#endif
// Serial output baud rate (debug prints and serial gateway speed)
#ifndef MY_BAUD_RATE
#define MY_BAUD_RATE 115200
#endif
/**********************************
* Radio selection and node config
***********************************/
// Selecting radio transport is optional (for a gateway node).
//#define MY_RADIO_NRF24
//#define MY_RADIO_RFM69
// Node id defaults to AUTO (tries to fetch id from controller)
#ifndef MY_NODE_ID
#define MY_NODE_ID AUTO
#endif
// Node parent defaults to AUTO (tries to find a parent automatically)
#ifndef MY_PARENT_NODE_ID
#define MY_PARENT_NODE_ID AUTO
#endif
// Enables repeater functionality (relays messages from other nodes)
// #define MY_REPEATER_FEATURE
/**********************************
* Over the air firmware updates
***********************************/
// The following define enables the safe over-the-air firmware update feature
// which requires external flash and the DualOptiBoot bootloader.
// Enable MY_OTA_FIRMWARE_FEATURE in sketch to allow safe over-the-air firmware updates.
// This feature requires external flash and the DualOptiBoot boot-loader.
// Note: You can still have OTA FW updates without external flash but it
// requires the MYSBootloader and disabled MY_OTA_FIRMWARE_FEATURE
//#define MY_OTA_FIRMWARE_FEATURE
// Slave select pin for external flash
#ifndef MY_OTA_FLASH_SS
#define MY_OTA_FLASH_SS 8
#endif
// Flash jdecid
#ifndef MY_OTA_FLASH_JDECID
#define MY_OTA_FLASH_JDECID 0x1F65
#endif
/**********************************
* Gateway config
***********************************/
// Max buffersize needed for messages coming from controller
#ifndef MY_GATEWAY_MAX_RECEIVE_LENGTH
#define MY_GATEWAY_MAX_RECEIVE_LENGTH 100
#endif
// Max buffer size when sending messages
#ifndef MY_GATEWAY_MAX_SEND_LENGTH
#define MY_GATEWAY_MAX_SEND_LENGTH 120
#endif
// Max number of parallel clients (sever mode)
#ifndef MY_GATEWAY_MAX_CLIENTS
#define MY_GATEWAY_MAX_CLIENTS 1
#endif
/**********************************
* Information LEDs blinking
***********************************/
// This feature enables LEDs blinking on message receive, transmit
// or if some error occured. This was commonly used only in gateways,
// or if some error occurred. This was commonly used only in gateways,
// but now can be used in any sensor node. Also the LEDs can now be
// disabled in the gateway.
// #define WITH_LEDS_BLINKING
//#define MY_LEDS_BLINKING_FEATURE
// The following setting allows you to inverse the blinking feature WITH_LEDS_BLINKING
// When WITH_LEDS_BLINKING_INVERSE is enabled LEDSs are normally turned on and switches
// The following setting allows you to inverse the blinking feature MY_LEDS_BLINKING_FEATURE
// When MY_WITH_LEDS_BLINKING_INVERSE is enabled LEDSs are normally turned on and switches
// off when blinking
//#define WITH_LEDS_BLINKING_INVERSE
//#define MY_WITH_LEDS_BLINKING_INVERSE
// default LEDs blinking period in milliseconds
#define DEFAULT_LED_BLINK_PERIOD 300
#ifndef MY_DEFAULT_LED_BLINK_PERIOD
#define MY_DEFAULT_LED_BLINK_PERIOD 300
#endif
// The RX LED default pin
#define DEFAULT_RX_LED_PIN 6
#ifndef MY_DEFAULT_RX_LED_PIN
#if defined(ARDUINO_ARCH_ESP8266)
#define MY_DEFAULT_RX_LED_PIN 8
#else
#define MY_DEFAULT_RX_LED_PIN 6
#endif
#endif
// The TX LED default pin
#define DEFAULT_TX_LED_PIN 5
#ifndef MY_DEFAULT_TX_LED_PIN
#if defined(ARDUINO_ARCH_ESP8266)
#define MY_DEFAULT_TX_LED_PIN 9
#else
#define MY_DEFAULT_TX_LED_PIN 5
#endif
#endif
// The Error LED default pin
#define DEFAULT_ERR_LED_PIN 4
#ifndef MY_DEFAULT_ERR_LED_PIN
#if defined(ARDUINO_ARCH_ESP8266)
#define MY_DEFAULT_ERR_LED_PIN 7
#else
#define MY_DEFAULT_ERR_LED_PIN 4
#endif
#endif
/**********************************************
* Gateway inclusion button/mode configuration
**********************************************/
// Enabled inclusion mode feature
//#define MY_INCLUSION_MODE_FEATURE
// Enables inclusion-mode button feature on the gateway device
//#define MY_INCLUSION_BUTTON_FEATURE
// Disable inclusion mode button if inclusion mode feature is not enabled
#ifndef MY_INCLUSION_MODE_FEATURE
#undef MY_INCLUSION_BUTTON_FEATURE
#endif
// The default input pin used for the inclusion mode button
#ifndef MY_INCLUSION_MODE_BUTTON_PIN
#if defined(ARDUINO_ARCH_ESP8266)
#define MY_INCLUSION_MODE_BUTTON_PIN 5
#else
#define MY_INCLUSION_MODE_BUTTON_PIN 3
#endif
#endif
// Number of seconds (default one minute) inclusion mode should be enabled
#ifndef MY_INCLUSION_MODE_DURATION
#define MY_INCLUSION_MODE_DURATION 60
#endif
/**********************************
* Message Signing Settings
***********************************/
// Disable to completly disable signing functionality in library
//#define MY_SIGNING_FEATURE
// Enable one of these in sketch to use the signing functionality
//#define MY_SIGNING_ATSHA204
//#define MY_SIGNING_SOFT
// Define a suitable timeout for a signature verification session
// Consider the turnaround from a nonce being generated to a signed message being received
// Consider the turn-around from a nonce being generated to a signed message being received
// which might vary, especially in networks with many hops. 5s ought to be enough for anyone.
#ifndef MY_VERIFICATION_TIMEOUT_MS
#define MY_VERIFICATION_TIMEOUT_MS 5000
#endif
// Enable to turn on whitelisting
// Enable to turn on white-listing
// When enabled, a signing node will salt the signature with it's unique signature and nodeId.
// The verifying node will look up the sender in a local table of trusted nodes and
// do the corresponding salting in order to verify the signature.
// For this reason, if whitelisting is enabled on one of the nodes in a sign-verify pair, both
// nodes have to implement whitelisting for this to work.
// Note that a node can still transmit a non-salted message (i.e. have whitelisting disabled)
// to a node that has whitelisting enabled (assuming the receiver does not have a matching entry
// for the sender in it's whitelist)
//#define MY_SECURE_NODE_WHITELISTING
// For this reason, if white listing is enabled on one of the nodes in a sign-verify pair, both
// nodes have to implement white listing for this to work.
// Note that a node can still transmit a non-salted message (i.e. have white listing disabled)
// to a node that has white listing enabled (assuming the receiver does not have a matching entry
// for the sender in it's white list)
//#define MY_SIGNING_NODE_WHITELIST {{.nodeId = GATEWAY_ADDRESS,.serial = {0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01}}}
// MySigningAtsha204 default setting
#define MY_ATSHA204_PIN 17 // A3 - pin where ATSHA204 is attached
// Atsha204 default pin setting
#ifndef MY_SIGNING_ATSHA204_PIN
#define MY_SIGNING_ATSHA204_PIN 17 // A3 - pin where ATSHA204 is attached
#endif
// MySigningAtsha204Soft default settings
#define MY_RANDOMSEED_PIN 7 // A7 - Pin used for random generation (do not connect anything to this)
// Pin used for random generation in soft signing (do not connect anything to this when enabled)
#ifndef MY_SIGNING_SOFT_RANDOMSEED_PIN
#define MY_SIGNING_SOFT_RANDOMSEED_PIN 7 // A7 -
#endif
// Set the soft_serial value to an arbitrary value for proper security
#ifndef MY_SIGNING_SOFT_SERIAL
#define MY_SIGNING_SOFT_SERIAL 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09
#endif
// Key to use for HMAC calculation in MySigningAtsha204Soft (32 bytes)
#define MY_HMAC_KEY 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
#ifndef MY_SIGNING_SOFT_HMAC_KEY
#define MY_SIGNING_SOFT_HMAC_KEY 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
#endif
/**********************************
* NRF24L01 Driver Defaults
***********************************/
#define RF24_CE_PIN 9
#define RF24_CS_PIN 10
#define RF24_PA_LEVEL RF24_PA_MAX
#define RF24_PA_LEVEL_GW RF24_PA_LOW
// RF channel for the sensor net, 0-127
#define RF24_CHANNEL 76
//RF24_250KBPS for 250kbs, RF24_1MBPS for 1Mbps, or RF24_2MBPS for 2Mbps
#define RF24_DATARATE RF24_250KBPS
// This is also act as base value for sensor nodeId addresses. Change this (or channel) if you have more than one sensor network.
#define RF24_BASE_RADIO_ID ((uint64_t)0xA8A8E1FC00LL)
// Enable SOFTSPI for NRF24L01 when using the W5100 Ethernet module
//#define SOFTSPI
#ifdef SOFTSPI
// Define the soft SPI pins used for NRF radio
const uint8_t SOFT_SPI_MISO_PIN = 16;
const uint8_t SOFT_SPI_MOSI_PIN = 15;
const uint8_t SOFT_SPI_SCK_PIN = 14;
// Enables RF24 encryption (all nodes and gateway must have this enabled)
//#define MY_RF24_ENABLE_ENCRYPTION
// Default encryption key. Override in sketch if needed.
#ifndef MY_RF24_ENCRYPTKEY
#define MY_RF24_ENCRYPTKEY 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x10,0x11,0x12,0x13,0x14,0x15,0x16
#endif
// Default pin settings. Override in sketch if needed.
#ifndef MY_RF24_CE_PIN
#if defined(ARDUINO_ARCH_ESP8266)
#define MY_RF24_CE_PIN 4
#else
#define MY_RF24_CE_PIN 9
#endif
#endif
#ifndef MY_RF24_CS_PIN
#if defined(ARDUINO_ARCH_ESP8266)
#define MY_RF24_CS_PIN 15
#else
#define MY_RF24_CS_PIN 10
#endif
#endif
#ifndef MY_RF24_PA_LEVEL
#define MY_RF24_PA_LEVEL RF24_PA_MAX
#endif
// RF channel for the sensor net, 0-127
#ifndef MY_RF24_CHANNEL
#define MY_RF24_CHANNEL 76
#endif
//RF24_250KBPS for 250kbs, RF24_1MBPS for 1Mbps, or RF24_2MBPS for 2Mbps
#ifndef MY_RF24_DATARATE
#define MY_RF24_DATARATE RF24_250KBPS
#endif
// This is also act as base value for sensor nodeId addresses. Change this (or channel) if you have more than one sensor network.
#ifndef MY_RF24_BASE_RADIO_ID
#define MY_RF24_BASE_RADIO_ID ((uint64_t)0xA8A8E1FC00LL)
#endif
// Enable SOFTSPI for NRF24L01, useful for the W5100 Ethernet module
//#define MY_SOFTSPI
#ifndef MY_SOFT_SPI_SCK_PIN
#define MY_SOFT_SPI_SCK_PIN 14
#endif
#ifndef MY_SOFT_SPI_MISO_PIN
#define MY_SOFT_SPI_MISO_PIN 16
#endif
#ifndef MY_SOFT_SPI_MOSI_PIN
#define MY_SOFT_SPI_MOSI_PIN 15
#endif
/**********************************
* RFM69 Driver Defaults
***********************************/
// Default network id. Use the same for all nodes that will talk to each other
#define RFM69_NETWORKID 100
// Default frequency to use. This must match the hardware version of the RFM69 radio (uncomment one):
// #define RFM69_FREQUENCY RF69_433MHZ
#define RFM69_FREQUENCY RF69_868MHZ
//#define FREQUENCY RF69_915MHZ
#ifndef MY_RFM69_FREQUENCY
// #define MY_RFM69_FREQUENCY RF69_433MHZ
#define MY_RFM69_FREQUENCY RF69_868MHZ
//#define MY_RFM69_FREQUENCY RF69_915MHZ
#endif
// Enable this if you're running the RFM69HW model
#ifndef MY_IS_RFM69HW
#define MY_IS_RFM69HW false
#endif
// Default network id. Use the same for all nodes that will talk to each other
#ifndef MY_RFM69_NETWORKID
#define MY_RFM69_NETWORKID 100
#endif
#ifndef MY_RF69_IRQ_PIN
#define MY_RF69_IRQ_PIN RF69_IRQ_PIN
#endif
#ifndef MY_RF69_SPI_CS
#define MY_RF69_SPI_CS RF69_SPI_CS
#endif
#ifndef MY_RF69_IRQ_NUM
#define MY_RF69_IRQ_NUM RF69_IRQ_NUM
#endif
// Enable this for encryption of packets
//#define RFM69_ENABLE_ENCRYPTION
#define RFM69_ENCRYPTKEY "sampleEncryptKey" //exactly the same 16 characters/bytes on all nodes!
//#define MY_RFM69_ENABLE_ENCRYPTION
#ifndef MY_RFM69_ENCRYPTKEY
#define MY_RFM69_ENCRYPTKEY "sampleEncryptKey" //exactly the same 16 characters/bytes on all nodes!
#endif
/**************************************
* Ethernet Gateway Transport Defaults
***************************************/
// The gateway options available
//#define MY_GATEWAY_W5100
//#define MY_GATEWAY_ENC28J60
//#define MY_GATEWAY_ESP8266
// The port to open on controller or gateway
#ifndef MY_PORT
#define MY_PORT 5003
#endif
// Static ip address of gateway (if this is disabled, DHCP will be used)
//#define MY_IP_ADDRESS 192,168,178,66
// Enables UDP mode for Ethernet gateway (W5100)
//#define MY_USE_UDP
// DHCP, default renewal setting
#ifndef MY_IP_RENEWAL_INTERVAL
#define MY_IP_RENEWAL_INTERVAL 60000
#endif
#ifndef MY_MAC_ADDRESS
#define MY_MAC_ADDRESS 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
#endif
// Controller ip-address, if this is defined, gateway will act as a client trying to contact controller on MY_PORT.
// If MY_CONTROLLER_IP_ADDRESS is left un-defined, gateway acts as server allowing incoming connections.
//#define MY_CONTROLLER_IP_ADDRESS 192, 168, 178, 254
#endif
-54
View File
@@ -1,54 +0,0 @@
/**
* The MySensors Arduino library handles the wireless radio link and protocol
* between your home built sensors/actuators and HA controller of choice.
* The sensors forms a self healing radio network with optional repeaters. Each
* repeater and gateway builds a routing tables in EEPROM which keeps track of the
* network topology allowing messages to be routed to nodes.
*
* Created by Henrik Ekblad <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 MyHw_h
#define MyHw_h
#include <stdint.h>
#include <stddef.h>
#include "MyConfig.h"
/**
* The Hw contains all hardware specific details of the platform
*/
class MyHw
{
public:
// MyHw constructor
MyHw();
/* // The following is defined as macros to save space
virtual void init() = 0;
virtual void watchdogReset() = 0;
virtual void reboot() = 0;
virtual unsigned long millisec() = 0;
virtual uint8_t readConfig(uint8_t pos) = 0;
virtual void writeConfig(uint8_t pos, uint8_t value) = 0;
virtual void readConfigBlock(void* buf, void * pos, size_t length) = 0;
virtual void writeConfigBlock(void* pos, void* buf, uint16_t length) = 0; */
virtual void sleep(unsigned long ms) = 0;
virtual bool sleep(uint8_t interrupt, uint8_t mode, unsigned long ms) = 0;
virtual uint8_t sleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2, unsigned long ms) = 0;
#ifdef DEBUG
virtual void debugPrint(bool isGW, const char *fmt, ... ) = 0;
#endif
};
#endif
-86
View File
@@ -1,86 +0,0 @@
/**
* The MySensors Arduino library handles the wireless radio link and protocol
* between your home built sensors/actuators and HA controller of choice.
* The sensors forms a self healing radio network with optional repeaters. Each
* repeater and gateway builds a routing tables in EEPROM which keeps track of the
* network topology allowing messages to be routed to nodes.
*
* Created by Henrik Ekblad <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.
*/
#ifdef ARDUINO_ARCH_ESP8266
#ifndef MyHwESP8266_h
#define MyHwESP8266_h
#include "MyHw.h"
#include "MyConfig.h"
#include "MyMessage.h"
#ifdef __cplusplus
#include <Arduino.h>
#include <SPI.h>
#endif
// Define these as macros to save valuable space
#define hw_digitalWrite(__pin, __value) (digitalWrite(__pin, __value))
#define hw_init() Serial.begin(BAUD_RATE)
#define hw_watchdogReset() wdt_reset()
#define hw_reboot() wdt_enable(WDTO_15MS); while (1)
#define hw_millis() millis()
void hw_readConfigBlock(void* buf, void* adr, size_t length);
void hw_writeConfigBlock(void* buf, void* adr, size_t length);
void hw_writeConfig(int adr, uint8_t value);
uint8_t hw_readConfig(int adr);
enum period_t
{
SLEEP_15Ms,
SLEEP_30MS,
SLEEP_60MS,
SLEEP_120MS,
SLEEP_250MS,
SLEEP_500MS,
SLEEP_1S,
SLEEP_2S,
SLEEP_4S,
SLEEP_8S,
SLEEP_FOREVER
};
class MyHwESP8266 : public MyHw
{
public:
MyHwESP8266();
/* void init();
void watchdogReset();
void reboot();
unsigned long millis();
uint8_t readConfig(uint8_t pos);
void writeConfig(uint8_t pos, uint8_t value);
void readConfigBlock(void* buf, void * pos, size_t length);
void writeConfigBlock(void* pos, void* buf, size_t length); */
void sleep(unsigned long ms);
bool sleep(uint8_t interrupt, uint8_t mode, unsigned long ms);
uint8_t sleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2, unsigned long ms);
#ifdef DEBUG
void debugPrint(bool isGW, const char *fmt, ... );
#endif
};
#endif
#endif // #ifdef ARDUINO_ARCH_ESP8266
-887
View File
@@ -1,887 +0,0 @@
/**
* The MySensors Arduino library handles the wireless radio link and protocol
* between your home built sensors/actuators and HA controller of choice.
* The sensors forms a self healing radio network with optional repeaters. Each
* repeater and gateway builds a routing tables in EEPROM which keeps track of the
* network topology allowing messages to be routed to nodes.
*
* Created by Henrik Ekblad <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 "MySensor.h"
#define DISTANCE_INVALID (0xFF)
#ifdef MY_SIGNING_FEATURE
// Macros for manipulating signing requirement table
#define DO_SIGN(node) (node == 0 ? (~doSign[0]&1) : (~doSign[node>>4]&(node%16)))
#define SET_SIGN(node) (node == 0 ? (doSign[0]&=~1) : (doSign[node>>4]&=~(node%16)))
#define CLEAR_SIGN(node) (node == 0 ? (doSign[0]|=1) : (doSign[node>>4]|=(node%16)))
#endif
// Inline function and macros
static inline MyMessage& build (MyMessage &msg, uint8_t sender, uint8_t destination, uint8_t sensor, uint8_t command, uint8_t type, bool enableAck) {
msg.sender = sender;
msg.destination = destination;
msg.sensor = sensor;
msg.type = type;
mSetCommand(msg,command);
mSetRequestAck(msg,enableAck);
mSetAck(msg,false);
return msg;
}
static inline bool isValidParent( const uint8_t parent ) {
return parent != AUTO;
}
static inline bool isValidDistance( const uint8_t distance ) {
return distance != DISTANCE_INVALID;
}
MySensor::MySensor(MyTransport &_radio, MyHw &_hw
#ifdef MY_SIGNING_FEATURE
, MySigning &_signer
#endif
#ifdef WITH_LEDS_BLINKING
, uint8_t _rx, uint8_t _tx, uint8_t _er, unsigned long _blink_period
#endif
)
:
radio(_radio),
#ifdef MY_SIGNING_FEATURE
signer(_signer),
#endif
#ifdef WITH_LEDS_BLINKING
pinRx(_rx), pinTx(_tx), pinEr(_er), ledBlinkPeriod(_blink_period),
#endif
#ifdef MY_OTA_FIRMWARE_FEATURE
flash(MY_OTA_FLASH_SS, MY_OTA_FLASH_JDECID),
#endif
hw(_hw)
{
}
#ifdef MY_OTA_FIRMWARE_FEATURE
// do a crc16 on the whole received firmware
bool MySensor::isValidFirmware() {
// init crc
uint16_t crc = ~0;
for (uint16_t i = 0; i < fc.blocks * FIRMWARE_BLOCK_SIZE; ++i) {
crc ^= flash.readByte(i + FIRMWARE_START_OFFSET);
for (int8_t j = 0; j < 8; ++j) {
if (crc & 1)
crc = (crc >> 1) ^ 0xA001;
else
crc = (crc >> 1);
}
}
return crc == fc.crc;
}
#endif
#ifdef WITH_LEDS_BLINKING
void MySensor::handleLedsBlinking() {
// Just return if it is not the time...
// http://playground.arduino.cc/Code/TimingRollover
if ((long)(hw_millis() - blink_next_time) < 0)
return;
else
blink_next_time = hw_millis() + ledBlinkPeriod;
// do the actual blinking
if(countRx && countRx != 255) {
// switch led on
hw_digitalWrite(pinRx, LED_ON);
}
else if(!countRx) {
// switching off
hw_digitalWrite(pinRx, LED_OFF);
}
if(countRx != 255)
--countRx;
if(countTx && countTx != 255) {
// switch led on
hw_digitalWrite(pinTx, LED_ON);
}
else if(!countTx) {
// switching off
hw_digitalWrite(pinTx, LED_OFF);
}
if(countTx != 255)
--countTx;
if(countErr && countErr != 255) {
// switch led on
hw_digitalWrite(pinEr, LED_ON);
}
else if(!countErr) {
// switching off
hw_digitalWrite(pinEr, LED_OFF);
}
if(countErr != 255)
--countErr;
}
void MySensor::rxBlink(uint8_t cnt) {
if(countRx == 255) { countRx = cnt; }
}
void MySensor::txBlink(uint8_t cnt) {
if(countTx == 255) { countTx = cnt; }
}
void MySensor::errBlink(uint8_t cnt) {
if(countErr == 255) { countErr = cnt; }
}
#endif
void MySensor::begin(void (*_msgCallback)(const MyMessage &), uint8_t _nodeId, boolean _repeaterMode, uint8_t _parentNodeId) {
#ifdef ENABLED_SERIAL
hw_init();
#endif
repeaterMode = _repeaterMode;
msgCallback = _msgCallback;
failedTransmissions = 0;
// Only gateway should use node id 0!
isGateway = _nodeId == GATEWAY_ADDRESS;
// Setup radio
if (!radio.init()) {
debug(PSTR("radio init fail\n"));
while(1); // Nothing more we can do
}
#ifdef MY_SIGNING_FEATURE
// Read out the signing requirements from EEPROM
hw_readConfigBlock((void*)doSign, (void*)EEPROM_SIGNING_REQUIREMENT_TABLE_ADDRESS, sizeof(doSign));
#endif
#ifdef WITH_LEDS_BLINKING
// Setup led pins
pinMode(pinRx, OUTPUT);
pinMode(pinTx, OUTPUT);
pinMode(pinEr, OUTPUT);
// Set initial state of leds
hw_digitalWrite(pinRx, LED_OFF);
hw_digitalWrite(pinTx, LED_OFF);
hw_digitalWrite(pinEr, LED_OFF);
// initialize counters
countRx = 0;
countTx = 0;
countErr = 0;
#endif
if (isGateway) {
// Set configuration for gateway
nc.parentNodeId = GATEWAY_ADDRESS;
nc.distance = 0;
nc.nodeId = GATEWAY_ADDRESS;
} else {
// Read settings from eeprom
hw_readConfigBlock((void*)&nc, (void*)EEPROM_NODE_ID_ADDRESS, sizeof(NodeConfig));
// Read latest received controller configuration from EEPROM
hw_readConfigBlock((void*)&cc, (void*)EEPROM_CONTROLLER_CONFIG_ADDRESS, sizeof(ControllerConfig));
#ifdef MY_OTA_FIRMWARE_FEATURE
// Read firmware config from EEPROM, i.e. type, version, CRC, blocks
hw_readConfigBlock((void*)&fc, (void*)EEPROM_FIRMWARE_TYPE_ADDRESS, sizeof(NodeFirmwareConfig));
#endif
if (cc.isMetric == 0xff) {
// Eeprom empty, set default to metric
cc.isMetric = 0x01;
}
autoFindParent = _parentNodeId == AUTO;
if (!autoFindParent) {
nc.parentNodeId = _parentNodeId;
// Save static parent id in eeprom (used by bootloader)
hw_writeConfig(EEPROM_PARENT_NODE_ID_ADDRESS, _parentNodeId);
// We don't actually know the distance to gw here. Let's pretend it is 1.
// If the current node is also repeater, be aware of this.
nc.distance = 1;
} else if (!isValidParent(nc.parentNodeId)) {
// Auto find parent, but parent in eeprom is invalid. Try find one.
findParentNode();
}
if (_nodeId != AUTO) {
// Set static id
nc.nodeId = _nodeId;
// Save static id in eeprom
hw_writeConfig(EEPROM_NODE_ID_ADDRESS, _nodeId);
} else if (nc.nodeId == AUTO && isValidParent(nc.parentNodeId)) {
// Try to fetch node-id from gateway
requestNodeId();
}
}
setupNode();
debug(PSTR("%s started, id=%d, parent=%d, distance=%d\n"), isGateway?"gateway":(repeaterMode?"repeater":"sensor"), nc.nodeId, nc.parentNodeId, nc.distance);
}
uint8_t MySensor::getNodeId() {
return nc.nodeId;
}
ControllerConfig MySensor::getConfig() {
return cc;
}
void MySensor::requestNodeId() {
debug(PSTR("req id\n"));
radio.setAddress(nc.nodeId);
build(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_ID_REQUEST, false).set("");
sendWrite(nc.parentNodeId, msg);
wait(2000);
}
void MySensor::setupNode() {
// Open reading pipe for messages directed to this node (set write pipe to same)
radio.setAddress(nc.nodeId);
// Present node and request config
if (!isGateway && nc.nodeId != AUTO) {
#ifdef MY_SIGNING_FEATURE
// Notify gateway (and possibly controller) about the signing preferences of this node
sendRoute(build(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_REQUEST_SIGNING, false).set(signer.requestSignatures()));
// If we do require signing, wait for the gateway to tell us how it prefer us to transmit our messages
if (signer.requestSignatures()) {
wait(2000);
}
#endif
// Send presentation for this radio node (attach
present(NODE_SENSOR_ID, repeaterMode? S_ARDUINO_REPEATER_NODE : S_ARDUINO_NODE);
// Send a configuration exchange request to controller
// Node sends parent node. Controller answers with latest node configuration
// which is picked up in process()
sendRoute(build(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_CONFIG, false).set(nc.parentNodeId));
// Wait configuration reply.
wait(2000);
#ifdef MY_OTA_FIRMWARE_FEATURE
RequestFirmwareConfig *reqFWConfig = (RequestFirmwareConfig *)msg.data;
mSetLength(msg, sizeof(RequestFirmwareConfig));
mSetCommand(msg, C_STREAM);
mSetPayloadType(msg,P_CUSTOM);
// copy node settings to reqFWConfig
memcpy(reqFWConfig,&fc,sizeof(NodeFirmwareConfig));
// add bootloader information
reqFWConfig->BLVersion = MY_OTA_BOOTLOADER_VERSION;
fwUpdateOngoing = false;
sendRoute(build(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_STREAM, ST_FIRMWARE_CONFIG_REQUEST, false));
#endif
}
}
void MySensor::findParentNode() {
static boolean findingParentNode = false;
if (findingParentNode)
return;
findingParentNode = true;
failedTransmissions = 0;
// Set distance to max
nc.distance = 255;
// Send ping message to BROADCAST_ADDRESS (to which all relaying nodes and gateway listens and should reply to)
debug(PSTR("find parent\n"));
build(msg, nc.nodeId, BROADCAST_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_FIND_PARENT, false).set("");
// Write msg, but suppress recursive parent search
sendWrite(BROADCAST_ADDRESS, msg);
// Wait for ping response.
wait(2000);
findingParentNode = false;
}
boolean MySensor::sendRoute(MyMessage &message) {
uint8_t sender = message.sender;
uint8_t dest = message.destination;
uint8_t last = message.last;
bool ok;
// If we still don't have any parent id, re-request and skip this message.
if (nc.parentNodeId == AUTO) {
findParentNode();
#ifdef WITH_LEDS_BLINKING
errBlink(1);
#endif
return false;
}
// If we still don't have any node id, re-request and skip this message.
if (nc.nodeId == AUTO) {
requestNodeId();
#ifdef WITH_LEDS_BLINKING
errBlink(1);
#endif
return false;
}
mSetVersion(message, PROTOCOL_VERSION);
#ifdef MY_SIGNING_FEATURE
// If destination is known to require signed messages and we are the sender, sign this message unless it is an ACK or a handshake message
if (DO_SIGN(message.destination) && message.sender == nc.nodeId && !mGetAck(message) && mGetLength(message) &&
(mGetCommand(message) != C_INTERNAL ||
(message.type != I_GET_NONCE && message.type != I_GET_NONCE_RESPONSE && message.type != I_REQUEST_SIGNING &&
message.type != I_ID_REQUEST && message.type != I_ID_RESPONSE &&
message.type != I_FIND_PARENT && message.type != I_FIND_PARENT_RESPONSE))) {
bool signOk = false;
// Send nonce-request
if (!sendRoute(build(tmpMsg, nc.nodeId, message.destination, message.sensor, C_INTERNAL, I_GET_NONCE, false).set(""))) {
debug(PSTR("nonce tr err\n"));
return false;
}
// We have to wait for the nonce to arrive before we can sign our original message
// Other messages could come in-between. We trust process() takes care of them
unsigned long enter = hw_millis();
msgSign = message; // Copy the message to sign since message buffer might be touched in process()
while (hw_millis() - enter < MY_VERIFICATION_TIMEOUT_MS) {
if (process()) {
if (mGetCommand(getLastMessage()) == C_INTERNAL && getLastMessage().type == I_GET_NONCE_RESPONSE) {
// Proceed with signing if nonce has been received
if (signer.putNonce(getLastMessage()) && signer.signMsg(msgSign)) {
message = msgSign; // Write the signed message back
signOk = true;
}
break;
}
}
}
if (hw_millis() - enter > MY_VERIFICATION_TIMEOUT_MS) {
debug(PSTR("nonce tmo\n"));
#ifdef WITH_LEDS_BLINKING
errBlink(1);
#endif
return false;
}
if (!signOk) {
debug(PSTR("sign fail\n"));
#ifdef WITH_LEDS_BLINKING
errBlink(1);
#endif
return false;
}
// After this point, only the 'last' member of the message structure is allowed to be altered if the message has been signed,
// or signature will become invalid and the message rejected by the receiver
} else mSetSigned(message, 0); // Message is not supposed to be signed, make sure it is marked unsigned
#endif
if (dest == GATEWAY_ADDRESS || !repeaterMode) {
// Store this address in routing table (if repeater)
if (repeaterMode) {
hw_writeConfig(EEPROM_ROUTES_ADDRESS+sender, last);
}
// If destination is the gateway or if we aren't a repeater, let
// our parent take care of the message
ok = sendWrite(nc.parentNodeId, message);
} else {
// Relay the message
uint8_t route = hw_readConfig(EEPROM_ROUTES_ADDRESS+dest);
if (route > GATEWAY_ADDRESS && route < BROADCAST_ADDRESS) {
// This message should be forwarded to a child node. If we send message
// to this nodes pipe then all children will receive it because the are
// all listening to this nodes pipe.
//
// +----B
// -A
// +----C------D
//
// We're node C, Message comes from A and has destination D
//
// Message destination is not gateway and is in routing table for this node.
// Send it downstream
return sendWrite(route, message);
} else if (sender == GATEWAY_ADDRESS && dest == BROADCAST_ADDRESS) {
// Node has not yet received any id. We need to send it
// by doing a broadcast sending,
return sendWrite(BROADCAST_ADDRESS, message);
} else if (isGateway) {
// Destination isn't in our routing table and isn't a broadcast address
// Nothing to do here
return false;
} else {
// A message comes from a child node and we have no
// route for it.
//
// +----B
// -A
// +----C------D <-- Message comes from D
//
// We're node C
//
// Message should be passed to node A (this nodes relay)
// This message should be routed back towards sensor net gateway
ok = sendWrite(nc.parentNodeId, message);
// Add this child to our "routing table" if it not already exist
hw_writeConfig(EEPROM_ROUTES_ADDRESS+sender, last);
}
}
if (!ok) {
// Failure when sending to parent node. The parent node might be down and we
// need to find another route to gateway.
#ifdef WITH_LEDS_BLINKING
errBlink(1);
#endif
failedTransmissions++;
if (autoFindParent && failedTransmissions > SEARCH_FAILURES) {
findParentNode();
}
} else {
failedTransmissions = 0;
}
return ok;
}
boolean MySensor::sendWrite(uint8_t to, MyMessage &message) {
mSetVersion(message, PROTOCOL_VERSION);
uint8_t length = mGetSigned(message) ? MAX_MESSAGE_LENGTH : mGetLength(message);
message.last = nc.nodeId;
#ifdef WITH_LEDS_BLINKING
txBlink(1);
#endif
bool ok = radio.send(to, &message, min(MAX_MESSAGE_LENGTH, HEADER_SIZE + length));
debug(PSTR("send: %d-%d-%d-%d s=%d,c=%d,t=%d,pt=%d,l=%d,sg=%d,st=%s:%s\n"),
message.sender,message.last, to, message.destination, message.sensor, mGetCommand(message), message.type,
mGetPayloadType(message), mGetLength(message), mGetSigned(message), to==BROADCAST_ADDRESS ? "bc" : (ok ? "ok":"fail"), message.getString(convBuf));
return ok;
}
bool MySensor::send(MyMessage &message, bool enableAck) {
message.sender = nc.nodeId;
mSetCommand(message,C_SET);
mSetRequestAck(message,enableAck);
return sendRoute(message);
}
void MySensor::sendBatteryLevel(uint8_t value, bool enableAck) {
sendRoute(build(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_BATTERY_LEVEL, enableAck).set(value));
}
void MySensor::sendHeartbeat(void) {
sendRoute(build(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_HEARTBEAT, false).set(heartbeat++));
}
void MySensor::present(uint8_t childSensorId, uint8_t sensorType, const char *description, bool enableAck) {
sendRoute(build(msg, nc.nodeId, GATEWAY_ADDRESS, childSensorId, C_PRESENTATION, sensorType, enableAck).set(childSensorId==NODE_SENSOR_ID?LIBRARY_VERSION:description));
}
void MySensor::sendSketchInfo(const char *name, const char *version, bool enableAck) {
if (name != NULL) {
sendRoute(build(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_SKETCH_NAME, enableAck).set(name));
}
if (version != NULL) {
sendRoute(build(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_SKETCH_VERSION, enableAck).set(version));
}
}
void MySensor::request(uint8_t childSensorId, uint8_t variableType, uint8_t destination) {
sendRoute(build(msg, nc.nodeId, destination, childSensorId, C_REQ, variableType, false).set(""));
}
void MySensor::requestTime(void (* _timeCallback)(unsigned long)) {
timeCallback = _timeCallback;
sendRoute(build(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_TIME, false).set(""));
}
boolean MySensor::process() {
hw_watchdogReset();
#ifdef WITH_LEDS_BLINKING
handleLedsBlinking();
#endif
uint8_t to = 0;
if (!radio.available(&to))
{
#ifdef MY_OTA_FIRMWARE_FEATURE
unsigned long enter = hw_millis();
if (fwUpdateOngoing && (enter - fwLastRequestTime > MY_OTA_RETRY_DELAY)) {
if (!fwRetry) {
debug(PSTR("fw upd fail\n"));
// Give up. We have requested MY_OTA_RETRY times without any packet in return.
fwUpdateOngoing = false;
#ifdef WITH_LEDS_BLINKING
errBlink(1);
#endif
return false;
}
fwRetry--;
fwLastRequestTime = enter;
// Time to (re-)request firmware block from controller
RequestFWBlock *firmwareRequest = (RequestFWBlock *)msg.data;
mSetLength(msg, sizeof(RequestFWBlock));
firmwareRequest->type = fc.type;
firmwareRequest->version = fc.version;
firmwareRequest->block = (fwBlock - 1);
sendRoute(build(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_STREAM, ST_FIRMWARE_REQUEST, false));
}
#endif
return false;
}
#ifdef MY_SIGNING_FEATURE
(void)signer.checkTimer(); // Manage signing timeout
#endif
uint8_t len = radio.receive((uint8_t *)&msg);
(void)len; //until somebody makes use of 'len'
#ifdef WITH_LEDS_BLINKING
rxBlink(1);
#endif
#ifdef MY_SIGNING_FEATURE
// Before processing message, reject unsigned messages if signing is required and check signature (if it is signed and addressed to us)
// Note that we do not care at all about any signature found if we do not require signing, nor do we care about ACKs (they are never signed)
if ((!isGateway || DO_SIGN(msg.sender)) && signer.requestSignatures() && msg.destination == nc.nodeId && mGetLength(msg) && !mGetAck(msg) &&
(mGetCommand(msg) != C_INTERNAL ||
(msg.type != I_GET_NONCE_RESPONSE && msg.type != I_GET_NONCE && msg.type != I_REQUEST_SIGNING &&
msg.type != I_ID_REQUEST && msg.type != I_ID_RESPONSE &&
msg.type != I_FIND_PARENT && msg.type != I_FIND_PARENT_RESPONSE))) {
if (!mGetSigned(msg)) {
// Got unsigned message that should have been signed
debug(PSTR("no sign\n"));
#ifdef WITH_LEDS_BLINKING
errBlink(1);
#endif
return false;
}
else if (!signer.verifyMsg(msg)) {
debug(PSTR("verify fail\n"));
#ifdef WITH_LEDS_BLINKING
errBlink(1);
#endif
return false; // This signed message has been tampered with!
}
}
#endif
// Add string termination, good if we later would want to print it.
msg.data[mGetLength(msg)] = '\0';
debug(PSTR("read: %d-%d-%d s=%d,c=%d,t=%d,pt=%d,l=%d,sg=%d:%s\n"),
msg.sender, msg.last, msg.destination, msg.sensor, mGetCommand(msg), msg.type, mGetPayloadType(msg), mGetLength(msg), mGetSigned(msg), msg.getString(convBuf));
mSetSigned(msg,0); // Clear the sign-flag now as verification (and debug printing) is completed
if(!(mGetVersion(msg) == PROTOCOL_VERSION)) {
debug(PSTR("ver mismatch\n"));
#ifdef WITH_LEDS_BLINKING
errBlink(1);
#endif
return false;
}
uint8_t command = mGetCommand(msg);
uint8_t type = msg.type;
uint8_t sender = msg.sender;
uint8_t last = msg.last;
uint8_t destination = msg.destination;
if (destination == nc.nodeId) {
// This message is addressed to this node
if (repeaterMode && last != nc.parentNodeId) {
// Message is from one of the child nodes. Add it to routing table.
hw_writeConfig(EEPROM_ROUTES_ADDRESS+sender, last);
}
// Check if sender requests an ack back.
if (mGetRequestAck(msg)) {
// Copy message
tmpMsg = msg;
mSetRequestAck(tmpMsg,false); // Reply without ack flag (otherwise we would end up in an eternal loop)
mSetAck(tmpMsg,true);
tmpMsg.sender = nc.nodeId;
tmpMsg.destination = msg.sender;
sendRoute(tmpMsg);
}
if (command == C_INTERNAL) {
if (type == I_FIND_PARENT_RESPONSE) {
if (autoFindParent) {
// We've received a reply to a FIND_PARENT message. Check if the distance is
// shorter than we already have.
uint8_t distance = msg.getByte();
if (isValidDistance(distance))
{
// Distance to gateway is one more for us w.r.t. parent
distance++;
if (isValidDistance(distance) && (distance < nc.distance)) {
// Found a neighbor closer to GW than previously found
nc.distance = distance;
nc.parentNodeId = msg.sender;
hw_writeConfig(EEPROM_PARENT_NODE_ID_ADDRESS, nc.parentNodeId);
hw_writeConfig(EEPROM_DISTANCE_ADDRESS, nc.distance);
debug(PSTR("parent=%d, d=%d\n"), nc.parentNodeId, nc.distance);
}
}
}
return false;
#ifdef MY_SIGNING_FEATURE
} else if (type == I_GET_NONCE) {
if (signer.getNonce(msg)) {
sendRoute(build(msg, nc.nodeId, msg.sender, NODE_SENSOR_ID, C_INTERNAL, I_GET_NONCE_RESPONSE, false));
}
return false; // Nonce exchange is an internal MySensor protocol message, no need to inform caller about this
} else if (type == I_REQUEST_SIGNING) {
if (msg.getBool()) {
// We received an indicator that the sender require us to sign all messages we send to it
SET_SIGN(msg.sender);
} else {
// We received an indicator that the sender does not require us to sign all messages we send to it
CLEAR_SIGN(msg.sender);
}
// Save updated table
hw_writeConfigBlock((void*)doSign, (void*)EEPROM_SIGNING_REQUIREMENT_TABLE_ADDRESS, sizeof(doSign));
// Inform sender about our preference if we are a gateway, but only require signing if the sender required signing
// We do not currently want a gateway to require signing from all nodes in a network just because it wants one node
// to sign it's messages
if (isGateway) {
if (signer.requestSignatures() && DO_SIGN(msg.sender))
sendRoute(build(msg, nc.nodeId, msg.sender, NODE_SENSOR_ID, C_INTERNAL, I_REQUEST_SIGNING, false).set(true));
else
sendRoute(build(msg, nc.nodeId, msg.sender, NODE_SENSOR_ID, C_INTERNAL, I_REQUEST_SIGNING, false).set(false));
}
return false; // Signing request is an internal MySensor protocol message, no need to inform caller about this
} else if (type == I_GET_NONCE_RESPONSE) {
return true; // Just pass along nonce silently (no need to call callback for these)
#endif
} else if (sender == GATEWAY_ADDRESS) {
bool isMetric;
if (type == I_REBOOT) {
// Requires MySensors or other bootloader with watchdogs enabled
hw_reboot();
} else if (type == I_ID_RESPONSE) {
if (nc.nodeId == AUTO) {
nc.nodeId = msg.getByte();
if (nc.nodeId == AUTO) {
// sensor net gateway will return max id if all sensor id are taken
debug(PSTR("full\n"));
while (1); // Wait here. Nothing else we can do...
}
setupNode();
// Write id to EEPROM
hw_writeConfig(EEPROM_NODE_ID_ADDRESS, nc.nodeId);
debug(PSTR("id=%d\n"), nc.nodeId);
}
} else if (type == I_CONFIG) {
// Pick up configuration from controller (currently only metric/imperial)
// and store it in eeprom if changed
isMetric = msg.getString()[0] == 'M' ;
cc.isMetric = isMetric;
hw_writeConfig(EEPROM_CONTROLLER_CONFIG_ADDRESS, isMetric);
} else if (type == I_CHILDREN) {
if (repeaterMode && msg.getString()[0] == 'C') {
// Clears child relay data for this node
debug(PSTR("clear\n"));
uint8_t i = 255;
do {
hw_writeConfig(EEPROM_ROUTES_ADDRESS+i, 0xff);
} while (i--);
// Clear parent node id & distance to gw
hw_writeConfig(EEPROM_PARENT_NODE_ID_ADDRESS, 0xFF);
hw_writeConfig(EEPROM_DISTANCE_ADDRESS, 0xFF);
// Find parent node
findParentNode();
sendRoute(build(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_CHILDREN,false).set(""));
}
} else if (type == I_TIME) {
if (timeCallback != NULL) {
// Deliver time to callback
timeCallback(msg.getULong());
}
}
return false;
}
}
#ifdef MY_OTA_FIRMWARE_FEATURE
else if (command == C_STREAM) {
if (type == ST_FIRMWARE_CONFIG_RESPONSE) {
NodeFirmwareConfig *firmwareConfigResponse = (NodeFirmwareConfig *)msg.data;
// compare with current node configuration, if they differ, start fw fetch process
if (memcmp(&fc,firmwareConfigResponse,sizeof(NodeFirmwareConfig))) {
debug(PSTR("fw update\n"));
// copy new FW config
memcpy(&fc,firmwareConfigResponse,sizeof(NodeFirmwareConfig));
// Init flash
if (!flash.initialize()) {
debug(PSTR("flash init fail\n"));
fwUpdateOngoing = false;
} else {
// erase lower 32K -> max flash size for ATMEGA328
flash.blockErase32K(0);
// wait until flash erased
while ( flash.busy() );
fwBlock = fc.blocks;
fwUpdateOngoing = true;
// reset flags
fwRetry = MY_OTA_RETRY+1;
fwLastRequestTime = 0;
}
return false;
} else debug(PSTR("fw update skipped\n"));
} else if (type == ST_FIRMWARE_RESPONSE) {
if (fwUpdateOngoing) {
// Save block to flash
debug(PSTR("fw block %d\n"), fwBlock);
// extract FW block
ReplyFWBlock *firmwareResponse = (ReplyFWBlock *)msg.data;
// write to flash
flash.writeBytes( ((fwBlock - 1) * FIRMWARE_BLOCK_SIZE) + FIRMWARE_START_OFFSET, firmwareResponse->data, FIRMWARE_BLOCK_SIZE);
// wait until flash written
while ( flash.busy() );
fwBlock--;
if (!fwBlock) {
// We're finished! Do a checksum and reboot.
fwUpdateOngoing = false;
if (isValidFirmware()) {
debug(PSTR("fw checksum ok\n"));
// All seems ok, write size and signature to flash (DualOptiboot will pick this up and flash it)
uint16_t fwsize = FIRMWARE_BLOCK_SIZE * fc.blocks;
uint8_t OTAbuffer[10] = {'F','L','X','I','M','G',':',(uint8_t)(fwsize >> 8),(uint8_t)(fwsize & 0xff),':'};
flash.writeBytes(0, OTAbuffer, 10);
// Write the new firmware config to eeprom
hw_writeConfigBlock((void*)&fc, (void*)EEPROM_FIRMWARE_TYPE_ADDRESS, sizeof(NodeFirmwareConfig));
hw_reboot();
} else {
debug(PSTR("fw checksum fail\n"));
}
}
// reset flags
fwRetry = MY_OTA_RETRY+1;
fwLastRequestTime = 0;
} else {
debug(PSTR("No fw update ongoing\n"));
}
return false;
}
}
#endif
// Call incoming message callback if available
if (msgCallback != NULL) {
msgCallback(msg);
}
// Return true if message was addressed for this node...
return true;
} else if (repeaterMode && nc.nodeId != AUTO) {
// If this node have an id, relay the message
if (command == C_INTERNAL && type == I_FIND_PARENT) {
if (sender != nc.parentNodeId) {
if (nc.distance == DISTANCE_INVALID)
findParentNode();
if (nc.distance != DISTANCE_INVALID) {
// Relaying nodes should always answer ping messages
// Wait a random delay of 0-2 seconds to minimize collision
// between ping ack messages from other relaying nodes
wait(hw_millis() & 0x3ff);
sendWrite(sender, build(msg, nc.nodeId, sender, NODE_SENSOR_ID, C_INTERNAL, I_FIND_PARENT_RESPONSE, false).set(nc.distance));
}
}
} else if (to == nc.nodeId) {
// We should try to relay this message to another node
sendRoute(msg);
}
}
return false;
}
MyMessage& MySensor::getLastMessage() {
return msg;
}
void MySensor::saveState(uint8_t pos, uint8_t value) {
hw_writeConfig(EEPROM_LOCAL_CONFIG_ADDRESS+pos, value);
}
uint8_t MySensor::loadState(uint8_t pos) {
return hw_readConfig(EEPROM_LOCAL_CONFIG_ADDRESS+pos);
}
void MySensor::wait(unsigned long ms) {
unsigned long enter = hw_millis();
while (hw_millis() - enter < ms) {
process();
}
}
void MySensor::sleep(unsigned long ms) {
#ifdef MY_OTA_FIRMWARE_FEATURE
if (fwUpdateOngoing) {
// Do not sleep node while fw update is ongoing
process();
} else {
#endif
radio.powerDown();
hw.sleep(ms);
#ifdef MY_OTA_FIRMWARE_FEATURE
}
#endif
}
bool MySensor::sleep(uint8_t interrupt, uint8_t mode, unsigned long ms) {
#ifdef MY_OTA_FIRMWARE_FEATURE
if (fwUpdateOngoing) {
// Do not sleep node while fw update is ongoing
process();
return false;
} else {
#endif
radio.powerDown();
return hw.sleep(interrupt, mode, ms) ;
#ifdef MY_OTA_FIRMWARE_FEATURE
}
#endif
}
int8_t MySensor::sleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2, unsigned long ms) {
#ifdef MY_OTA_FIRMWARE_FEATURE
if (fwUpdateOngoing) {
// Do not sleep node while fw update is ongoing
process();
return -1;
} else {
#endif
radio.powerDown();
return hw.sleep(interrupt1, mode1, interrupt2, mode2, ms) ;
#ifdef MY_OTA_FIRMWARE_FEATURE
}
#endif
}
+177 -369
View File
@@ -16,390 +16,198 @@
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*/
#ifndef MySensor_h
#define MySensor_h
#include "Version.h" // Auto generated by bot
#include "MyConfig.h"
#include "MyHw.h"
#include "MyTransport.h"
#include "MyTransportNRF24.h"
#include "MyParser.h"
#ifdef MY_SIGNING_FEATURE
#include "MySigning.h"
#include "MySigningNone.h"
#endif
#include "MyMessage.h"
#ifdef MY_OTA_FIRMWARE_FEATURE
#include "utility/SPIFlash.h"
#endif
#include <stddef.h>
#include <stdarg.h>
#include "core/MySensorCore.h"
// Detect node type
#if defined(MY_GATEWAY_SERIAL) || defined(MY_GATEWAY_W5100) || defined(MY_GATEWAY_ENC28J60) || defined(ARDUINO_ARCH_ESP8266) || defined(MY_GATEWAY_MQTT_CLIENT)
#define MY_GATEWAY_FEATURE
#define MY_IS_GATEWAY (true)
#define MY_NODE_TYPE "gateway"
#elif defined(MY_REPEATER_FEATURE)
#define MY_NODE_TYPE "repeater"
#else
#define MY_NODE_TYPE "sensor"
#endif
// Set the hardware driver to use (initialized by MySensor-class)
//#if defined __AVR_ATmega328P__
// Enable radio "feature" if one of the radio types was enabled
#if defined(MY_RADIO_NRF24) || defined(MY_RADIO_RFM69)
#define MY_RADIO_FEATURE
#endif
// HARDWARE
#if defined(ARDUINO_ARCH_ESP8266)
#include "MyHwESP8266.h"
typedef MyHwESP8266 MyHwDriver;
// Remove PSTR macros from debug prints
#undef PSTR
#define PSTR(x) (x)
//#undef F
//#define F(x) (x)
#include "core/MyHwESP8266.cpp"
// For ESP8266, we always enable gateway feature
#define MY_GATEWAY_ESP8266
#elif defined(ARDUINO_ARCH_AVR)
#include "MyHwATMega328.h"
typedef MyHwATMega328 MyHwDriver;
#include "core/MyHwATMega328.cpp"
#endif
//#endif
#ifdef DEBUG
#define debug(x,...) hw.debugPrint(isGateway, x, ##__VA_ARGS__)
// LEDS
#if defined(MY_LEDS_BLINKING_FEATURE)
#include "core/MyLeds.cpp"
#else
#define debug(x,...)
#include "core/MyLeds.h"
#endif
#ifdef WITH_LEDS_BLINKING_INVERSE
#define LED_ON 0x1
#define LED_OFF 0x0
// INCLUSION MODE
#if defined(MY_INCLUSION_MODE_FEATURE)
#include "core/MyInclusionMode.cpp"
#endif
// SIGNING
#if defined(MY_SIGNING_ATSHA204) || defined(MY_SIGNING_SOFT)
// SIGNING COMMON FUNCTIONS
#include "core/MySigning.cpp"
#define MY_SIGNING_FEATURE
#if defined(MY_SIGNING_ATSHA204) && defined(MY_SIGNING_SOFT)
#error Only one signing engine can be activated
#endif
#if defined(MY_SIGNING_ATSHA204)
#include "core/MySigningAtsha204.cpp"
#include "drivers/ATSHA204/ATSHA204.cpp"
#elif defined(MY_SIGNING_SOFT)
#include "core/MySigningAtsha204Soft.cpp"
#include "drivers/ATSHA204/sha256.cpp"
#endif
#endif
// GATEWAY - TRANSPORT
#if defined(MY_GATEWAY_MQTT_CLIENT)
// GATEWAY - COMMON FUNCTIONS
// We only support MQTT Client using W5100 and ESP8266 at the moment
#if !defined(MY_CONTROLLER_IP_ADDRESS)
#error You must specify MY_CONTROLLER_IP_ADDRESS (MQTT broker address)
#endif
#if !defined(MY_MQTT_TOPIC_PREFIX)
#error You must specify a topic prefix MY_MQTT_TOPIC_PREFIX for this MQTT client
#endif
#if !defined(MY_MQTT_CLIENT_ID)
#error You must define a unique MY_MQTT_CLIENT_ID for this MQTT client
#endif
#include "drivers/pubsubclient/src/PubSubClient.cpp"
#include "core/MyGatewayTransport.cpp"
#include "core/MyGatewayTransportMQTTClient.cpp"
#elif defined(MY_GATEWAY_FEATURE)
// GATEWAY - COMMON FUNCTIONS
#include "core/MyGatewayTransport.cpp"
// We currently only support one protocol at the moment, enable it.
#include "core/MyProtocolMySensors.cpp"
// GATEWAY - CONFIGURATION
#if defined(MY_RADIO_FEATURE)
// We assume that a gateway having a radio also should act as repeater
#define MY_REPEATER_FEATURE
#endif
#if defined(MY_CONTROLLER_IP_ADDRESS)
#define MY_GATEWAY_CLIENT_MODE
#endif
#if !defined(MY_PORT)
#error You must define MY_PORT (controller or gatway port to open)
#endif
#if defined(MY_GATEWAY_ESP8266)
// GATEWAY - ESP8266
#include "core/MyGatewayTransportEthernet.cpp"
#elif defined(MY_GATEWAY_W5100)
// GATEWAY - W5100
#include "core/MyGatewayTransportEthernet.cpp"
#elif defined(MY_GATEWAY_ENC28J60)
// GATEWAY - ENC28J60
#if defined(MY_USE_UDP)
#error UDP mode is not available for ENC28J60
#endif
#include "core/MyGatewayTransportEthernet.cpp"
#elif defined(MY_GATEWAY_SERIAL)
// GATEWAY - SERIAL
#include "core/MyGatewayTransportSerial.cpp"
#endif
#endif
// RADIO
#if defined(MY_RADIO_NRF24) || defined(MY_RADIO_RFM69)
// SOFTSPI
#ifdef MY_SOFTSPI
#if defined(ARDUINO_ARCH_ESP8266)
#error Soft SPI is not available on ESP8266
#endif
#include "drivers/AVR/DigitalIO/SoftI2cMaster.cpp"
#include "drivers/AVR/DigitalIO/PinIO.cpp"
#endif
// FLASH
#ifdef MY_OTA_FIRMWARE_FEATURE
#include "drivers/SPIFlash/SPIFlash.cpp"
#endif
#include "core/MyTransport.cpp"
#if defined(MY_RADIO_NRF24) && defined(MY_RADIO_RFM69)
#error Only one radio driver can be activated
#endif
#if defined(MY_RADIO_NRF24)
#if defined(MY_RF24_ENABLE_ENCRYPTION)
#include "drivers/AES/AES.cpp"
#endif
#include "drivers/RF24/RF24.cpp"
#include "core/MyTransportNRF24.cpp"
#elif defined(MY_RADIO_RFM69)
#include "drivers/RFM69/RFM69.cpp"
#include "core/MyTransportRFM69.cpp"
#endif
#endif
// Make sure to disable child features when parent feature is disabled
#if !defined(MY_RADIO_FEATURE)
#undef MY_OTA_FIRMWARE_FEATURE
#undef MY_REPEATER_FEATURE
#undef MY_SIGNING_NODE_WHITELISTING
#undef MY_SIGNING_FEATURE
#endif
#if !defined(MY_GATEWAY_FEATURE)
#undef MY_INCLUSION_MODE_FEATURE
#undef MY_INCLUSION_BUTTON_FEATURE
#endif
#if !defined(MY_GATEWAY_FEATURE) && !defined(MY_RADIO_FEATURE)
#error No radio or gateway feature activated. This means nowhere to send messages! Pretty pointless.
#endif
#include "core/MyMessage.cpp"
#include "core/MySensorCore.cpp"
extern void setup();
// Optional sketch functions called by MySensors library
void receive(const MyMessage &message) __attribute__((weak));
void receiveTime(unsigned long) __attribute__((weak));
void presentation() __attribute__((weak));
extern "C" void setup() __attribute__((weak));
extern "C" void loop() __attribute__((weak));
extern "C" void loop2() __attribute__((weak));
#include <Arduino.h>
#if defined(MY_GATEWAY_ESP8266)
#include "core/MyMainESP8266.cpp"
#else
#define LED_ON 0x0
#define LED_OFF 0x1
#include "core/MyMainDefault.cpp"
#endif
// EEPROM start address for mysensors library data
#define EEPROM_START 0
// EEPROM location of node id
#define EEPROM_NODE_ID_ADDRESS EEPROM_START
// EEPROM location of parent id
#define EEPROM_PARENT_NODE_ID_ADDRESS (EEPROM_START+1)
// EEPROM location of distance to gateway
#define EEPROM_DISTANCE_ADDRESS (EEPROM_PARENT_NODE_ID_ADDRESS+1)
#define EEPROM_ROUTES_ADDRESS (EEPROM_DISTANCE_ADDRESS+1) // Where to start storing routing information in EEPROM. Will allocate 256 bytes.
#define EEPROM_CONTROLLER_CONFIG_ADDRESS (EEPROM_ROUTES_ADDRESS+256) // Location of controller sent configuration (we allow one payload of config data from controller)
#define EEPROM_FIRMWARE_TYPE_ADDRESS (EEPROM_CONTROLLER_CONFIG_ADDRESS+24)
#define EEPROM_FIRMWARE_VERSION_ADDRESS (EEPROM_FIRMWARE_TYPE_ADDRESS+2)
#define EEPROM_FIRMWARE_BLOCKS_ADDRESS (EEPROM_FIRMWARE_VERSION_ADDRESS+2)
#define EEPROM_FIRMWARE_CRC_ADDRESS (EEPROM_FIRMWARE_BLOCKS_ADDRESS+2)
#define EEPROM_SIGNING_REQUIREMENT_TABLE_ADDRESS (EEPROM_FIRMWARE_CRC_ADDRESS+2)
#define EEPROM_LOCAL_CONFIG_ADDRESS (EEPROM_SIGNING_REQUIREMENT_TABLE_ADDRESS+32) // First free address for sketch static configuration
// Search for a new parent node after this many transmission failures
#define SEARCH_FAILURES 5
struct NodeConfig
{
uint8_t nodeId; // Current node id
uint8_t parentNodeId; // Where this node sends its messages
uint8_t distance; // This nodes distance to sensor net gateway (number of hops)
};
struct ControllerConfig {
uint8_t isMetric;
};
// Size of each firmware block
#define FIRMWARE_BLOCK_SIZE 16
// Number of times a firmware block should be requested before giving up
#define FIRMWARE_MAX_REQUESTS 5
// Number of times to request a fw block before giving up
#define MY_OTA_RETRY 5
// Number of millisecons before re-request a fw block
#define MY_OTA_RETRY_DELAY 500
// Start offset for firmware in flash (DualOptiboot wants to keeps a signature first)
#define FIRMWARE_START_OFFSET 10
// Bootloader version
#define MY_OTA_BOOTLOADER_MAJOR_VERSION 3
#define MY_OTA_BOOTLOADER_MINOR_VERSION 0
#define MY_OTA_BOOTLOADER_VERSION (MY_OTA_BOOTLOADER_MINOR_VERSION * 256 + MY_OTA_BOOTLOADER_MAJOR_VERSION)
// FW config structure, stored in eeprom
typedef struct {
uint16_t type;
uint16_t version;
uint16_t blocks;
uint16_t crc;
} __attribute__((packed)) NodeFirmwareConfig;
typedef struct {
uint16_t type;
uint16_t version;
uint16_t blocks;
uint16_t crc;
uint16_t BLVersion;
} __attribute__((packed)) RequestFirmwareConfig;
typedef struct {
uint16_t type;
uint16_t version;
uint16_t block;
} __attribute__((packed)) RequestFWBlock;
typedef struct {
uint16_t type;
uint16_t version;
uint16_t block;
uint8_t data[FIRMWARE_BLOCK_SIZE];
} __attribute__((packed)) ReplyFWBlock;
#ifdef __cplusplus
class MySensor
{
public:
/**
* Constructor
*
* Creates a new instance of Sensor class.
*
*/
MySensor(MyTransport &radio =*new MyTransportNRF24(), MyHw &hw=*new MyHwDriver()
#ifdef MY_SIGNING_FEATURE
, MySigning &signer=*new MySigningNone()
#endif
#ifdef WITH_LEDS_BLINKING
, uint8_t _rx=DEFAULT_RX_LED_PIN,
uint8_t _tx=DEFAULT_TX_LED_PIN,
uint8_t _er=DEFAULT_ERR_LED_PIN,
unsigned long _blink_period=DEFAULT_LED_BLINK_PERIOD
#endif
);
/**
* Begin operation of the MySensors library
*
* Call this in setup(), before calling any other sensor net library methods.
* @param incomingMessageCallback Callback function for incoming messages from other nodes or controller and request responses. Default is NULL.
* @param nodeId The unique id (1-254) for this sensor. Default is AUTO(255) which means sensor tries to fetch an id from controller.
* @param repeaterMode Activate repeater mode. This node will forward messages to other nodes in the radio network. Make sure to call process() regularly. Default in false
* @param parentNodeId Use this to force node to always communicate with a certain parent node. Default is AUTO which means node automatically tries to find a parent.
*/
void begin(void (* msgCallback)(const MyMessage &)=NULL, uint8_t nodeId=AUTO, boolean repeaterMode=false, uint8_t parentNodeId=AUTO);
/**
* Return the nodes nodeId.
*/
uint8_t getNodeId();
/**
* Each node must present all attached sensors before any values can be handled correctly by the controller.
* It is usually good to present all attached sensors after power-up in setup().
*
* @param sensorId Select a unique sensor id for this sensor. Choose a number between 0-254.
* @param sensorType The sensor type. See sensor typedef in MyMessage.h.
* @param description A textual description of the sensor.
* @param ack Set this to true if you want destination node to send ack back to this node. Default is not to request any ack.
* @param description A textual description of the sensor.
*/
void present(uint8_t sensorId, uint8_t sensorType, const char *description="", bool ack=false);
/**
* Sends sketch meta information to the gateway. Not mandatory but a nice thing to do.
* @param name String containing a short Sketch name or NULL if not applicable
* @param version String containing a short Sketch version or NULL if not applicable
* @param ack Set this to true if you want destination node to send ack back to this node. Default is not to request any ack.
*
*/
void sendSketchInfo(const char *name, const char *version, bool ack=false);
/**
* Sends a message to gateway or one of the other nodes in the radio network
*
* @param msg Message to send
* @param ack Set this to true if you want destination node to send ack back to this node. Default is not to request any ack.
* @return true Returns true if message reached the first stop on its way to destination.
*/
bool send(MyMessage &msg, bool ack=false);
boolean sendRoute(MyMessage &message);
/**
* Send this nodes battery level to gateway.
* @param level Level between 0-100(%)
* @param ack Set this to true if you want destination node to send ack back to this node. Default is not to request any ack.
*
*/
void sendBatteryLevel(uint8_t level, bool ack=false);
/**
* Send a heartbeat message (I'm alive!) to the gateway/controller.
* The payload will be an incremental 16 bit integer value starting at 1 when sensor is powered on.
*/
void sendHeartbeat(void);
/**
* Requests a value from gateway or some other sensor in the radio network.
* Make sure to add callback-method in begin-method to handle request responses.
*
* @param childSensorId The unique child id for the different sensors connected to this Arduino. 0-254.
* @param variableType The variableType to fetch
* @param destination The nodeId of other node in radio network. Default is gateway
*/
void request(uint8_t childSensorId, uint8_t variableType, uint8_t destination=GATEWAY_ADDRESS);
/**
* Requests time from controller. Answer will be delivered to callback.
*
* @param callback for time request. Incoming argument is seconds since 1970.
*/
void requestTime(void (* timeCallback)(unsigned long));
/**
* Processes incoming messages to this node. If this is a relaying node it will
* Returns true if there is a message addressed for this node just was received.
* Use callback to handle incoming messages.
*/
boolean process();
/**
* Returns the most recent node configuration received from controller
*/
ControllerConfig getConfig();
/**
* Save a state (in local EEPROM). Good for actuators to "remember" state between
* power cycles.
*
* You have 256 bytes to play with. Note that there is a limitation on the number
* of writes the EEPROM can handle (~100 000 cycles on ATMega328).
*
* @param pos The position to store value in (0-255)
* @param Value to store in position
*/
void saveState(uint8_t pos, uint8_t value);
/**
* Load a state (from local EEPROM).
*
* @param pos The position to fetch value from (0-255)
* @return Value to store in position
*/
uint8_t loadState(uint8_t pos);
/**
* Returns the last received message
*/
MyMessage& getLastMessage(void);
/**
* Wait for a specified amount of time to pass. Keeps process()ing.
* This does not power-down the radio nor the Arduino.
* Because this calls process() in a loop, it is a good way to wait
* in your loop() on a repeater node or sensor that listens to messages.
* @param ms Number of milliseconds to sleep.
*/
void wait(unsigned long ms);
/**
* Sleep (PowerDownMode) the MCU and radio. Wake up on timer.
* @param ms Number of milliseconds to sleep.
*/
void sleep(unsigned long ms);
/**
* Sleep (PowerDownMode) the MCU and radio. Wake up on timer or pin change.
* See: http://arduino.cc/en/Reference/attachInterrupt for details on modes and which pin
* is assigned to what interrupt. On Nano/Pro Mini: 0=Pin2, 1=Pin3
* @param interrupt Interrupt that should trigger the wakeup
* @param mode RISING, FALLING, CHANGE
* @param ms Number of milliseconds to sleep or 0 to sleep forever
* @return true if wake up was triggered by pin change and false means timer woke it up.
*/
bool sleep(uint8_t interrupt, uint8_t mode, unsigned long ms=0);
/**
* Sleep (PowerDownMode) the MCU and radio. Wake up on timer or pin change for two separate interrupts.
* See: http://arduino.cc/en/Reference/attachInterrupt for details on modes and which pin
* is assigned to what interrupt. On Nano/Pro Mini: 0=Pin2, 1=Pin3
* @param interrupt1 First interrupt that should trigger the wakeup
* @param mode1 Mode for first interrupt (RISING, FALLING, CHANGE)
* @param interrupt2 Second interrupt that should trigger the wakeup
* @param mode2 Mode for second interrupt (RISING, FALLING, CHANGE)
* @param ms Number of milliseconds to sleep or 0 to sleep forever
* @return Interrupt number wake up was triggered by pin change and negative if timer woke it up.
*/
int8_t sleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2, unsigned long ms=0);
#ifdef WITH_LEDS_BLINKING
/**
* Blink with LEDs
* @param cnt how many blink cycles to keep the LED on. Default cycle is 300ms
*/
void rxBlink(uint8_t cnt);
void txBlink(uint8_t cnt);
void errBlink(uint8_t cnt);
#endif
protected:
NodeConfig nc; // Essential settings for node to work
ControllerConfig cc; // Configuration coming from controller
bool repeaterMode;
bool autoFindParent;
bool isGateway;
MyMessage msg; // Buffer for incoming messages.
MyMessage tmpMsg ; // Buffer for temporary messages (acks and nonces among others).
#ifdef WITH_LEDS_BLINKING
uint8_t pinRx; // Rx led pin
uint8_t pinTx; // Tx led pin
uint8_t pinEr; // Err led pin
// these variables don't need to be volatile, since we are not using interrupts
uint8_t countRx;
uint8_t countTx;
uint8_t countErr;
unsigned long ledBlinkPeriod;
void handleLedsBlinking(); // do the actual blinking
unsigned long blink_next_time;
#endif
MyTransport& radio;
#ifdef MY_SIGNING_FEATURE
uint16_t doSign[16]; // Bitfield indicating which sensors require signed communication
MyMessage msgSign; // Buffer for message to sign.
MySigning& signer;
#endif
#ifdef MY_OTA_FIRMWARE_FEATURE
NodeFirmwareConfig fc;
bool fwUpdateOngoing;
unsigned long fwLastRequestTime;
uint16_t fwBlock;
uint8_t fwRetry;
SPIFlash flash;
#endif
MyHw& hw;
boolean sendWrite(uint8_t dest, MyMessage &message);
private:
#ifdef DEBUG
char convBuf[MAX_PAYLOAD*2+1];
#endif
uint8_t failedTransmissions;
uint16_t heartbeat;
void (*timeCallback)(unsigned long); // Callback for requested time messages
void (*msgCallback)(const MyMessage &); // Callback for incoming messages from other nodes and gateway.
#ifdef MY_OTA_FIRMWARE_FEATURE
// do a crc16 on the whole received firmware
bool isValidFirmware();
#endif
void requestNodeId();
void setupNode();
void findParentNode();
uint8_t crc8Message(MyMessage &message);
};
#endif
#endif
-87
View File
@@ -1,87 +0,0 @@
/**
* The MySensors Arduino library handles the wireless radio link and protocol
* between your home built sensors/actuators and HA controller of choice.
* The sensors forms a self healing radio network with optional repeaters. Each
* repeater and gateway builds a routing tables in EEPROM which keeps track of the
* network topology allowing messages to be routed to nodes.
*
* Created by Henrik Ekblad <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.
*
*******************************
*
* DESCRIPTION
* Signing support created by Patrick "Anticimex" Fallberg <patrick@fallberg.net>
* The MySigning driver provides a generic API for various signing backends to offer
* signing of MySensors messages.
*
*/
#ifndef MySigning_h
#define MySigning_h
#include <stdint.h>
#include "MyMessage.h"
class MySigning
{
public:
// MySigning constructor.
// Different parameters would be needed depending on signing backend (e.g. pins connected etc.).
// Keeping these parameters as #define's in MyConfig to streamline the driver interface.
// @param requestSignatures Set this to true if you want destination node to sign all messages sent to this node. Default is not to require signing.
MySigning(bool requestSignatures=false);
// Returns the current requestSignature setting
bool requestSignatures();
// Stores signing identifier and a new nonce in provided message for signing operations.
// All space in message payload buffer is used for signing identifier and nonce.
// Returns false if subsystem is busy processing an ongoing signing operation.
// If successful, this marks the start of a signing operation at the receiving side so
// implementation is expected to do any necessary initializations within this call.
virtual bool getNonce(MyMessage &msg) = 0;
// Check timeout of verification session.
// Nonce will be purged if it takes too long for a signed message to be sent to the receiver.
// This function should be called on regular intervals.
virtual bool checkTimer(void) = 0;
// Get nonce from provided message and store for signing operations.
// Returns false if subsystem is busy processing an ongoing signing operation.
// Returns false if signing identifier found in message is not supported by the used backend.
// If successful, this marks the start of a signing operation at the sending side so
// implementation is expected to do any necessary initializations within this call.
virtual bool putNonce(MyMessage &msg) = 0;
// Signs provided message. All remaining space in message payload buffer is used for signing
// identifier and signature.
// Nonce used for signature calculation is the one generated previously using getNonce().
// Nonce will be cleared when this function is called to prevent re-use of nonce.
// Returns false if subsystem is busy processing an ongoing signing operation.
// Returns false if not two bytes or more of free payload space is left in provided message.
// This ends a signing operation at the sending side so implementation is expected to do any
// deinitializations and enter a power saving state within this call.
virtual bool signMsg(MyMessage &msg) = 0;
// Verifies signature in provided message.
// Nonce used for verification is the one previously set using putNonce().
// Nonce will be cleared when this function is called to prevent re-use of nonce.
// Returns false if subsystem is busy processing an ongoing signing operation.
// Returns false if signing identifier found in message is not supported by the used backend.
// This ends a signing operation at the receiving side so implementation is expected to do any
// deinitializations and enter a power saving state within this call.
virtual bool verifyMsg(MyMessage &msg) = 0;
public:
bool _requestSignatures;
};
#endif
-75
View File
@@ -1,75 +0,0 @@
/**
* The MySensors Arduino library handles the wireless radio link and protocol
* between your home built sensors/actuators and HA controller of choice.
* The sensors forms a self healing radio network with optional repeaters. Each
* repeater and gateway builds a routing tables in EEPROM which keeps track of the
* network topology allowing messages to be routed to nodes.
*
* Created by Henrik Ekblad <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.
*
*******************************
*
* DESCRIPTION
* Signing support created by Patrick "Anticimex" Fallberg <patrick@fallberg.net>
* ATSHA204 signing backend. The Atmel ATSHA204 offers true random number generation and
* HMAC-SHA256 authentication with a readout-protected key.
*
*/
#ifndef MySigningAtsha204_h
#define MySigningAtsha204_h
#include "MyConfig.h"
#include "MySigning.h"
#include "utility/ATSHA204.h"
#include <stdint.h>
#ifdef MY_SECURE_NODE_WHITELISTING
typedef struct {
uint8_t nodeId;
uint8_t serial[SHA204_SERIAL_SZ];
} whitelist_entry_t;
#endif
// The ATSHA204 is capable of generating proper random numbers for nonce
// and can calculate HMAC-SHA256 signatures. This is enterprise-
// level of security and ought to implement the signing needs for anybody.
class MySigningAtsha204 : public MySigning
{
public:
MySigningAtsha204(bool requestSignatures=true,
#ifdef MY_SECURE_NODE_WHITELISTING
uint8_t nof_whitelist_entries=0, const whitelist_entry_t* the_whitelist=NULL,
#endif
uint8_t atshaPin=MY_ATSHA204_PIN);
bool getNonce(MyMessage &msg);
bool checkTimer(void);
bool putNonce(MyMessage &msg);
bool signMsg(MyMessage &msg);
bool verifyMsg(MyMessage &msg);
private:
ATSHA204Class atsha204;
unsigned long timestamp;
bool verification_ongoing;
uint8_t current_nonce[NONCE_NUMIN_SIZE_PASSTHROUGH+SHA204_SERIAL_SZ+1];
uint8_t temp_message[SHA_MSG_SIZE];
uint8_t rx_buffer[SHA204_RSP_SIZE_MAX];
uint8_t tx_buffer[SHA204_CMD_SIZE_MAX];
void calculateSignature(MyMessage &msg);
uint8_t* sha256(const uint8_t* data, size_t sz);
#ifdef MY_SECURE_NODE_WHITELISTING
uint8_t whitlist_sz;
const whitelist_entry_t* whitelist;
#endif
};
#endif
@@ -1,85 +0,0 @@
/**
* The MySensors Arduino library handles the wireless radio link and protocol
* between your home built sensors/actuators and HA controller of choice.
* The sensors forms a self healing radio network with optional repeaters. Each
* repeater and gateway builds a routing tables in EEPROM which keeps track of the
* network topology allowing messages to be routed to nodes.
*
* Created by Henrik Ekblad <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.
*
*******************************
*
* DESCRIPTION
* Signing support created by Patrick "Anticimex" Fallberg <patrick@fallberg.net>
* ATSHA204 emulated signing backend. The emulated ATSHA204 implementation offers pseudo random
* number generation and HMAC-SHA256 authentication compatible with a "physical" ATSHA204.
* NOTE: Key is stored in clear text in the Arduino firmware. Therefore, the use of this back-end
* could compromise the key used in the signed message infrastructure if device is lost and its memory
* dumped.
*
*/
#ifndef MySigningHmac256Soft_h
#define MySigningHmac256Soft_h
#include "MyConfig.h"
#include "MySigning.h"
#include "utility/ATSHA204.h"
#include "utility/sha256.h"
#include <stdint.h>
#ifdef MY_SECURE_NODE_WHITELISTING
typedef struct {
uint8_t nodeId;
uint8_t serial[SHA204_SERIAL_SZ];
} whitelist_entry_t;
#endif
// This implementation is the pure software variant of the ATSHA204.
// It is designed to work fully compliant with nodes using ATSHA204 in the network
// and therefore uses the same signing identifier as ATSHA204.
// Because it is completly software based, the quality of the generated random numbers
// is weaker though. Random numbers are generated using the Arduino library and seed
// is sampled from an analog pin. This pin should unconnected in the hardware.
// The pin is selected using MY_RANDOMSEED_PIN in MyConfig.h.
class MySigningAtsha204Soft : public MySigning
{
public:
MySigningAtsha204Soft(bool requestSignatures=true,
#ifdef MY_SECURE_NODE_WHITELISTING
uint8_t nof_whitelist_entries=0, const whitelist_entry_t* the_whitelist=NULL,
const uint8_t* the_serial=NULL, //SHA204_SERIAL_SZ sized buffer expected if provided
#endif
uint8_t randomseedPin=MY_RANDOMSEED_PIN);
bool getNonce(MyMessage &msg);
bool checkTimer(void);
bool putNonce(MyMessage &msg);
bool signMsg(MyMessage &msg);
bool verifyMsg(MyMessage &msg);
private:
Sha256Class Sha256;
unsigned long timestamp;
bool verification_ongoing;
uint8_t current_nonce[NONCE_NUMIN_SIZE_PASSTHROUGH];
uint8_t temp_message[32];
static uint8_t hmacKey[32];
uint8_t rndPin;
uint8_t hmac[32];
void calculateSignature(MyMessage &msg);
#ifdef MY_SECURE_NODE_WHITELISTING
uint8_t whitlist_sz;
const whitelist_entry_t* whitelist;
const uint8_t* node_serial_info;
#endif
};
#endif
-95
View File
@@ -1,95 +0,0 @@
/**
* The MySensors Arduino library handles the wireless radio link and protocol
* between your home built sensors/actuators and HA controller of choice.
* The sensors forms a self healing radio network with optional repeaters. Each
* repeater and gateway builds a routing tables in EEPROM which keeps track of the
* network topology allowing messages to be routed to nodes.
*
* Created by Henrik Ekblad <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.
*
*******************************
*
* DESCRIPTION
* Signing support created by Patrick "Anticimex" Fallberg <patrick@fallberg.net>
* Disabled signing backend. Does not provide any security measures.
*
*/
#include "MyConfig.h"
#include "MySigning.h"
#include "MySigningNone.h"
#define SIGNING_IDENTIFIER (0)
// Uncomment this to get some useful serial debug info (Serial.print and Serial.println expected)
//#define DEBUG_SIGNING
#ifdef DEBUG_SIGNING
#define DEBUG_SIGNING_PRINTLN(args) Serial.println(args)
#else
#define DEBUG_SIGNING_PRINTLN(args)
#endif
#ifdef DEBUG_SIGNING
static void DEBUG_NONE_PRINTBUF(const __FlashStringHelper* str, uint8_t* buf, uint8_t sz)
{
int i;
Serial.println(str);
for (int i=0; i<sz; i++)
{
if (buf[i] < 0x10)
{
Serial.print('0'); // Because Serial.print does not 0-pad HEX
}
Serial.print(buf[i], HEX);
}
Serial.println();
}
#else
#define DEBUG_NONE_PRINTBUF(str, buf, sz)
#endif
MySigningNone::MySigningNone() : MySigning() {
}
bool MySigningNone::getNonce(MyMessage &msg) {
(void)msg;
return true;
}
bool MySigningNone::checkTimer() {
return true;
}
bool MySigningNone::putNonce(MyMessage &msg) {
(void)msg;
return true;
}
bool MySigningNone::signMsg(MyMessage &msg) {
// If we cannot fit any signature in the message, refuse to sign it
if (mGetLength(msg) > MAX_PAYLOAD-2) {
DEBUG_SIGNING_PRINTLN(F("MTOL")); // Message too large for signature to fit
return false;
}
mSetSigned(msg, 1); // make sure signing flag is set before signature is calculated
msg.data[mGetLength(msg)] = SIGNING_IDENTIFIER; // Add signing identifier
return true;
}
bool MySigningNone::verifyMsg(MyMessage &msg) {
if (msg.data[mGetLength(msg)] != SIGNING_IDENTIFIER)
return false;
else
return true;
}
-51
View File
@@ -1,51 +0,0 @@
/**
* The MySensors Arduino library handles the wireless radio link and protocol
* between your home built sensors/actuators and HA controller of choice.
* The sensors forms a self healing radio network with optional repeaters. Each
* repeater and gateway builds a routing tables in EEPROM which keeps track of the
* network topology allowing messages to be routed to nodes.
*
* Created by Henrik Ekblad <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.
*
*******************************
*
* DESCRIPTION
* Signing support created by Patrick "Anticimex" Fallberg <patrick@fallberg.net>
* Disabled signing backend. Does not provide any security measures.
*
*/
#ifndef MySigningNone_h
#define MySigningNone_h
#include "MyConfig.h"
#include "MySigning.h"
#include <stdint.h>
// The "none" signing driver that can be used for nodes
// not requiring signing
// It does check SIGNING_IDENTIFIER byte to avoid illegal mixing of signing back-ends in
// the network (as seen by this node) and it does verify proper execution order on the API.
// The "none" driver rejects all other back-ends.
class MySigningNone : public MySigning
{
public:
MySigningNone();
bool getNonce(MyMessage &msg);
bool checkTimer(void);
bool putNonce(MyMessage &msg);
bool signMsg(MyMessage &msg);
bool verifyMsg(MyMessage &msg);
};
#endif
-23
View File
@@ -1,23 +0,0 @@
/**
* The MySensors Arduino library handles the wireless radio link and protocol
* between your home built sensors/actuators and HA controller of choice.
* The sensors forms a self healing radio network with optional repeaters. Each
* repeater and gateway builds a routing tables in EEPROM which keeps track of the
* network topology allowing messages to be routed to nodes.
*
* Created by Henrik Ekblad <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"
MyTransport::MyTransport() {
}
-65
View File
@@ -1,65 +0,0 @@
/**
* The MySensors Arduino library handles the wireless radio link and protocol
* between your home built sensors/actuators and HA controller of choice.
* The sensors forms a self healing radio network with optional repeaters. Each
* repeater and gateway builds a routing tables in EEPROM which keeps track of the
* network topology allowing messages to be routed to nodes.
*
* Created by Henrik Ekblad <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 MyTransport_h
#define MyTransport_h
#include <stdint.h>
#define AUTO 0xFF // 0-254. Id 255 is reserved for auto initialization of nodeId.
#define NODE_SENSOR_ID 0xFF // Node child id is always created for when a node
// This is the nodeId for sensor net gateway receiver sketch (where all sensors should send their data).
#define GATEWAY_ADDRESS ((uint8_t)0)
#define BROADCAST_ADDRESS ((uint8_t)0xFF)
class MyTransport
{
public:
// MyTransport constructor
// different parameters would be needed depending on radio module (e.g. pins connected etc.)
// keeping these parameters as #define's in MyConfig to streamline the driver interface
MyTransport();
// init()
// different parameters would be needed depending on radio module (e.g. channel, power, crc, etc.)
// keeping these parameters as #define's in MyConfig to streamline the driver interface
virtual bool init() = 0;
// setAddress(address)
// set the node address and start listening to that address (and the broadcast address)
virtual void setAddress(uint8_t address) = 0;
// getAddress()
// returns the current address from the radio driver
virtual uint8_t getAddress() = 0;
// send(to, data, len)
// reliable transmission of the data with given length (in bytes) to the destination address
// returns true if successfully submitted
virtual bool send(uint8_t to, const void* data, uint8_t len) = 0;
// available(to)
// returns true if a new packet arrived in the rx buffer
// populates "to" parameter with the address the packet was sent to (either own address or broadcast)
virtual bool available(uint8_t *to) = 0;
// receive(data)
// returns length of received packet
// populates "data" parameter with received data
virtual uint8_t receive(void* data) = 0;
// powers down the radio
virtual void powerDown() = 0;
};
#endif
-95
View File
@@ -1,95 +0,0 @@
/**
* The MySensors Arduino library handles the wireless radio link and protocol
* between your home built sensors/actuators and HA controller of choice.
* The sensors forms a self healing radio network with optional repeaters. Each
* repeater and gateway builds a routing tables in EEPROM which keeps track of the
* network topology allowing messages to be routed to nodes.
*
* Created by Henrik Ekblad <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"
#include "MyTransportNRF24.h"
MyTransportNRF24::MyTransportNRF24(uint8_t ce, uint8_t cs, uint8_t paLevel, uint8_t channel, rf24_datarate_e datarate)
:
MyTransport(),
rf24(ce, cs),
_paLevel(paLevel),
_channel(channel),
_datarate(datarate)
{
}
bool MyTransportNRF24::init() {
// Start up the radio library
rf24.begin();
if (!rf24.isPVariant()) {
return false;
}
rf24.setAutoAck(1);
rf24.setAutoAck(BROADCAST_PIPE,false); // Turn off auto ack for broadcast
rf24.enableAckPayload();
rf24.setChannel(_channel);
rf24.setPALevel(_paLevel);
rf24.setDataRate(_datarate);
rf24.setRetries(5,15);
rf24.setCRCLength(RF24_CRC_16);
rf24.enableDynamicPayloads();
// All nodes listen to broadcast pipe (for FIND_PARENT_RESPONSE messages)
rf24.openReadingPipe(BROADCAST_PIPE, TO_ADDR(BROADCAST_ADDRESS));
return true;
}
void MyTransportNRF24::setAddress(uint8_t address) {
_address = address;
rf24.openReadingPipe(WRITE_PIPE, TO_ADDR(address));
rf24.openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(address));
rf24.startListening();
}
uint8_t MyTransportNRF24::getAddress() {
return _address;
}
bool MyTransportNRF24::send(uint8_t to, const void* data, uint8_t len) {
// Make sure radio has powered up
rf24.powerUp();
rf24.stopListening();
rf24.openWritingPipe(TO_ADDR(to));
bool ok = rf24.write(data, len, to == BROADCAST_ADDRESS);
rf24.startListening();
return ok;
}
bool MyTransportNRF24::available(uint8_t *to) {
uint8_t pipe = 255;
boolean avail = rf24.available(&pipe);
(void)avail; //until somebody makes use of 'avail'
if (pipe == CURRENT_NODE_PIPE)
*to = _address;
else if (pipe == BROADCAST_PIPE)
*to = BROADCAST_ADDRESS;
return (rf24.available() && pipe < 6);
}
uint8_t MyTransportNRF24::receive(void* data) {
uint8_t len = rf24.getDynamicPayloadSize();
rf24.read(data, len);
return len;
}
void MyTransportNRF24::powerDown() {
rf24.powerDown();
}
@@ -0,0 +1,62 @@
/**
* The MySensors Arduino library handles the wireless radio link and protocol
* between your home built sensors/actuators and HA controller of choice.
* The sensors forms a self healing radio network with optional repeaters. Each
* repeater and gateway builds a routing tables in EEPROM which keeps track of the
* network topology allowing messages to be routed to nodes.
*
* Created by Tomas Hozza <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"
extern boolean transportSendRoute(MyMessage &message);
inline void gatewayTransportProcess() {
if (gatewayTransportAvailable()) {
MyMessage &_msg = gatewayTransportReceive();
if (_msg.destination == GATEWAY_ADDRESS) {
// Check if sender requests an ack back.
if (mGetRequestAck(_msg)) {
// Copy message
_msgTmp = _msg;
mSetRequestAck(_msgTmp, false); // Reply without ack flag (otherwise we would end up in an eternal loop)
mSetAck(_msgTmp, true);
_msgTmp.sender = _nc.nodeId;
_msgTmp.destination = _msg.sender;
gatewayTransportSend(_msgTmp);
}
if (mGetCommand(_msg) == C_INTERNAL) {
if (_msg.type == I_VERSION) {
// Request for version. Create the response
gatewayTransportSend(buildGw(_msg, I_VERSION).set(LIBRARY_VERSION));
#ifdef MY_INCLUSION_MODE_FEATURE
} else if (_msg.type == I_INCLUSION_MODE) {
// Request to change inclusion mode
inclusionModeSet(atoi(_msg.data) == 1);
#endif
} else {
_processInternalMessages();
}
} else {
// Call incoming message callback if available
if (receive) {
receive(_msg);
}
}
} else {
#if defined(MY_RADIO_FEATURE)
transportSendRoute(_msg);
#endif
}
}
}
@@ -5,8 +5,8 @@
* repeater and gateway builds a routing tables in EEPROM which keeps track of the
* network topology allowing messages to be routed to nodes.
*
* Created by Henrik Ekblad <henrik.ekblad@mysensors.org>
* Copyright (C) 2013-2015 Sensnology AB
* 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
@@ -17,7 +17,39 @@
* version 2 as published by the Free Software Foundation.
*/
#include "MyParser.h"
#ifndef MyGatewayTransport_h
#define MyGatewayTransport_h
MyParser::MyParser() {
}
#include "MyConfig.h"
#include "MyMessage.h"
#include "MyProtocol.h"
#include "MySensorCore.h"
// Common gateway functions
void gatewayTransportProcess();
// Gateway "interface" functions
/**
* initialize the driver
*/
bool gatewayTransportInit();
/**
* Send message to controller
*/
bool gatewayTransportSend(MyMessage &message);
/*
* Check if a new message is available from controller
*/
bool gatewayTransportAvailable();
/*
* Pick up last message received from controller
*/
MyMessage& gatewayTransportReceive();
#endif /* MyGatewayTransportEthernet_h */
@@ -0,0 +1,335 @@
/**
* The MySensors Arduino library handles the wireless radio link and protocol
* between your home built sensors/actuators and HA controller of choice.
* The sensors forms a self healing radio network with optional repeaters. Each
* repeater and gateway builds a routing tables in EEPROM which keeps track of the
* network topology allowing messages to be routed to nodes.
*
* Created by Tomas Hozza <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"
#if defined(MY_CONTROLLER_IP_ADDRESS)
IPAddress _ethernetControllerIP(MY_CONTROLLER_IP_ADDRESS);
#endif
#if defined(MY_IP_ADDRESS)
IPAddress _ethernetGatewayIP(MY_IP_ADDRESS);
#endif
byte _ethernetGatewayMAC[] = { MY_MAC_ADDRESS };
uint16_t _ethernetGatewayPort = MY_PORT;
MyMessage _ethernetMsg;
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
// gatewayTransportSend(buildGw(_msg, I_GATEWAY_READY).set("Gateway startup complete."));
typedef struct
{
char string[MY_GATEWAY_MAX_RECEIVE_LENGTH];
uint8_t idx;
} inputBuffer;
#if defined(MY_GATEWAY_ESP8266)
// Some re-defines to make code more readable below
#define EthernetServer WiFiServer
#define EthernetClient WiFiClient
#define EthernetUDP WiFiUDP
#if defined(MY_IP_ADDRESS)
IPAddress gateway(MY_IP_GATEWAY_ADDRESS);
IPAddress subnet(MY_IP_SUBNET_ADDRESS);
#endif
static bool clientsConnected[MY_GATEWAY_MAX_CLIENTS];
#endif
#if defined(MY_USE_UDP)
EthernetUDP _ethernetServer;
#else
EthernetServer _ethernetServer(_ethernetGatewayPort);
#endif
static EthernetClient clients[MY_GATEWAY_MAX_CLIENTS];
static inputBuffer inputString[MY_GATEWAY_MAX_CLIENTS];
#ifndef MY_IP_ADDRESS
void gatewayTransportRenewIP();
#endif
// On W5100 boards with SPI_EN exposed we can use the real SPI bus together with radio
// (if we enable it during usage)
#ifdef MY_W5100_SPI_EN
void _w5100_spi_en(boolean enable)
{
if (enable)
{
// Pull up pin
pinMode(MY_W5100_SPI_EN, INPUT);
digitalWrite(MY_W5100_SPI_EN, HIGH);
}
else
{
// Ground pin
pinMode(MY_W5100_SPI_EN, OUTPUT);
digitalWrite(MY_W5100_SPI_EN, LOW);
}
}
#else
#define _w5100_spi_en(x)
#endif
bool gatewayTransportInit() {
_w5100_spi_en(true);
#if defined(MY_GATEWAY_ESP8266)
(void)WiFi.begin(MY_ESP8266_SSID, MY_ESP8266_PASSWORD);
#ifdef MY_IP_ADDRESS
WiFi.config(_ethernetGatewayIP, gateway, subnet);
#endif
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(".");
yield();
}
Serial.print(F("IP: "));
Serial.println(WiFi.localIP());
#else
#ifdef MY_IP_ADDRESS
Ethernet.begin(_ethernetGatewayMAC, _ethernetGatewayIP);
Serial.print(F("IP: "));
Serial.println(Ethernet.localIP());
#else
// Get IP address from DHCP
Ethernet.begin(_ethernetGatewayMAC);
Serial.print(F("IP: "));
Serial.println(Ethernet.localIP());
#endif /* IP_ADDRESS_DHCP */
// give the Ethernet interface a second to initialize
// TODO: use HW delay
delay(1000);
#endif
#ifdef MY_USE_UDP
_ethernetServer.begin(_ethernetGatewayPort);
#else
// we have to use pointers due to the constructor of EthernetServer
_ethernetServer.begin();
#if defined(MY_GATEWAY_ESP8266)
_ethernetServer.setNoDelay(true);
#endif
#endif /* USE_UDP */
_w5100_spi_en(false);
return true;
}
bool gatewayTransportSend(MyMessage &message)
{
bool ret = true;
char *_ethernetMsg = protocolFormat(message);
_w5100_spi_en(true);
#if defined(MY_CONTROLLER_IP_ADDRESS)
#if defined(MY_USE_UDP)
_ethernetServer.beginPacket(_ethernetControllerIP, MY_PORT);
_ethernetServer.write(_ethernetMsg, strlen(_ethernetMsg));
// returns 1 if the packet was sent successfully
ret = _ethernetServer.endPacket();
#else
EthernetClient client;
if (client.connect(_ethernetControllerIP, MY_PORT)) {
client.write(_ethernetMsg, strlen(_ethernetMsg));
client.stop();
}
else {
// connecting to the server failed!
ret = false;
}
#endif
#else
// Send message to connected clients
#if defined(MY_GATEWAY_ESP8266)
for (uint8_t i = 0; i < ARRAY_SIZE(clients); i++)
{
if (clients[i] && clients[i].connected())
{
clients[i].write((uint8_t*)_ethernetMsg, strlen(_ethernetMsg));
}
}
#else
_ethernetServer.write(_ethernetMsg);
#endif
#endif
_w5100_spi_en(false);
return ret;
}
bool _readFromClient(uint8_t i) {
while (clients[i].connected() && clients[i].available()) {
char inChar = clients[i].read();
if (inputString[i].idx < MY_GATEWAY_MAX_RECEIVE_LENGTH - 1) {
// if newline then command is complete
if (inChar == '\n') {
// a command was issued by the client
// we will now try to send it to the actuator
// echo the string to the serial port
debug(PSTR("Client %d: %s\n"), i, inputString[i].string);
// clear the string:
inputString[i].idx = 0;
bool ret = protocolParse(_ethernetMsg, inputString[i].string);
inputString[i].string[inputString[i].idx] = 0;
if (ret) {
return ret;
}
} else {
// add it to the inputString:
inputString[i].string[inputString[i].idx++] = inChar;
}
} else {
// Incoming message too long. Throw away
debug(PSTR("Client %d: Message too long\n"), i);
inputString[i].idx = 0;
// Finished with this client's message. Next loop() we'll see if there's more to read.
break;
}
}
return false;
}
bool gatewayTransportAvailable()
{
_w5100_spi_en(true);
#if !defined(MY_IP_ADDRESS) && defined(MY_GATEWAY_W5100)
// renew IP address using DHCP
gatewayTransportRenewIP();
#endif
#ifdef MY_USE_UDP
int packet_size = _ethernetServer.parsePacket();
if (packet_size) {
//debug(PSTR("UDP packet available. Size:%d\n"), packet_size);
_ethernetServer.read(inputString[0].string, MY_GATEWAY_MAX_RECEIVE_LENGTH);
_w5100_spi_en(false);
debug(PSTR("UDP packet received: %s\n"), inputString[0].string);
return protocolParse(_ethernetMsg, inputString[0].string);
}
#else
#if defined(MY_GATEWAY_ESP8266)
// ESP8266: Go over list of clients and stop any that are no longer connected.
// If the server has a new client connection it will be assigned to a free slot.
bool allSlotsOccupied = true;
for (uint8_t i = 0; i < ARRAY_SIZE(clients); i++) {
if (!clients[i].connected()) {
if (clientsConnected[i]) {
debug(PSTR("Client %d disconnected\n"), i);
clients[i].stop();
}
//check if there are any new clients
if (_ethernetServer.hasClient()) {
clients[i] = _ethernetServer.available();
inputString[i].idx = 0;
debug(PSTR("Client %d connected\n"), i);
_w5100_spi_en(false);
gatewayTransportSend(buildGw(_msg, I_GATEWAY_READY).set("Gateway startup complete."));
_w5100_spi_en(true);
}
}
bool connected = clients[i].connected();
clientsConnected[i] = connected;
allSlotsOccupied &= connected;
}
if (allSlotsOccupied && _ethernetServer.hasClient()) {
//no free/disconnected spot so reject
debug(PSTR("No free slot available\n"));
EthernetClient c = _ethernetServer.available();
c.stop();
}
// Loop over clients connect and read available data
for (uint8_t i = 0; i < ARRAY_SIZE(clients); i++) {
if (_readFromClient(i)) {
_w5100_spi_en(false);
return true;
}
}
#else
// W5100/ENC module does not have hasClient-method. We can only serve one client at the time.
EthernetClient newclient = _ethernetServer.available();
// if a new client connects make sure to dispose any previous existing sockets
if (newclient) {
if (clients[0] != newclient) {
clients[0].stop();
clients[0] = newclient;
debug(PSTR("Client connected\n"));
_w5100_spi_en(false);
gatewayTransportSend(buildGw(_msg, I_GATEWAY_READY).set("Gateway startup complete."));
}
}
if (clients[0]) {
if (!clients[0].connected()) {
debug(PSTR("Client disconnected\n"));
clients[0].stop();
} else {
if (_readFromClient(0)) {
_w5100_spi_en(false);
return true;
}
}
}
#endif
#endif
_w5100_spi_en(false);
return false;
}
MyMessage& gatewayTransportReceive()
{
// Return the last parsed message
return _ethernetMsg;
}
#if !defined(MY_IP_ADDRESS) && !defined(MY_GATEWAY_ESP8266)
void gatewayTransportRenewIP()
{
/* renew/rebind IP address
0 - nothing happened
1 - renew failed
2 - renew success
3 - rebinf failed
4 - rebind success
*/
static unsigned long next_time = hwMillis() + MY_IP_RENEWAL_INTERVAL;
unsigned long now = hwMillis();
// http://playground.arduino.cc/Code/TimingRollover
if ((long)(now - next_time) < 0)
return;
if (Ethernet.maintain() & ~(0x06)) {
debug(PSTR("IP was not renewed correctly\n"));
/* Error occured -> IP was not renewed */
return;
}
_w5100_spi_en(false);
next_time = now + MY_IP_RENEWAL_INTERVAL;
}
#endif /* IP_ADDRESS_DHCP */
@@ -0,0 +1,184 @@
/**
* The MySensors Arduino library handles the wireless radio link and protocol
* between your home built sensors/actuators and HA controller of choice.
* The sensors forms a self healing radio network with optional repeaters. Each
* repeater and gateway builds a routing tables in EEPROM which keeps track of the
* network topology allowing messages to be routed to nodes.
*
* Created by Henrik Ekblad <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 "MyProtocol.h"
#include "MyGatewayTransport.h"
#include "MyMessage.h"
// Topic structure: MY_MQTT_TOPIC_PREFIX/NODE-ID/SENSOR-ID/CMD-TYPE/ACK-FLAG/SUB-TYPE
IPAddress _brokerIp(MY_CONTROLLER_IP_ADDRESS);
#if defined(MY_GATEWAY_ESP8266)
#define EthernetClient WiFiClient
IPAddress _gatewayIp(MY_IP_GATEWAY_ADDRESS);
IPAddress _subnetIp(MY_IP_SUBNET_ADDRESS);
#else
byte _clientMAC[] = { MY_MAC_ADDRESS };
#endif
#if defined(MY_IP_ADDRESS)
IPAddress _clientIp(MY_IP_ADDRESS);
#endif
EthernetClient _ethClient;
PubSubClient _client(_ethClient);
bool _available = false;
char _convBuffer[MAX_PAYLOAD*2+1];
char _fmtBuffer[MY_GATEWAY_MAX_SEND_LENGTH];
MyMessage _mqttMsg;
bool gatewayTransportSend(MyMessage &message) {
if (!_client.connected())
return false;
snprintf_P(_fmtBuffer, MY_GATEWAY_MAX_SEND_LENGTH, PSTR(MY_MQTT_TOPIC_PREFIX "/%d/%d/%d/%d/%d"), message.sender, message.sensor, mGetCommand(message), mGetAck(message), message.type);
debug(PSTR("Sending message on topic: %s\n"), _fmtBuffer);
return _client.publish(_fmtBuffer, message.getString(_convBuffer));
}
void incomingMQTT(char* topic, byte* payload,
unsigned int length)
{
debug(PSTR("Message arrived on topic: %s\n"), topic);
char *str, *p;
uint8_t i = 0;
for (str = strtok_r(topic, "/", &p); str && i < 5;
str = strtok_r(NULL, "/", &p)) {
switch (i) {
case 0: {
// Topic prefix
if (strcmp_P(str, MY_MQTT_TOPIC_PREFIX) != 0) {
// Message not for us or malformed!
return;
}
break;
}
case 1: {
// Node id
_mqttMsg.destination = atoi(str);
break;
}
case 2: {
// Sensor id
_mqttMsg.sensor = atoi(str);
break;
}
case 3: {
// Command type
mSetCommand(_mqttMsg, atoi(str));
break;
}
case 4: {
// Ack flag
mSetRequestAck(_mqttMsg, atoi(str)?1:0);
break;
}
case 5: {
// Sub type
_mqttMsg.type = atoi(str);
// Add payload
char* ca;
ca = (char *) payload;
ca += length;
*ca = '\0';
_mqttMsg.set((const char*) payload);
_available = true;
}
}
i++;
}
}
bool reconnectMQTT() {
debug(PSTR("Attempting MQTT connection...\n"));
// Attempt to connect
if (_client.connect(MY_MQTT_CLIENT_ID
#if defined(MY_MQTT_USER) && defined(MY_MQTT_PASSWORD)
, MY_MQTT_USER, MY_MQTT_PASSWORD
#endif
)) {
debug(PSTR("MQTT connected\n"));
// Once connected, publish an announcement...
//_client.publish("outTopic","hello world");
// ... and resubscribe
_client.subscribe(MY_MQTT_TOPIC_PREFIX "/+/+/+/+/+");
return true;
}
return false;
}
bool gatewayTransportInit() {
_client.setServer(_brokerIp, MY_PORT);
_client.setCallback(incomingMQTT);
#if defined(MY_GATEWAY_ESP8266)
(void)WiFi.begin(MY_ESP8266_SSID, MY_ESP8266_PASSWORD);
#ifdef MY_IP_ADDRESS
WiFi.config(_clientIp, _gatewayIp, _subnetIp);
#endif
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(".");
yield();
}
Serial.print(F("IP: "));
Serial.println(WiFi.localIP());
#else
#ifdef MY_IP_ADDRESS
Ethernet.begin(_clientMAC, _clientIp);
Serial.print(F("IP: "));
Serial.println(Ethernet.localIP());
#else
// Get IP address from DHCP
Ethernet.begin(_clientMAC);
Serial.print(F("IP: "));
Serial.println(Ethernet.localIP());
#endif /* IP_ADDRESS_DHCP */
// give the Ethernet interface a second to initialize
// TODO: use HW delay
wait(1000);
#endif
return true;
}
bool gatewayTransportAvailable() {
if (!_client.connected()) {
reconnectMQTT();
return false;
}
_client.loop();
return _available;
}
MyMessage & gatewayTransportReceive() {
// Return the last parsed message
_available = false;
return _mqttMsg;
}
@@ -0,0 +1,78 @@
/**
* The MySensors Arduino library handles the wireless radio link and protocol
* between your home built sensors/actuators and HA controller of choice.
* The sensors forms a self healing radio network with optional repeaters. Each
* repeater and gateway builds a routing tables in EEPROM which keeps track of the
* network topology allowing messages to be routed to nodes.
*
* Created by Henrik Ekblad <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 "MyProtocol.h"
#include "MyGatewayTransport.h"
#include "MyMessage.h"
#include "MyProtocol.h"
char _serialInputString[MY_GATEWAY_MAX_RECEIVE_LENGTH]; // A buffer for incoming commands from serial interface
int _serialInputPos;
MyMessage _serialMsg;
bool gatewayTransportSend(MyMessage &message) {
Serial.print(protocolFormat(message));
// Serial print is always successful
return true;
}
bool gatewayTransportInit() {
gatewayTransportSend(buildGw(_msg, I_GATEWAY_READY).set("Gateway startup complete."));
return true;
}
bool gatewayTransportAvailable() {
bool available = false;
while (Serial.available()) {
// get the new byte:
char inChar = (char) Serial.read();
// if the incoming character is a newline, set a flag
// so the main loop can do something about it:
if (_serialInputPos < MY_GATEWAY_MAX_RECEIVE_LENGTH - 1 && !available) {
if (inChar == '\n') {
_serialInputString[_serialInputPos] = 0;
available = true;
} else {
// add it to the inputString:
_serialInputString[_serialInputPos] = inChar;
_serialInputPos++;
}
} else {
// Incoming message too long. Throw away
_serialInputPos = 0;
}
}
if (available) {
// Parse message and return parse result
available = protocolParse(_serialMsg, _serialInputString);
_serialInputPos = 0;
}
return available;
}
MyMessage & gatewayTransportReceive() {
// Return the last parsed message
return _serialMsg;
}
@@ -17,38 +17,37 @@
* version 2 as published by the Free Software Foundation.
*/
#ifndef MyTransportNRF24_h
#define MyTransportNRF24_h
#ifndef MyHw_h
#define MyHw_h
#include "MyConfig.h"
#include "MyTransport.h"
#include <stdint.h>
#include "utility/RF24.h"
#include "utility/RF24_config.h"
#include "MyMessage.h"
#include "MyHw.h"
#define TO_ADDR(x) (RF24_BASE_RADIO_ID + x)
#define WRITE_PIPE ((uint8_t)0)
#define CURRENT_NODE_PIPE ((uint8_t)1)
#define BROADCAST_PIPE ((uint8_t)2)
class MyTransportNRF24 : public MyTransport
{
public:
MyTransportNRF24(uint8_t ce=RF24_CE_PIN, uint8_t cs=RF24_CS_PIN, uint8_t paLevel=RF24_PA_LEVEL, uint8_t channel=RF24_CHANNEL, rf24_datarate_e datarate=RF24_DATARATE);
bool init();
void setAddress(uint8_t address);
uint8_t getAddress();
bool send(uint8_t to, const void* data, uint8_t len);
bool available(uint8_t *to);
uint8_t receive(void* data);
void powerDown();
private:
RF24 rf24;
uint8_t _address;
uint8_t _paLevel;
uint8_t _channel;
rf24_datarate_e _datarate;
};
#ifdef __cplusplus
#include <Arduino.h>
#endif
// Implement these as functions or macros
/*
#define hwDigitalWrite(__pin, __value)
#define hwInit() Serial.begin(BAUD_RATE)
#define hwWatchdogReset() wdt_reset()
#define hwReboot() wdt_enable(WDTO_15MS); while (1)
#define hwMillis() millis()
void hwReadConfigBlock(void* buf, void* adr, size_t length);
void hwWriteConfigBlock(void* buf, void* adr, size_t length);
void hwWriteConfig(int adr, uint8_t value);
uint8_t hwReadConfig(int adr);
*/
void hwSleep(unsigned long ms);
bool hwSleep(uint8_t interrupt, uint8_t mode, unsigned long ms);
uint8_t hwSleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2, unsigned long ms);
#ifdef MY_DEBUG
void hwDebugPrint(const char *fmt, ... );
#endif
#endif // #ifdef ARDUINO_ARCH_ESP8266
@@ -19,7 +19,6 @@
#ifdef ARDUINO_ARCH_AVR
#include "MyHw.h"
#include "MyHwATMega328.h"
int8_t pinIntTrigger = 0;
@@ -40,51 +39,10 @@ ISR (WDT_vect)
wdt_disable();
}
MyHwATMega328::MyHwATMega328() : MyHw()
{
}
/*
// The following was redifined as macros to save space
inline uint8_t MyHwATMega328::readConfig(uint8_t pos) {
return eeprom_read_byte((uint8_t*)pos);
}
inline void MyHwATMega328::writeConfig(uint8_t pos, uint8_t value) {
eeprom_update_byte((uint8_t*)pos, value);
}
inline void MyHwATMega328::readConfigBlock(void* buf, void * pos, size_t length) {
eeprom_read_block(buf, (void*)pos, length);
}
inline void MyHwATMega328::writeConfigBlock(void* pos, void* buf, size_t length) {
eeprom_write_block((void*)pos, (void*)buf, length);
}
inline void MyHwATMega328::init() {
Serial.begin(BAUD_RATE);
}
inline void MyHwATMega328::watchdogReset() {
wdt_reset();
}
inline void MyHwATMega328::reboot() {
wdt_enable(WDTO_15MS); for (;;);
}
inline unsigned long MyHwATMega328::millis() {
return ::millis();
}
*/
void powerDown(period_t period) {
void hwPowerDown(period_t period) {
ADCSRA &= ~(1 << ADEN);
@@ -119,63 +77,63 @@ void powerDown(period_t period) {
ADCSRA |= (1 << ADEN);
}
void MyHwATMega328::internalSleep(unsigned long ms) {
void hwInternalSleep(unsigned long ms) {
// Let serial prints finish (debug, log etc)
#ifdef ENABLED_SERIAL
#ifndef MY_DISABLED_SERIAL
Serial.flush();
#endif
pinIntTrigger = 0;
while (!pinIntTrigger && ms >= 8000) { powerDown(SLEEP_8S); ms -= 8000; }
if (!pinIntTrigger && ms >= 4000) { powerDown(SLEEP_4S); ms -= 4000; }
if (!pinIntTrigger && ms >= 2000) { powerDown(SLEEP_2S); ms -= 2000; }
if (!pinIntTrigger && ms >= 1000) { powerDown(SLEEP_1S); ms -= 1000; }
if (!pinIntTrigger && ms >= 500) { powerDown(SLEEP_500MS); ms -= 500; }
if (!pinIntTrigger && ms >= 250) { powerDown(SLEEP_250MS); ms -= 250; }
if (!pinIntTrigger && ms >= 125) { powerDown(SLEEP_120MS); ms -= 120; }
if (!pinIntTrigger && ms >= 64) { powerDown(SLEEP_60MS); ms -= 60; }
if (!pinIntTrigger && ms >= 32) { powerDown(SLEEP_30MS); ms -= 30; }
if (!pinIntTrigger && ms >= 16) { powerDown(SLEEP_15Ms); ms -= 15; }
while (!pinIntTrigger && ms >= 8000) { hwPowerDown(SLEEP_8S); ms -= 8000; }
if (!pinIntTrigger && ms >= 4000) { hwPowerDown(SLEEP_4S); ms -= 4000; }
if (!pinIntTrigger && ms >= 2000) { hwPowerDown(SLEEP_2S); ms -= 2000; }
if (!pinIntTrigger && ms >= 1000) { hwPowerDown(SLEEP_1S); ms -= 1000; }
if (!pinIntTrigger && ms >= 500) { hwPowerDown(SLEEP_500MS); ms -= 500; }
if (!pinIntTrigger && ms >= 250) { hwPowerDown(SLEEP_250MS); ms -= 250; }
if (!pinIntTrigger && ms >= 125) { hwPowerDown(SLEEP_120MS); ms -= 120; }
if (!pinIntTrigger && ms >= 64) { hwPowerDown(SLEEP_60MS); ms -= 60; }
if (!pinIntTrigger && ms >= 32) { hwPowerDown(SLEEP_30MS); ms -= 30; }
if (!pinIntTrigger && ms >= 16) { hwPowerDown(SLEEP_15Ms); ms -= 15; }
}
void MyHwATMega328::sleep(unsigned long ms) {
internalSleep(ms);
void hwSleep(unsigned long ms) {
hwInternalSleep(ms);
}
bool MyHwATMega328::sleep(uint8_t interrupt, uint8_t mode, unsigned long ms) {
bool hwSleep(uint8_t interrupt, uint8_t mode, unsigned long ms) {
// Let serial prints finish (debug, log etc)
bool pinTriggeredWakeup = true;
attachInterrupt(interrupt, wakeUp, mode);
if (ms>0) {
pinIntTrigger = 0;
internalSleep(ms);
hwInternalSleep(ms);
if (0 == pinIntTrigger) {
pinTriggeredWakeup = false;
}
} else {
#ifdef ENABLED_SERIAL
#ifndef MY_DISABLED_SERIAL
Serial.flush();
#endif
powerDown(SLEEP_FOREVER);
hwPowerDown(SLEEP_FOREVER);
}
detachInterrupt(interrupt);
return pinTriggeredWakeup;
}
inline uint8_t MyHwATMega328::sleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2, unsigned long ms) {
uint8_t hwSleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2, unsigned long ms) {
int8_t retVal = 1;
attachInterrupt(interrupt1, wakeUp, mode1);
attachInterrupt(interrupt2, wakeUp2, mode2);
if (ms>0) {
pinIntTrigger = 0;
internalSleep(ms);
hwInternalSleep(ms);
if (0 == pinIntTrigger) {
retVal = -1;
}
} else {
#ifdef ENABLED_SERIAL
#ifndef MY_DISABLED_SERIAL
Serial.flush();
#endif
powerDown(SLEEP_FOREVER);
hwPowerDown(SLEEP_FOREVER);
}
detachInterrupt(interrupt1);
detachInterrupt(interrupt2);
@@ -190,25 +148,25 @@ inline uint8_t MyHwATMega328::sleep(uint8_t interrupt1, uint8_t mode1, uint8_t i
#ifdef DEBUG
void MyHwATMega328::debugPrint(bool isGW, const char *fmt, ... ) {
#ifdef MY_DEBUG
void hwDebugPrint(const char *fmt, ... ) {
char fmtBuffer[300];
if (isGW) {
#ifdef MY_GATEWAY_FEATURE
// prepend debug message to be handled correctly by controller (C_INTERNAL, I_LOG_MESSAGE)
snprintf_P(fmtBuffer, 299, PSTR("0;0;%d;0;%d;"), C_INTERNAL, I_LOG_MESSAGE);
Serial.print(fmtBuffer);
}
#endif
va_list args;
va_start (args, fmt );
va_end (args);
if (isGW) {
#ifdef MY_GATEWAY_FEATURE
// Truncate message if this is gateway node
vsnprintf_P(fmtBuffer, 60, fmt, args);
fmtBuffer[59] = '\n';
fmtBuffer[60] = '\0';
} else {
#else
vsnprintf_P(fmtBuffer, 299, fmt, args);
}
#endif
va_end (args);
Serial.print(fmtBuffer);
Serial.flush();
@@ -20,8 +20,6 @@
#ifndef MyHwATMega328_h
#define MyHwATMega328_h
#ifdef ARDUINO_ARCH_AVR
#include "MyHw.h"
#include "MyConfig.h"
#include "MyMessage.h"
@@ -36,14 +34,13 @@
#ifdef __cplusplus
#include <Arduino.h>
#include <SPI.h>
#endif
#if defined __AVR_ATmega328P__
#ifndef sleep_bod_disable
#define sleep_bod_disable() \
do { \
unsigned char tempreg; \
unsigned char tempreg; \
__asm__ __volatile__("in %[tempreg], %[mcucr]" "\n\t" \
"ori %[tempreg], %[bods_bodse]" "\n\t" \
"out %[mcucr], %[tempreg]" "\n\t" \
@@ -60,22 +57,22 @@ do { \
// Define these as macros to save valuable space
#define hw_digitalWrite(__pin, __value) (digitalWrite(__pin, __value))
#define hw_init() Serial.begin(BAUD_RATE)
#define hw_watchdogReset() wdt_reset()
#define hw_reboot() wdt_enable(WDTO_15MS); while (1)
#define hw_millis() millis()
#define hw_readConfig(__pos) (eeprom_read_byte((uint8_t*)(__pos)))
#define hwDigitalWrite(__pin, __value) (digitalWrite(__pin, __value))
#define hwInit() Serial.begin(MY_BAUD_RATE)
#define hwWatchdogReset() wdt_reset()
#define hwReboot() wdt_enable(WDTO_15MS); while (1)
#define hwMillis() millis()
#define hwReadConfig(__pos) (eeprom_read_byte((uint8_t*)(__pos)))
#ifndef eeprom_update_byte
#define hw_writeConfig(loc, val) if((uint8_t)(val) != eeprom_read_byte((uint8_t*)(loc))) { eeprom_write_byte((uint8_t*)(loc), (val)); }
#define hwWriteConfig(loc, val) if((uint8_t)(val) != eeprom_read_byte((uint8_t*)(loc))) { eeprom_write_byte((uint8_t*)(loc), (val)); }
#else
#define hw_writeConfig(__pos, __value) (eeprom_update_byte((uint8_t*)(__pos), (__value)))
#define hwWriteConfig(__pos, __value) (eeprom_update_byte((uint8_t*)(__pos), (__value)))
#endif
//
#define hw_readConfigBlock(__buf, __pos, __length) (eeprom_read_block((__buf), (void*)(__pos), (__length)))
#define hw_writeConfigBlock(__pos, __buf, __length) (eeprom_write_block((void*)(__pos), (void*)__buf, (__length)))
#define hwReadConfigBlock(__buf, __pos, __length) (eeprom_read_block((__buf), (void*)(__pos), (__length)))
#define hwWriteConfigBlock(__pos, __buf, __length) (eeprom_write_block((void*)(__pos), (void*)__buf, (__length)))
@@ -94,28 +91,6 @@ enum period_t
SLEEP_FOREVER
};
class MyHwATMega328 : public MyHw
{
public:
MyHwATMega328();
void hwInternalSleep(unsigned long ms);
/* void init();
void watchdogReset();
void reboot();
unsigned long millis();
uint8_t readConfig(uint8_t pos);
void writeConfig(uint8_t pos, uint8_t value);
void readConfigBlock(void* buf, void * pos, size_t length);
void writeConfigBlock(void* pos, void* buf, size_t length); */
void sleep(unsigned long ms);
bool sleep(uint8_t interrupt, uint8_t mode, unsigned long ms);
uint8_t sleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2, unsigned long ms);
#ifdef DEBUG
void debugPrint(bool isGW, const char *fmt, ... );
#endif
private:
void internalSleep(unsigned long ms);
};
#endif
#endif // #ifdef ARDUINO_ARCH_AVR
@@ -17,11 +17,8 @@
* version 2 as published by the Free Software Foundation.
*/
#ifdef ARDUINO_ARCH_ESP8266
#include "MyHw.h"
#include "MyHwESP8266.h"
#include <EEPROM.h>
/*
int8_t pinIntTrigger = 0;
@@ -43,7 +40,7 @@ ISR (WDT_vect)
}
*/
static void hw_initConfigBlock( size_t length = 1024 /*ATMega328 has 1024 bytes*/ )
static void hwInitConfigBlock( size_t length = 1024 /*ATMega328 has 1024 bytes*/ )
{
static bool initDone = false;
if (!initDone)
@@ -53,9 +50,9 @@ static void hw_initConfigBlock( size_t length = 1024 /*ATMega328 has 1024 bytes*
}
}
void hw_readConfigBlock(void* buf, void* adr, size_t length)
void hwReadConfigBlock(void* buf, void* adr, size_t length)
{
hw_initConfigBlock();
hwInitConfigBlock();
uint8_t* dst = static_cast<uint8_t*>(buf);
int offs = reinterpret_cast<int>(adr);
while (length-- > 0)
@@ -64,9 +61,9 @@ void hw_readConfigBlock(void* buf, void* adr, size_t length)
}
}
void hw_writeConfigBlock(void* buf, void* adr, size_t length)
void hwWriteConfigBlock(void* buf, void* adr, size_t length)
{
hw_initConfigBlock();
hwInitConfigBlock();
uint8_t* src = static_cast<uint8_t*>(buf);
int offs = reinterpret_cast<int>(adr);
while (length-- > 0)
@@ -76,103 +73,58 @@ void hw_writeConfigBlock(void* buf, void* adr, size_t length)
EEPROM.commit();
}
uint8_t hw_readConfig(int adr)
uint8_t hwReadConfig(int adr)
{
uint8_t value;
hw_readConfigBlock(&value, reinterpret_cast<void*>(adr), 1);
hwReadConfigBlock(&value, reinterpret_cast<void*>(adr), 1);
return value;
}
void hw_writeConfig(int adr, uint8_t value)
void hwWriteConfig(int adr, uint8_t value)
{
uint8_t curr = hw_readConfig(adr);
uint8_t curr = hwReadConfig(adr);
if (curr != value)
{
hw_writeConfigBlock(&value, reinterpret_cast<void*>(adr), 1);
hwWriteConfigBlock(&value, reinterpret_cast<void*>(adr), 1);
}
}
MyHwESP8266::MyHwESP8266() : MyHw()
{
}
/*
// The following was redifined as macros to save space
inline uint8_t MyHwATMega328::readConfig(uint8_t pos) {
return eeprom_read_byte((uint8_t*)pos);
}
inline void MyHwATMega328::writeConfig(uint8_t pos, uint8_t value) {
eeprom_update_byte((uint8_t*)pos, value);
}
inline void MyHwATMega328::readConfigBlock(void* buf, void * pos, size_t length) {
eeprom_read_block(buf, (void*)pos, length);
}
inline void MyHwATMega328::writeConfigBlock(void* pos, void* buf, size_t length) {
eeprom_write_block((void*)pos, (void*)buf, length);
}
inline void MyHwATMega328::init() {
Serial.begin(BAUD_RATE);
}
inline void MyHwATMega328::watchdogReset() {
wdt_reset();
}
inline void MyHwATMega328::reboot() {
wdt_enable(WDTO_15MS); for (;;);
}
inline unsigned long MyHwATMega328::millis() {
return ::millis();
}
*/
void MyHwESP8266::sleep(unsigned long ms) {
void hwSleep(unsigned long ms) {
// TODO: Not supported!
}
bool MyHwESP8266::sleep(uint8_t interrupt, uint8_t mode, unsigned long ms) {
bool hwSleep(uint8_t interrupt, uint8_t mode, unsigned long ms) {
// TODO: Not supported!
return false;
}
inline uint8_t MyHwESP8266::sleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2, unsigned long ms) {
uint8_t hwSleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2, unsigned long ms) {
// TODO: Not supported!
return 0;
}
#ifdef DEBUG
void MyHwESP8266::debugPrint(bool isGW, const char *fmt, ... ) {
#ifdef MY_DEBUG
void hwDebugPrint(const char *fmt, ... ) {
char fmtBuffer[300];
if (isGW) {
#ifdef MY_GATEWAY_FEATURE
// prepend debug message to be handled correctly by controller (C_INTERNAL, I_LOG_MESSAGE)
snprintf_P(fmtBuffer, 299, PSTR("0;0;%d;0;%d;"), C_INTERNAL, I_LOG_MESSAGE);
Serial.print(fmtBuffer);
}
#endif
va_list args;
va_start (args, fmt );
va_end (args);
if (isGW) {
#ifdef MY_GATEWAY_FEATURE
// Truncate message if this is gateway node
vsnprintf_P(fmtBuffer, 60, fmt, args);
fmtBuffer[59] = '\n';
fmtBuffer[60] = '\0';
} else {
#else
vsnprintf_P(fmtBuffer, 299, fmt, args);
}
#endif
va_end (args);
Serial.print(fmtBuffer);
Serial.flush();
@@ -180,5 +132,3 @@ void MyHwESP8266::debugPrint(bool isGW, const char *fmt, ... ) {
//Serial.write(freeRam());
}
#endif
#endif // #ifdef ARDUINO_ARCH_ESP8266
@@ -16,8 +16,37 @@
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*/
#ifndef MyHwESP8266_h
#define MyHwESP8266_h
#include "MyHw.h"
#include "MyConfig.h"
MyHw::MyHw() {
}
#include "MyMessage.h"
#ifdef __cplusplus
#include <Arduino.h>
//#include <SPI.h>
#endif
#define min(a,b) ((a)<(b)?(a):(b))
#define max(a,b) ((a)>(b)?(a):(b))
// Define these as macros to save valuable space
#define hwDigitalWrite(__pin, __value) (digitalWrite(__pin, __value))
#define hwInit() Serial.begin(MY_BAUD_RATE); Serial.setDebugOutput(true)
#define hwWatchdogReset() wdt_reset()
#define hwReboot() wdt_enable(WDTO_15MS); while (1)
#define hwMillis() millis()
void hwReadConfigBlock(void* buf, void* adr, size_t length);
void hwWriteConfigBlock(void* buf, void* adr, size_t length);
void hwWriteConfig(int adr, uint8_t value);
uint8_t hwReadConfig(int adr);
#endif // #ifdef ARDUINO_ARCH_ESP8266
@@ -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);
}
}
@@ -17,18 +17,19 @@
* version 2 as published by the Free Software Foundation.
*/
#ifndef MyParserSerial_h
#define MyParserSerial_h
#ifndef MyInclusionMode_h
#define MyInclusionMode_h
#include "MySensorCore.h"
unsigned long inclusionStartTime;
bool inclusionMode;
extern bool gatewayTransportSend(MyMessage &message);
void inclusionInit();
void inclusionModeSet(boolean newMode);
void inclusionProcess();
#include "MyConfig.h"
#include "MyParser.h"
class MyParserSerial : public MyParser
{
public:
MyParserSerial();
bool parse(MyMessage &message, char *inputString);
private:
uint8_t h2i(char c);
};
#endif
+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; }
}
@@ -17,32 +17,38 @@
* version 2 as published by the Free Software Foundation.
*/
#ifndef MyTransportRF69_h
#define MyTransportRF69_h
#include "MyConfig.h"
#include "MyTransport.h"
#include <stdint.h>
#include "utility/RFM69.h"
#include <SPI.h>
#ifndef MyLeds_h
#define MyLeds_h
class MyTransportRFM69 : public MyTransport
{
public:
MyTransportRFM69(uint8_t freqBand=RFM69_FREQUENCY, uint8_t networkId=RFM69_NETWORKID, uint8_t slaveSelectPin=RF69_SPI_CS, uint8_t interruptPin=RF69_IRQ_PIN, bool isRFM69HW=false, uint8_t interruptNum=RF69_IRQ_NUM);
bool init();
void setAddress(uint8_t address);
uint8_t getAddress();
bool send(uint8_t to, const void* data, uint8_t len);
bool available(uint8_t *to);
uint8_t receive(void* data);
void powerDown();
private:
RFM69 radio;
uint8_t _address;
uint8_t _freqBand;
uint8_t _networkId;
};
#ifdef MY_WITH_LEDS_BLINKING_INVERSE
#define LED_ON 0x1
#define LED_OFF 0x0
#else
#define LED_ON 0x0
#define LED_OFF 0x1
#endif
#ifdef MY_LEDS_BLINKING_FEATURE
#define ledBlinkTx(x,...) ledsBlinkTx(x)
#define ledBlinkRx(x,...) ledsBlinkRx(x)
#define ledBlinkErr(x,...) ledsBlinkErr(x)
/**
* Blink with LEDs
* @param cnt how many blink cycles to keep the LED on. Default cycle is 300ms
*/
void ledsInit();
void ledsBlinkRx(uint8_t cnt);
void ledsBlinkTx(uint8_t cnt);
void ledsBlinkErr(uint8_t cnt);
void ledsProcess(); // do the actual blinking
#else
// Remove led functions if feature is disabled
#define ledBlinkTx(x,...)
#define ledBlinkRx(x,...)
#define ledBlinkErr(x,...)
#endif
#endif
@@ -0,0 +1,16 @@
// Initialize library and handle sketch functions like we want to
int main(void) {
init();
#if defined(USBCON)
USBDevice.attach();
#endif
_begin(); // Startup MySensors library
if (setup) setup(); // Call sketch setup
for(;;) {
_process(); // Process incoming data
if (loop) loop(); // Call sketch loop
if (serialEventRun) serialEventRun();
}
return 0;
}
+134
View File
@@ -0,0 +1,134 @@
extern "C" {
#include "ets_sys.h"
#include "os_type.h"
#include "osapi.h"
#include "mem.h"
#include "user_interface.h"
#include "cont.h"
}
#define LOOP_TASK_PRIORITY 0
#define LOOP_QUEUE_SIZE 1
#define OPTIMISTIC_YIELD_TIME_US 16000
struct rst_info resetInfo;
int atexit(void (*func)()) {
return 0;
}
extern "C" void ets_update_cpu_frequency(int freqmhz);
void initVariant() __attribute__((weak));
void initVariant() {
}
void preloop_update_frequency() __attribute__((weak));
void preloop_update_frequency() {
#if defined(F_CPU) && (F_CPU == 160000000L)
REG_SET_BIT(0x3ff00014, BIT(0));
ets_update_cpu_frequency(160);
#endif
}
extern void (*__init_array_start)(void);
extern void (*__init_array_end)(void);
cont_t g_cont __attribute__ ((aligned (16)));
static os_event_t g_loop_queue[LOOP_QUEUE_SIZE];
static uint32_t g_micros_at_task_start;
extern "C" void abort() {
do {
*((int*)0) = 0;
} while(true);
}
extern "C" void esp_yield() {
if (cont_can_yield(&g_cont)) {
cont_yield(&g_cont);
}
}
extern "C" void esp_schedule() {
system_os_post(LOOP_TASK_PRIORITY, 0, 0);
}
extern "C" void __yield() {
if (cont_can_yield(&g_cont)) {
esp_schedule();
esp_yield();
}
else {
abort();
}
}
extern "C" void yield(void) __attribute__ ((weak, alias("__yield")));
extern "C" void optimistic_yield(uint32_t interval_us) {
if (cont_can_yield(&g_cont) &&
(system_get_time() - g_micros_at_task_start) > interval_us)
{
yield();
}
}
static void loop_wrapper() {
static bool setup_done = false;
if(!setup_done) {
_begin(); // Startup MySensors library
setup();
setup_done = true;
}
preloop_update_frequency();
_process(); // Process incoming data
loop();
esp_schedule();
}
static void loop_task(os_event_t *events) {
g_micros_at_task_start = system_get_time();
cont_run(&g_cont, &loop_wrapper);
if(cont_check(&g_cont) != 0) {
ets_printf("\r\nsketch stack overflow detected\r\n");
abort();
}
}
static void do_global_ctors(void) {
void (**p)(void);
for(p = &__init_array_start; p != &__init_array_end; ++p)
(*p)();
}
void init_done() {
system_set_os_print(1);
do_global_ctors();
esp_schedule();
}
extern "C" void user_init(void) {
struct rst_info *rtc_info_ptr = system_get_rst_info();
memcpy((void *) &resetInfo, (void *) rtc_info_ptr, sizeof(resetInfo));
uart_div_modify(0, UART_CLK_FREQ / (115200));
init();
initVariant();
cont_init(&g_cont);
system_os_task(loop_task,
LOOP_TASK_PRIORITY, g_loop_queue,
LOOP_QUEUE_SIZE);
system_init_done_cb(&init_done);
}
@@ -151,7 +151,7 @@ typedef enum {
I_INCLUSION_MODE, I_CONFIG, I_FIND_PARENT, I_FIND_PARENT_RESPONSE,
I_LOG_MESSAGE, I_CHILDREN, I_SKETCH_NAME, I_SKETCH_VERSION,
I_REBOOT, I_GATEWAY_READY, I_REQUEST_SIGNING, I_GET_NONCE, I_GET_NONCE_RESPONSE,
I_HEARTBEAT
I_HEARTBEAT, I_PRESENTATION
} mysensor_internal;
@@ -17,19 +17,19 @@
* version 2 as published by the Free Software Foundation.
*/
#ifndef MyParser_h
#define MyParser_h
#ifndef MyProtocol_h
#define MyProtocol_h
#include "MyConfig.h"
#include "MyMessage.h"
class MyParser
{
public:
// MyParser constructor
MyParser();
// parse(message, inputString)
// parse a string into a message element
// returns true if successfully parsed the input string
virtual bool parse(MyMessage &message, char *inputString) = 0;
};
// parse(message, inputString)
// parse a string into a message element
// returns true if successfully parsed the input string
bool protocolParse(MyMessage &message, char *inputString);
// Format MyMessage to the protocol represenataion
char *protocolFormat(MyMessage &message);
#endif
@@ -17,13 +17,16 @@
* version 2 as published by the Free Software Foundation.
*/
#include "MyParser.h"
#include "MyParserSerial.h"
#include "MyConfig.h"
#include "MyTransport.h"
#include "MyProtocol.h"
MyParserSerial::MyParserSerial() : MyParser() {}
uint8_t protocolH2i(char c);
bool MyParserSerial::parse(MyMessage &message, char *inputString) {
char _fmtBuffer[MY_GATEWAY_MAX_SEND_LENGTH];
char _convBuffer[MAX_PAYLOAD*2+1];
bool protocolParse(MyMessage &message, char *inputString) {
char *str, *p, *value=NULL;
uint8_t bvalue[MAX_PAYLOAD];
uint8_t blen = 0;
@@ -58,22 +61,25 @@ bool MyParserSerial::parse(MyMessage &message, char *inputString) {
blen = 0;
uint8_t val;
while (*str) {
val = h2i(*str++) << 4;
val += h2i(*str++);
val = protocolH2i(*str++) << 4;
val += protocolH2i(*str++);
bvalue[blen] = val;
blen++;
}
} else {
value = str;
// Remove ending carriage return character (if it exists)
// Remove trailing carriage return and newline character (if it exists)
uint8_t lastCharacter = strlen(value)-1;
if (value[lastCharacter] == '\r')
value[lastCharacter] = 0;
if (value[lastCharacter] == '\n')
value[lastCharacter] = 0;
}
break;
}
i++;
}
//debug(PSTR("Received %d"), i);
// Check for invalid input
if (i < 5)
return false;
@@ -89,7 +95,12 @@ bool MyParserSerial::parse(MyMessage &message, char *inputString) {
return true;
}
uint8_t MyParserSerial::h2i(char c) {
char * protocolFormat(MyMessage &message) {
snprintf_P(_fmtBuffer, MY_GATEWAY_MAX_SEND_LENGTH, PSTR("%d;%d;%d;%d;%d;%s\n"), message.sender, message.sensor, mGetCommand(message), mGetAck(message), message.type, message.getString(_convBuffer));
return _fmtBuffer;
}
uint8_t protocolH2i(char c) {
uint8_t i = 0;
if (c <= '9')
i += c - '0';
@@ -99,3 +110,5 @@ uint8_t MyParserSerial::h2i(char c) {
i += c - 'A' + 10;
return i;
}
+351
View File
@@ -0,0 +1,351 @@
/**
* The MySensors Arduino library handles the wireless radio link and protocol
* between your home built sensors/actuators and HA controller of choice.
* The sensors forms a self healing radio network with optional repeaters. Each
* repeater and gateway builds a routing tables in EEPROM which keeps track of the
* network topology allowing messages to be routed to nodes.
*
* Created by Henrik Ekblad <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 "MySensorCore.h"
ControllerConfig _cc; // Configuration coming from controller
NodeConfig _nc; // Essential settings for node to work
MyMessage _msg; // Buffer for incoming messages.
MyMessage _msgTmp; // Buffer for temporary messages (acks and nonces among others).
#ifdef MY_DEBUG
char _convBuf[MAX_PAYLOAD*2+1];
#endif
uint16_t _heartbeat;
void (*_timeCallback)(unsigned long); // Callback for requested time messages
void _process() {
hwWatchdogReset();
#if defined (MY_LEDS_BLINKING_FEATURE)
ledsProcess();
#endif
#if defined(MY_INCLUSION_MODE_FEATURE)
inclusionProcess();
#endif
#if defined(MY_GATEWAY_FEATURE)
gatewayTransportProcess();
#endif
#if defined(MY_RADIO_FEATURE)
transportProcess();
#endif
}
#if defined(MY_RADIO_FEATURE)
static inline bool isValidParent( const uint8_t parent ) {
return parent != AUTO;
}
#endif
void _begin() {
#if !defined(MY_DISABLED_SERIAL)
hwInit();
#endif
debug(PSTR("Starting...\n"));
#if defined(MY_RADIO_FEATURE)
_failedTransmissions = 0;
// Setup radio
if (!transportInit()) {
debug(PSTR("Radio init fail\n"));
// Nothing more we can do
while(1) {
#if defined(MY_GATEWAY_ESP8266)
yield();
#endif
};
}
#endif
#if defined(MY_GATEWAY_FEATURE)
#if defined(MY_INCLUSION_BUTTON_FEATURE)
inclusionInit();
#endif
// initialize the transport driver
if (!gatewayTransportInit()) {
debug(PSTR("Transport driver init fail\n"));
// Nothing more we can do
while(1) {
#if defined(MY_GATEWAY_ESP8266)
yield();
#endif
}
}
#endif
#if defined(MY_SIGNING_FEATURE)
// Read out the signing requirements from EEPROM
hwReadConfigBlock((void*)_doSign, (void*)EEPROM_SIGNING_REQUIREMENT_TABLE_ADDRESS, sizeof(_doSign));
#endif
#if defined(MY_LEDS_BLINKING_FEATURE)
ledsInit();
#endif
// Read latest received controller configuration from EEPROM
hwReadConfigBlock((void*)&_cc, (void*)EEPROM_CONTROLLER_CONFIG_ADDRESS, sizeof(ControllerConfig));
if (_cc.isMetric == 0xff) {
// Eeprom empty, set default to metric
_cc.isMetric = 0x01;
}
#if defined(MY_GATEWAY_FEATURE)
// Set configuration for gateway
_nc.parentNodeId = GATEWAY_ADDRESS;
_nc.distance = 0;
_nc.nodeId = GATEWAY_ADDRESS;
#elif defined(MY_RADIO_FEATURE)
// Read settings from eeprom
hwReadConfigBlock((void*)&_nc, (void*)EEPROM_NODE_ID_ADDRESS, sizeof(NodeConfig));
// Read latest received controller configuration from EEPROM
hwReadConfigBlock((void*)&_cc, (void*)EEPROM_CONTROLLER_CONFIG_ADDRESS, sizeof(ControllerConfig));
#ifdef MY_OTA_FIRMWARE_FEATURE
// Read firmware config from EEPROM, i.e. type, version, CRC, blocks
hwReadConfigBlock((void*)&_fc, (void*)EEPROM_FIRMWARE_TYPE_ADDRESS, sizeof(NodeFirmwareConfig));
#endif
_autoFindParent = MY_PARENT_NODE_ID == AUTO;
if (!_autoFindParent) {
_nc.parentNodeId = MY_PARENT_NODE_ID;
// Save static parent id in eeprom (used by bootloader)
hwWriteConfig(EEPROM_PARENT_NODE_ID_ADDRESS, MY_PARENT_NODE_ID);
// We don't actually know the distance to gw here. Let's pretend it is 1.
// If the current node is also repeater, be aware of this.
_nc.distance = 1;
} else if (!isValidParent(_nc.parentNodeId)) {
// Auto find parent, but parent in eeprom is invalid. Try find one.
transportFindParentNode();
}
if (MY_NODE_ID != AUTO) {
// Set static id
_nc.nodeId = MY_NODE_ID;
// Save static id in eeprom
hwWriteConfig(EEPROM_NODE_ID_ADDRESS, MY_NODE_ID);
} else if (_nc.nodeId == AUTO && isValidParent(_nc.parentNodeId)) {
// Try to fetch node-id from gateway
transportRequestNodeId();
}
#endif
#if defined(MY_RADIO_FEATURE)
transportPresentNode();
#endif
if (presentation)
presentation();
debug(PSTR("%s started, id=%d, parent=%d, distance=%d\n"), MY_NODE_TYPE, _nc.nodeId, _nc.parentNodeId, _nc.distance);
}
uint8_t getNodeId() {
return _nc.nodeId;
}
ControllerConfig getConfig() {
return _cc;
}
boolean _sendRoute(MyMessage &message) {
#if defined(MY_GATEWAY_FEATURE)
if (message.destination == _nc.nodeId) {
// This is a message sent from a sensor attached on the gateway node.
// Pass it directly to the gateway transport layer.
ledBlinkTx(1);
return gatewayTransportSend(message);
}
#endif
#if defined(MY_RADIO_FEATURE)
return transportSendRoute(message);
#else
return false;
#endif
}
bool send(MyMessage &message, bool enableAck) {
message.sender = _nc.nodeId;
mSetCommand(message,C_SET);
mSetRequestAck(message,enableAck);
return _sendRoute(message);
}
void sendBatteryLevel(uint8_t value, bool enableAck) {
_sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_BATTERY_LEVEL, enableAck).set(value));
}
void sendHeartbeat(void) {
_sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_HEARTBEAT, false).set(_heartbeat++));
}
void present(uint8_t childSensorId, uint8_t sensorType, const char *description, bool enableAck) {
_sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, childSensorId, C_PRESENTATION, sensorType, enableAck).set(childSensorId==NODE_SENSOR_ID?LIBRARY_VERSION:description));
}
void sendSketchInfo(const char *name, const char *version, bool enableAck) {
if (name != NULL) {
_sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_SKETCH_NAME, enableAck).set(name));
}
if (version != NULL) {
_sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_SKETCH_VERSION, enableAck).set(version));
}
}
void request(uint8_t childSensorId, uint8_t variableType, uint8_t destination) {
_sendRoute(build(_msg, _nc.nodeId, destination, childSensorId, C_REQ, variableType, false).set(""));
}
void requestTime() {
_sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_TIME, false).set(""));
}
// Message delivered through _msg
void _processInternalMessages() {
bool isMetric;
uint8_t type = _msg.type;
if (type == I_REBOOT) {
// Requires MySensors or other bootloader with watchdogs enabled
hwReboot();
} else if (type == I_CONFIG) {
// Pick up configuration from controller (currently only metric/imperial)
// and store it in eeprom if changed
isMetric = _msg.getString()[0] == 'M' ;
_cc.isMetric = isMetric;
hwWriteConfig(EEPROM_CONTROLLER_CONFIG_ADDRESS, isMetric);
} else if (type == I_PRESENTATION) {
// Re-send node presentation to controller
#if defined(MY_RADIO_FEATURE)
transportPresentNode();
#endif
if (presentation)
presentation();
} else if (type == I_HEARTBEAT) {
if (!mGetAck(_msg)) {
// Send heartbeat ack message back to sender (with the same payload)
_msgTmp = _msg;
mSetRequestAck(_msgTmp,false); // Reply without ack flag (otherwise we would end up in an eternal loop)
mSetAck(_msgTmp,true);
_msgTmp.sender = _nc.nodeId;
_msgTmp.destination = _msg.sender;
_sendRoute(_msgTmp);
}
} else if (type == I_TIME && receiveTime) {
// Deliver time to callback
receiveTime(_msg.getULong());
}
#if defined(MY_REPEATER_FEATURE)
if (type == I_CHILDREN && _msg.getString()[0] == 'C') {
// Clears child relay data for this node
debug(PSTR("clear\n"));
uint8_t i = 255;
do {
hwWriteConfig(EEPROM_ROUTES_ADDRESS+i, 0xff);
} while (i--);
// Clear parent node id & distance to gw
hwWriteConfig(EEPROM_PARENT_NODE_ID_ADDRESS, 0xFF);
hwWriteConfig(EEPROM_DISTANCE_ADDRESS, 0xFF);
// Find parent node
transportFindParentNode();
_sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_CHILDREN,false).set(""));
}
#endif
}
void saveState(uint8_t pos, uint8_t value) {
hwWriteConfig(EEPROM_LOCAL_CONFIG_ADDRESS+pos, value);
}
uint8_t loadState(uint8_t pos) {
return hwReadConfig(EEPROM_LOCAL_CONFIG_ADDRESS+pos);
}
void wait(unsigned long ms) {
unsigned long enter = hwMillis();
while (hwMillis() - enter < ms) {
_process();
yield();
}
}
void sleep(unsigned long ms) {
#if defined(MY_OTA_FIRMWARE_FEATURE)
if (_fwUpdateOngoing) {
// Do not sleep node while fw update is ongoing
_process();
return;
} else {
#endif
#if defined(MY_RADIO_FEATURE)
transportPowerDown();
#endif
hwSleep(ms);
#if defined(MY_OTA_FIRMWARE_FEATURE)
}
#endif
}
bool sleep(uint8_t interrupt, uint8_t mode, unsigned long ms) {
#if defined(MY_OTA_FIRMWARE_FEATURE)
if (_fwUpdateOngoing) {
// Do not sleep node while fw update is ongoing
return false;
} else {
#endif
#if defined(MY_RADIO_FEATURE)
transportPowerDown();
#endif
return hwSleep(interrupt, mode, ms) ;
#if defined(MY_OTA_FIRMWARE_FEATURE)
}
#endif
}
int8_t sleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2, unsigned long ms) {
#if defined(MY_OTA_FIRMWARE_FEATURE)
if (_fwUpdateOngoing) {
// Do not sleep node while fw update is ongoing
return -1;
} else {
#endif
#if defined(MY_RADIO_FEATURE)
transportPowerDown();
#endif
return hwSleep(interrupt1, mode1, interrupt2, mode2, ms) ;
#if defined(MY_OTA_FIRMWARE_FEATURE)
}
#endif
}
+257
View File
@@ -0,0 +1,257 @@
/**
* The MySensors Arduino library handles the wireless radio link and protocol
* between your home built sensors/actuators and HA controller of choice.
* The sensors forms a self healing radio network with optional repeaters. Each
* repeater and gateway builds a routing tables in EEPROM which keeps track of the
* network topology allowing messages to be routed to nodes.
*
* Created by Henrik Ekblad <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 MySensorCore_h
#define MySensorCore_h
#include "Version.h" // Auto generated by bot
#include "MyConfig.h"
#ifdef MY_SIGNING_FEATURE
#include "MySigning.h"
#include "MySigningNone.h"
#endif
#include "MyMessage.h"
#ifdef MY_OTA_FIRMWARE_FEATURE
#include "drivers/SPIFlash/SPIFlash.h"
#endif
#include <stddef.h>
#include <stdarg.h>
#ifdef MY_DEBUG
#define debug(x,...) hwDebugPrint(x, ##__VA_ARGS__)
#else
#define debug(x,...)
#endif
// EEPROM start address for mysensors library data
#define EEPROM_START 0
// EEPROM location of node id
#define EEPROM_NODE_ID_ADDRESS EEPROM_START
// EEPROM location of parent id
#define EEPROM_PARENT_NODE_ID_ADDRESS (EEPROM_START+1)
// EEPROM location of distance to gateway
#define EEPROM_DISTANCE_ADDRESS (EEPROM_PARENT_NODE_ID_ADDRESS+1)
#define EEPROM_ROUTES_ADDRESS (EEPROM_DISTANCE_ADDRESS+1) // Where to start storing routing information in EEPROM. Will allocate 256 bytes.
#define EEPROM_CONTROLLER_CONFIG_ADDRESS (EEPROM_ROUTES_ADDRESS+256) // Location of controller sent configuration (we allow one payload of config data from controller)
#define EEPROM_FIRMWARE_TYPE_ADDRESS (EEPROM_CONTROLLER_CONFIG_ADDRESS+24)
#define EEPROM_FIRMWARE_VERSION_ADDRESS (EEPROM_FIRMWARE_TYPE_ADDRESS+2)
#define EEPROM_FIRMWARE_BLOCKS_ADDRESS (EEPROM_FIRMWARE_VERSION_ADDRESS+2)
#define EEPROM_FIRMWARE_CRC_ADDRESS (EEPROM_FIRMWARE_BLOCKS_ADDRESS+2)
#define EEPROM_SIGNING_REQUIREMENT_TABLE_ADDRESS (EEPROM_FIRMWARE_CRC_ADDRESS+2)
#define EEPROM_LOCAL_CONFIG_ADDRESS (EEPROM_SIGNING_REQUIREMENT_TABLE_ADDRESS+32) // First free address for sketch static configuration
// This is the nodeId for sensor net gateway receiver sketch (where all sensors should send their data).
#define GATEWAY_ADDRESS ((uint8_t)0)
// Node child is always created/presented when a node is started
#define NODE_SENSOR_ID 0xFF
struct NodeConfig
{
uint8_t nodeId; // Current node id
uint8_t parentNodeId; // Where this node sends its messages
uint8_t distance; // This nodes distance to sensor net gateway (number of hops)
};
struct ControllerConfig {
uint8_t isMetric;
};
/**
* Return this nodes id.
*/
uint8_t getNodeId();
/**
* Each node must present all attached sensors before any values can be handled correctly by the controller.
* It is usually good to present all attached sensors after power-up in setup().
*
* @param sensorId Select a unique sensor id for this sensor. Choose a number between 0-254.
* @param sensorType The sensor type. See sensor typedef in MyMessage.h.
* @param description A textual description of the sensor.
* @param ack Set this to true if you want destination node to send ack back to this node. Default is not to request any ack.
* @param description A textual description of the sensor.
*/
void present(uint8_t sensorId, uint8_t sensorType, const char *description="", bool ack=false);
/**
* Sends sketch meta information to the gateway. Not mandatory but a nice thing to do.
* @param name String containing a short Sketch name or NULL if not applicable
* @param version String containing a short Sketch version or NULL if not applicable
* @param ack Set this to true if you want destination node to send ack back to this node. Default is not to request any ack.
*
*/
void sendSketchInfo(const char *name, const char *version, bool ack=false);
/**
* Sends a message to gateway or one of the other nodes in the radio network
*
* @param msg Message to send
* @param ack Set this to true if you want destination node to send ack back to this node. Default is not to request any ack.
* @return true Returns true if message reached the first stop on its way to destination.
*/
bool send(MyMessage &msg, bool ack=false);
/**
* Send this nodes battery level to gateway.
* @param level Level between 0-100(%)
* @param ack Set this to true if you want destination node to send ack back to this node. Default is not to request any ack.
*
*/
void sendBatteryLevel(uint8_t level, bool ack=false);
/**
* Send a heartbeat message (I'm alive!) to the gateway/controller.
* The payload will be an incremental 16 bit integer value starting at 1 when sensor is powered on.
*/
void sendHeartbeat(void);
/**
* Requests a value from gateway or some other sensor in the radio network.
* Make sure to add callback-method in begin-method to handle request responses.
*
* @param childSensorId The unique child id for the different sensors connected to this Arduino. 0-254.
* @param variableType The variableType to fetch
* @param destination The nodeId of other node in radio network. Default is gateway
*/
void request(uint8_t childSensorId, uint8_t variableType, uint8_t destination=GATEWAY_ADDRESS);
/**
* Requests time from controller. Answer will be delivered to receiveTime function in sketch.
*
*/
void requestTime();
/**
* Returns the most recent node configuration received from controller
*/
ControllerConfig getConfig();
/**
* Save a state (in local EEPROM). Good for actuators to "remember" state between
* power cycles.
*
* You have 256 bytes to play with. Note that there is a limitation on the number
* of writes the EEPROM can handle (~100 000 cycles on ATMega328).
*
* @param pos The position to store value in (0-255)
* @param Value to store in position
*/
void saveState(uint8_t pos, uint8_t value);
/**
* Load a state (from local EEPROM).
*
* @param pos The position to fetch value from (0-255)
* @return Value to store in position
*/
uint8_t loadState(uint8_t pos);
/**
* Wait for a specified amount of time to pass. Keeps process()ing.
* This does not power-down the radio nor the Arduino.
* Because this calls process() in a loop, it is a good way to wait
* in your loop() on a repeater node or sensor that listens to messages.
* @param ms Number of milliseconds to sleep.
*/
void wait(unsigned long ms);
/**
* Sleep (PowerDownMode) the MCU and radio. Wake up on timer.
* @param ms Number of milliseconds to sleep.
*/
void sleep(unsigned long ms);
/**
* Sleep (PowerDownMode) the MCU and radio. Wake up on timer or pin change.
* See: http://arduino.cc/en/Reference/attachInterrupt for details on modes and which pin
* is assigned to what interrupt. On Nano/Pro Mini: 0=Pin2, 1=Pin3
* @param interrupt Interrupt that should trigger the wakeup
* @param mode RISING, FALLING, CHANGE
* @param ms Number of milliseconds to sleep or 0 to sleep forever
* @return true if wake up was triggered by pin change and false means timer woke it up.
*/
bool sleep(uint8_t interrupt, uint8_t mode, unsigned long ms=0);
/**
* Sleep (PowerDownMode) the MCU and radio. Wake up on timer or pin change for two separate interrupts.
* See: http://arduino.cc/en/Reference/attachInterrupt for details on modes and which pin
* is assigned to what interrupt. On Nano/Pro Mini: 0=Pin2, 1=Pin3
* @param interrupt1 First interrupt that should trigger the wakeup
* @param mode1 Mode for first interrupt (RISING, FALLING, CHANGE)
* @param interrupt2 Second interrupt that should trigger the wakeup
* @param mode2 Mode for second interrupt (RISING, FALLING, CHANGE)
* @param ms Number of milliseconds to sleep or 0 to sleep forever
* @return Interrupt number wake up was triggered by pin change and negative if timer woke it up.
*/
int8_t sleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2, unsigned long ms=0);
/****** PRIVATE ********/
void _begin();
void _process(void);
void _processInternalMessages();
boolean _sendRoute(MyMessage &message);
extern NodeConfig _nc;
extern MyMessage _msg; // Buffer for incoming messages.
extern MyMessage _msgTmp; // Buffer for temporary messages (acks and nonces among others).
#ifdef MY_DEBUG
extern char _convBuf[MAX_PAYLOAD*2+1];
#endif
void receive(const MyMessage &message) __attribute__((weak));
void receiveTime(unsigned long) __attribute__((weak));
void presentation() __attribute__((weak));
// Inline function and macros
static inline MyMessage& build(MyMessage &msg, uint8_t sender, uint8_t destination, uint8_t sensor, uint8_t command, uint8_t type, bool enableAck) {
msg.sender = sender;
msg.destination = destination;
msg.sensor = sensor;
msg.type = type;
mSetCommand(msg,command);
mSetRequestAck(msg,enableAck);
mSetAck(msg,false);
return msg;
}
static inline MyMessage& buildGw(MyMessage &msg, uint8_t type) {
msg.sender = GATEWAY_ADDRESS;
msg.destination = GATEWAY_ADDRESS;
msg.sensor = 0;
msg.type = type;
mSetCommand(msg, C_INTERNAL);
mSetRequestAck(msg, false);
mSetAck(msg, false);
return msg;
}
#endif
@@ -19,10 +19,7 @@
#include "MySigning.h"
MySigning::MySigning(bool requestSignatures) : _requestSignatures(requestSignatures) {
}
uint16_t _doSign[16]; // Bitfield indicating which sensors require signed communication
MyMessage _msgSign; // Buffer for message to sign.
bool MySigning::requestSignatures() {
return _requestSignatures;
}
+86
View File
@@ -0,0 +1,86 @@
/**
* The MySensors Arduino library handles the wireless radio link and protocol
* between your home built sensors/actuators and HA controller of choice.
* The sensors forms a self healing radio network with optional repeaters. Each
* repeater and gateway builds a routing tables in EEPROM which keeps track of the
* network topology allowing messages to be routed to nodes.
*
* Created by Henrik Ekblad <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.
*
*******************************
*
* DESCRIPTION
* Signing support created by Patrick "Anticimex" Fallberg <patrick@fallberg.net>
* The MySigning driver provides a generic API for various signing backends to offer
* signing of MySensors messages.
*
*/
#ifndef MySigning_h
#define MySigning_h
#include "MyConfig.h"
#include <stdint.h>
#include "MyMessage.h"
#include "drivers/ATSHA204/ATSHA204.h"
#ifdef MY_SIGNING_NODE_WHITELISTING
typedef struct {
uint8_t nodeId;
uint8_t serial[SHA204_SERIAL_SZ];
} whitelist_entry_t;
#endif
// Macros for manipulating signing requirement table
#define DO_SIGN(node) (node == 0 ? (~_doSign[0]&1) : (~_doSign[node>>4]&(node%16)))
#define SET_SIGN(node) (node == 0 ? (_doSign[0]&=~1) : (_doSign[node>>4]&=~(node%16)))
#define CLEAR_SIGN(node) (node == 0 ? (_doSign[0]|=1) : (_doSign[node>>4]|=(node%16)))
#define NUM_OF(x) (sizeof(x)/sizeof(x[0]))
// Stores signing identifier and a new nonce in provided message for signing operations.
// All space in message payload buffer is used for signing identifier and nonce.
// Returns false if subsystem is busy processing an ongoing signing operation.
// If successful, this marks the start of a signing operation at the receiving side so
// implementation is expected to do any necessary initializations within this call.
bool signerGetNonce(MyMessage &msg);
// Check timeout of verification session.
// Nonce will be purged if it takes too long for a signed message to be sent to the receiver.
// This function should be called on regular intervals.
bool signerCheckTimer(void);
// Get nonce from provided message and store for signing operations.
// Returns false if subsystem is busy processing an ongoing signing operation.
// Returns false if signing identifier found in message is not supported by the used backend.
// If successful, this marks the start of a signing operation at the sending side so
// implementation is expected to do any necessary initializations within this call.
bool signerPutNonce(MyMessage &msg);
// Signs provided message. All remaining space in message payload buffer is used for signing
// identifier and signature.
// Nonce used for signature calculation is the one generated previously using getNonce().
// Nonce will be cleared when this function is called to prevent re-use of nonce.
// Returns false if subsystem is busy processing an ongoing signing operation.
// Returns false if not two bytes or more of free payload space is left in provided message.
// This ends a signing operation at the sending side so implementation is expected to do any
// deinitializations and enter a power saving state within this call.
bool signerSignMsg(MyMessage &msg);
// Verifies signature in provided message.
// Nonce used for verification is the one previously set using putNonce().
// Nonce will be cleared when this function is called to prevent re-use of nonce.
// Returns false if subsystem is busy processing an ongoing signing operation.
// Returns false if signing identifier found in message is not supported by the used backend.
// This ends a signing operation at the receiving side so implementation is expected to do any
// deinitializations and enter a power saving state within this call.
bool signerVerifyMsg(MyMessage &msg);
#endif
@@ -27,13 +27,28 @@
#if defined(ARDUINO_ARCH_AVR)
#include "MySigning.h"
#include "MySigningAtsha204.h"
#define SIGNING_IDENTIFIER (1)
// Uncomment this to get some useful serial debug info (Serial.print and Serial.println expected)
//#define DEBUG_SIGNING
ATSHA204Class atsha204(MY_SIGNING_ATSHA204_PIN);
unsigned long _signing_timestamp;
bool _signing_verification_ongoing = false;
uint8_t _signing_current_nonce[NONCE_NUMIN_SIZE_PASSTHROUGH+SHA204_SERIAL_SZ+1];
uint8_t _signing_temp_message[SHA_MSG_SIZE];
uint8_t _singning_rx_buffer[SHA204_RSP_SIZE_MAX];
uint8_t _singning_tx_buffer[SHA204_CMD_SIZE_MAX];
#ifdef MY_SIGNING_NODE_WHITELISTING
const whitelist_entry_t _signing_whitelist[] = MY_SIGNING_NODE_WHITELISTING;
#endif
void signerCalculateSignature(MyMessage &msg);
uint8_t* signerSha256(const uint8_t* data, size_t sz);
#ifdef DEBUG_SIGNING
#define DEBUG_SIGNING_PRINTLN(args) Serial.println(args)
#else
@@ -59,79 +74,63 @@ static void DEBUG_SIGNING_PRINTBUF(const __FlashStringHelper* str, uint8_t* buf,
#endif
MySigningAtsha204::MySigningAtsha204(bool requestSignatures,
#ifdef MY_SECURE_NODE_WHITELISTING
uint8_t nof_whitelist_entries, const whitelist_entry_t* the_whitelist,
#endif
uint8_t atshaPin)
:
MySigning(requestSignatures),
atsha204(atshaPin),
#ifdef MY_SECURE_NODE_WHITELISTING
whitelist(the_whitelist),
whitlist_sz(nof_whitelist_entries),
#endif
verification_ongoing(false)
{
}
bool MySigningAtsha204::getNonce(MyMessage &msg) {
bool signerGetNonce(MyMessage &msg) {
// Generate random number for use as nonce
// We used a basic whitening technique that takes the first byte of a new random value and builds up a 32-byte random value
// This 32-byte random value is then hashed (SHA256) to produce the resulting nonce
for (int i = 0; i < 32; i++) {
if (atsha204.sha204m_execute(SHA204_RANDOM, RANDOM_NO_SEED_UPDATE, 0, 0, NULL,
RANDOM_COUNT, tx_buffer, RANDOM_RSP_SIZE, rx_buffer) != SHA204_SUCCESS) {
RANDOM_COUNT, _singning_tx_buffer, RANDOM_RSP_SIZE, _singning_rx_buffer) != SHA204_SUCCESS) {
DEBUG_SIGNING_PRINTLN(F("FTGN")); // FTGN = Failed to generate nonce
return false;
}
current_nonce[i] = rx_buffer[SHA204_BUFFER_POS_DATA];
_signing_current_nonce[i] = _singning_rx_buffer[SHA204_BUFFER_POS_DATA];
}
memcpy(current_nonce, sha256(current_nonce, 32), MAX_PAYLOAD);
memcpy(_signing_current_nonce, signerSha256(_signing_current_nonce, 32), MAX_PAYLOAD);
// We set the part of the 32-byte nonce that does not fit into a message to 0xAA
memset(&current_nonce[MAX_PAYLOAD], 0xAA, sizeof(current_nonce)-MAX_PAYLOAD);
memset(&_signing_current_nonce[MAX_PAYLOAD], 0xAA, sizeof(_signing_current_nonce)-MAX_PAYLOAD);
// Replace the first byte in the nonce with our signing identifier
current_nonce[0] = SIGNING_IDENTIFIER;
_signing_current_nonce[0] = SIGNING_IDENTIFIER;
// Transfer the first part of the nonce to the message
msg.set(current_nonce, MAX_PAYLOAD);
verification_ongoing = true;
timestamp = millis(); // Set timestamp to determine when to purge nonce
msg.set(_signing_current_nonce, MAX_PAYLOAD);
_signing_verification_ongoing = true;
_signing_timestamp = millis(); // Set timestamp to determine when to purge nonce
// Be a little fancy to handle turnover (prolong the time allowed to timeout after turnover)
// Note that if message is "too" quick, and arrives before turnover, it will be rejected
// but this is consider such a rare case that it is accepted and rejects are 'safe'
if (timestamp + MY_VERIFICATION_TIMEOUT_MS < millis()) timestamp = 0;
if (_signing_timestamp + MY_VERIFICATION_TIMEOUT_MS < millis()) _signing_timestamp = 0;
return true;
}
bool MySigningAtsha204::checkTimer() {
if (verification_ongoing) {
if (millis() < timestamp || millis() > timestamp + MY_VERIFICATION_TIMEOUT_MS) {
bool signerCheckTimer() {
if (_signing_verification_ongoing) {
if (millis() < _signing_timestamp || millis() > _signing_timestamp + MY_VERIFICATION_TIMEOUT_MS) {
DEBUG_SIGNING_PRINTLN(F("VT")); // VT = Verification timeout
// Purge nonce
memset(current_nonce, 0x00, NONCE_NUMIN_SIZE_PASSTHROUGH);
verification_ongoing = false;
memset(_signing_current_nonce, 0x00, NONCE_NUMIN_SIZE_PASSTHROUGH);
_signing_verification_ongoing = false;
return false;
}
}
return true;
}
bool MySigningAtsha204::putNonce(MyMessage &msg) {
bool signerPutNonce(MyMessage &msg) {
if (((uint8_t*)msg.getCustom())[0] != SIGNING_IDENTIFIER) {
DEBUG_SIGNING_PRINTLN(F("ISI")); // ISI = Incorrect signing identifier
return false;
}
memcpy(current_nonce, (uint8_t*)msg.getCustom(), MAX_PAYLOAD);
memcpy(_signing_current_nonce, (uint8_t*)msg.getCustom(), MAX_PAYLOAD);
// We set the part of the 32-byte nonce that does not fit into a message to 0xAA
memset(&current_nonce[MAX_PAYLOAD], 0xAA, sizeof(current_nonce)-MAX_PAYLOAD);
memset(&_signing_current_nonce[MAX_PAYLOAD], 0xAA, sizeof(_signing_current_nonce)-MAX_PAYLOAD);
return true;
}
bool MySigningAtsha204::signMsg(MyMessage &msg) {
bool signerSignMsg(MyMessage &msg) {
// If we cannot fit any signature in the message, refuse to sign it
if (mGetLength(msg) > MAX_PAYLOAD-2) {
DEBUG_SIGNING_PRINTLN(F("MTOL")); // Message too large for signature to fit
@@ -140,37 +139,37 @@ bool MySigningAtsha204::signMsg(MyMessage &msg) {
// Calculate signature of message
mSetSigned(msg, 1); // make sure signing flag is set before signature is calculated
calculateSignature(msg);
signerCalculateSignature(msg);
#ifdef MY_SECURE_NODE_WHITELISTING
#ifdef MY_SIGNING_NODE_WHITELISTING
// Salt the signature with the senders nodeId and the unique serial of the ATSHA device
memcpy(current_nonce, &rx_buffer[SHA204_BUFFER_POS_DATA], 32); // We can reuse the nonce buffer now since it is no longer needed
current_nonce[32] = msg.sender;
atsha204.getSerialNumber(&current_nonce[33]);
(void)sha256(current_nonce, 32+1+SHA204_SERIAL_SZ); // we can 'void' sha256 because the hash is already put in the correct place
memcpy(_signing_current_nonce, &_singning_rx_buffer[SHA204_BUFFER_POS_DATA], 32); // We can reuse the nonce buffer now since it is no longer needed
_signing_current_nonce[32] = msg.sender;
atsha204.getSerialNumber(&_signing_current_nonce[33]);
(void)signerSha256(_signing_current_nonce, 32+1+SHA204_SERIAL_SZ); // we can 'void' sha256 because the hash is already put in the correct place
DEBUG_SIGNING_PRINTLN(F("SWS")); // SWS = Signature whitelist salted
#endif
// Overwrite the first byte in the signature with the signing identifier
rx_buffer[SHA204_BUFFER_POS_DATA] = SIGNING_IDENTIFIER;
_singning_rx_buffer[SHA204_BUFFER_POS_DATA] = SIGNING_IDENTIFIER;
// Transfer as much signature data as the remaining space in the message permits
memcpy(&msg.data[mGetLength(msg)], &rx_buffer[SHA204_BUFFER_POS_DATA], MAX_PAYLOAD-mGetLength(msg));
memcpy(&msg.data[mGetLength(msg)], &_singning_rx_buffer[SHA204_BUFFER_POS_DATA], MAX_PAYLOAD-mGetLength(msg));
return true;
}
bool MySigningAtsha204::verifyMsg(MyMessage &msg) {
if (!verification_ongoing) {
bool signerVerifyMsg(MyMessage &msg) {
if (!_signing_verification_ongoing) {
DEBUG_SIGNING_PRINTLN(F("NAVS")); // NAVS = No active verification session
return false;
} else {
// Make sure we have not expired
if (!checkTimer()) {
if (!signerCheckTimer()) {
return false;
}
verification_ongoing = false;
_signing_verification_ongoing = false;
if (msg.data[mGetLength(msg)] != SIGNING_IDENTIFIER) {
DEBUG_SIGNING_PRINTLN(F("ISI")); // ISI = Incorrect signing identifier
@@ -178,29 +177,29 @@ bool MySigningAtsha204::verifyMsg(MyMessage &msg) {
}
DEBUG_SIGNING_PRINTBUF(F("SIM:"), (uint8_t*)&msg.data[mGetLength(msg)], MAX_PAYLOAD-mGetLength(msg)); // SIM = Signature in message
calculateSignature(msg); // Get signature of message
signerCalculateSignature(msg); // Get signature of message
#ifdef MY_SECURE_NODE_WHITELISTING
#ifdef MY_SIGNING_NODE_WHITELISTING
// Look up the senders nodeId in our whitelist and salt the signature with that data
for (int j=0; j < whitlist_sz; j++) {
if (whitelist[j].nodeId == msg.sender) {
for (int j=0; j < NUM_OF(_signing_whitelist); j++) {
if (_signing_whitelist[j].nodeId == msg.sender) {
DEBUG_SIGNING_PRINTLN(F("SIW")); // SIW = Sender found in whitelist
memcpy(current_nonce, &rx_buffer[SHA204_BUFFER_POS_DATA], 32); // We can reuse the nonce buffer now since it is no longer needed
current_nonce[32] = msg.sender;
memcpy(&current_nonce[33], whitelist[j].serial, SHA204_SERIAL_SZ);
(void)sha256(current_nonce, 32+1+SHA204_SERIAL_SZ); // we can 'void' sha256 because the hash is already put in the correct place
memcpy(_signing_current_nonce, &_singning_rx_buffer[SHA204_BUFFER_POS_DATA], 32); // We can reuse the nonce buffer now since it is no longer needed
_signing_current_nonce[32] = msg.sender;
memcpy(&_signing_current_nonce[33], _signing_whitelist[j].serial, SHA204_SERIAL_SZ);
(void)signerSha256(_signing_current_nonce, 32+1+SHA204_SERIAL_SZ); // we can 'void' sha256 because the hash is already put in the correct place
break;
}
}
#endif
// Overwrite the first byte in the signature with the signing identifier
rx_buffer[SHA204_BUFFER_POS_DATA] = SIGNING_IDENTIFIER;
_singning_rx_buffer[SHA204_BUFFER_POS_DATA] = SIGNING_IDENTIFIER;
// Compare the caluclated signature with the provided signature
if (memcmp(&msg.data[mGetLength(msg)], &rx_buffer[SHA204_BUFFER_POS_DATA], MAX_PAYLOAD-mGetLength(msg))) {
DEBUG_SIGNING_PRINTBUF(F("SNOK:"), &rx_buffer[SHA204_BUFFER_POS_DATA], MAX_PAYLOAD-mGetLength(msg)); // SNOK = Signature bad
#ifdef MY_SECURE_NODE_WHITELISTING
if (memcmp(&msg.data[mGetLength(msg)], &_singning_rx_buffer[SHA204_BUFFER_POS_DATA], MAX_PAYLOAD-mGetLength(msg))) {
DEBUG_SIGNING_PRINTBUF(F("SNOK:"), &_singning_rx_buffer[SHA204_BUFFER_POS_DATA], MAX_PAYLOAD-mGetLength(msg)); // SNOK = Signature bad
#ifdef MY_SIGNING_NODE_WHITELISTING
DEBUG_SIGNING_PRINTLN(F("W?")); // W? = Is the sender whitelisted?
#endif
return false;
@@ -211,60 +210,60 @@ bool MySigningAtsha204::verifyMsg(MyMessage &msg) {
}
}
// Helper to calculate signature of msg (returned in rx_buffer[SHA204_BUFFER_POS_DATA])
void MySigningAtsha204::calculateSignature(MyMessage &msg) {
memset(temp_message, 0, 32);
memcpy(temp_message, (uint8_t*)&msg.data[1-HEADER_SIZE], MAX_MESSAGE_LENGTH-1-(MAX_PAYLOAD-mGetLength(msg)));
// Helper to calculate signature of msg (returned in _singning_rx_buffer[SHA204_BUFFER_POS_DATA])
void signerCalculateSignature(MyMessage &msg) {
memset(_signing_temp_message, 0, 32);
memcpy(_signing_temp_message, (uint8_t*)&msg.data[1-HEADER_SIZE], MAX_MESSAGE_LENGTH-1-(MAX_PAYLOAD-mGetLength(msg)));
// Program the data to sign into the ATSHA204
DEBUG_SIGNING_PRINTBUF(F("MSG:"), (uint8_t*)&msg.data[1-HEADER_SIZE], MAX_MESSAGE_LENGTH-1-(MAX_PAYLOAD-mGetLength(msg))); // MSG = Message to sign
DEBUG_SIGNING_PRINTBUF(F("CNC:"), current_nonce, 32); // CNC = Current nonce
(void)atsha204.sha204m_execute(SHA204_WRITE, SHA204_ZONE_DATA | SHA204_ZONE_COUNT_FLAG, 8 << 3, 32, temp_message,
WRITE_COUNT_LONG, tx_buffer, WRITE_RSP_SIZE, rx_buffer);
DEBUG_SIGNING_PRINTBUF(F("CNC:"), _signing_current_nonce, 32); // CNC = Current nonce
(void)atsha204.sha204m_execute(SHA204_WRITE, SHA204_ZONE_DATA | SHA204_ZONE_COUNT_FLAG, 8 << 3, 32, _signing_temp_message,
WRITE_COUNT_LONG, _singning_tx_buffer, WRITE_RSP_SIZE, _singning_rx_buffer);
// Program the nonce to use for the signature (has to be done just before GENDIG due to chip limitations)
(void)atsha204.sha204m_execute(SHA204_NONCE, NONCE_MODE_PASSTHROUGH, 0, NONCE_NUMIN_SIZE_PASSTHROUGH, current_nonce,
NONCE_COUNT_LONG, tx_buffer, NONCE_RSP_SIZE_SHORT, rx_buffer);
(void)atsha204.sha204m_execute(SHA204_NONCE, NONCE_MODE_PASSTHROUGH, 0, NONCE_NUMIN_SIZE_PASSTHROUGH, _signing_current_nonce,
NONCE_COUNT_LONG, _singning_tx_buffer, NONCE_RSP_SIZE_SHORT, _singning_rx_buffer);
// Purge nonce when used
memset(current_nonce, 0x00, NONCE_NUMIN_SIZE_PASSTHROUGH);
memset(_signing_current_nonce, 0x00, NONCE_NUMIN_SIZE_PASSTHROUGH);
// Generate digest of data and nonce
(void)atsha204.sha204m_execute(SHA204_GENDIG, GENDIG_ZONE_DATA, 8, 0, NULL,
GENDIG_COUNT_DATA, tx_buffer, GENDIG_RSP_SIZE, rx_buffer);
GENDIG_COUNT_DATA, _singning_tx_buffer, GENDIG_RSP_SIZE, _singning_rx_buffer);
// Calculate HMAC of message+nonce digest and secret key
(void)atsha204.sha204m_execute(SHA204_HMAC, HMAC_MODE_SOURCE_FLAG_MATCH, 0, 0, NULL,
HMAC_COUNT, tx_buffer, HMAC_RSP_SIZE, rx_buffer);
HMAC_COUNT, _singning_tx_buffer, HMAC_RSP_SIZE, _singning_rx_buffer);
// Put device back to sleep
atsha204.sha204c_sleep();
DEBUG_SIGNING_PRINTBUF(F("HMAC:"), &rx_buffer[SHA204_BUFFER_POS_DATA], 32);
DEBUG_SIGNING_PRINTBUF(F("HMAC:"), &_singning_rx_buffer[SHA204_BUFFER_POS_DATA], 32);
}
// Helper to calculate a generic SHA256 digest of provided buffer (only supports one block)
// The pointer to the hash is returned, but the hash is also stored in rx_buffer[SHA204_BUFFER_POS_DATA])
uint8_t* MySigningAtsha204::sha256(const uint8_t* data, size_t sz) {
// The pointer to the hash is returned, but the hash is also stored in _singning_rx_buffer[SHA204_BUFFER_POS_DATA])
uint8_t* signerSha256(const uint8_t* data, size_t sz) {
// Initiate SHA256 calculator
(void)atsha204.sha204m_execute(SHA204_SHA, SHA_INIT, 0, 0, NULL,
SHA_COUNT_SHORT, tx_buffer, SHA_RSP_SIZE_SHORT, rx_buffer);
SHA_COUNT_SHORT, _singning_tx_buffer, SHA_RSP_SIZE_SHORT, _singning_rx_buffer);
// Calculate a hash
memset(temp_message, 0x00, SHA_MSG_SIZE);
memcpy(temp_message, data, sz);
temp_message[sz] = 0x80;
memset(_signing_temp_message, 0x00, SHA_MSG_SIZE);
memcpy(_signing_temp_message, data, sz);
_signing_temp_message[sz] = 0x80;
// Write length data to the last bytes
temp_message[SHA_MSG_SIZE-2] = (sz >> 5);
temp_message[SHA_MSG_SIZE-1] = (sz << 3);
DEBUG_SIGNING_PRINTBUF(F("DTH:"), temp_message, SHA_MSG_SIZE); // DTH = Data to hash
(void)atsha204.sha204m_execute(SHA204_SHA, SHA_CALC, 0, SHA_MSG_SIZE, temp_message,
SHA_COUNT_LONG, tx_buffer, SHA_RSP_SIZE_LONG, rx_buffer);
_signing_temp_message[SHA_MSG_SIZE-2] = (sz >> 5);
_signing_temp_message[SHA_MSG_SIZE-1] = (sz << 3);
DEBUG_SIGNING_PRINTBUF(F("DTH:"), _signing_temp_message, SHA_MSG_SIZE); // DTH = Data to hash
(void)atsha204.sha204m_execute(SHA204_SHA, SHA_CALC, 0, SHA_MSG_SIZE, _signing_temp_message,
SHA_COUNT_LONG, _singning_tx_buffer, SHA_RSP_SIZE_LONG, _singning_rx_buffer);
// Put device back to sleep
atsha204.sha204c_sleep();
DEBUG_SIGNING_PRINTBUF(F("SHA:"), &rx_buffer[SHA204_BUFFER_POS_DATA], 32);
return &rx_buffer[SHA204_BUFFER_POS_DATA];
DEBUG_SIGNING_PRINTBUF(F("SHA:"), &_singning_rx_buffer[SHA204_BUFFER_POS_DATA], 32);
return &_singning_rx_buffer[SHA204_BUFFER_POS_DATA];
}
#endif // #if defined(ARDUINO_ARCH_AVR)
@@ -28,13 +28,27 @@
*
*/
#include "MyConfig.h"
#include "MySigning.h"
#include "MySigningAtsha204Soft.h"
#include "utility/sha256.h"
#include "drivers/ATSHA204/sha256.h"
#define SIGNING_IDENTIFIER (1)
Sha256Class _signing_sha256;
unsigned long _signing_timestamp;
bool _signing_verification_ongoing = false;
uint8_t _signing_current_nonce[NONCE_NUMIN_SIZE_PASSTHROUGH];
uint8_t _signing_temp_message[32];
static uint8_t _signing_hmac_key[32] = { MY_SIGNING_SOFT_HMAC_KEY };
uint8_t _signing_hmac[32];
#ifdef MY_SIGNING_NODE_WHITELISTING
const uint8_t _signing_node_serial_info[SHA204_SERIAL_SZ] = {MY_SIGNING_SOFT_SERIAL};
const whitelist_entry_t _signing_whitelist[] = MY_SIGNING_NODE_WHITELISTING;
#endif
void signerCalculateSignature(MyMessage &msg);
// Uncomment this to get some useful serial debug info (Serial.print and Serial.println expected)
//#define DEBUG_SIGNING
@@ -63,81 +77,60 @@ static void DEBUG_SIGNING_PRINTBUF(const __FlashStringHelper* str, uint8_t* buf,
#define DEBUG_SIGNING_PRINTBUF(str, buf, sz)
#endif
// Initialize hmacKey from MyConfig.h (codebender didn't like static initialization in constructor)
uint8_t MySigningAtsha204Soft::hmacKey[32] = { MY_HMAC_KEY };
MySigningAtsha204Soft::MySigningAtsha204Soft(bool requestSignatures,
#ifdef MY_SECURE_NODE_WHITELISTING
uint8_t nof_whitelist_entries, const whitelist_entry_t* the_whitelist,
const uint8_t* the_serial,
#endif
uint8_t randomseedPin)
:
MySigning(requestSignatures),
#ifdef MY_SECURE_NODE_WHITELISTING
whitlist_sz(nof_whitelist_entries),
whitelist(the_whitelist),
node_serial_info(the_serial),
#endif
Sha256(),
verification_ongoing(false),
rndPin(randomseedPin)
{
}
bool MySigningAtsha204Soft::getNonce(MyMessage &msg) {
bool signerGetNonce(MyMessage &msg) {
// Set randomseed
randomSeed(analogRead(rndPin));
randomSeed(analogRead(MY_SIGNING_SOFT_RANDOMSEED_PIN));
// We used a basic whitening technique that takes the first byte of a new random value and builds up a 32-byte random value
// This 32-byte random value is then hashed (SHA256) to produce the resulting nonce
Sha256.init();
_signing_sha256.init();
for (int i = 0; i < 32; i++) {
Sha256.write(random(255));
_signing_sha256.write(random(255));
}
memcpy(current_nonce, Sha256.result(), MAX_PAYLOAD);
memcpy(_signing_current_nonce, _signing_sha256.result(), MAX_PAYLOAD);
// We set the part of the 32-byte nonce that does not fit into a message to 0xAA
memset(&current_nonce[MAX_PAYLOAD], 0xAA, sizeof(current_nonce)-MAX_PAYLOAD);
memset(&_signing_current_nonce[MAX_PAYLOAD], 0xAA, sizeof(_signing_current_nonce)-MAX_PAYLOAD);
// Replace the first byte in the nonce with our signing identifier
current_nonce[0] = SIGNING_IDENTIFIER;
_signing_current_nonce[0] = SIGNING_IDENTIFIER;
// Transfer the first part of the nonce to the message
msg.set(current_nonce, MAX_PAYLOAD);
verification_ongoing = true;
timestamp = millis(); // Set timestamp to determine when to purge nonce
msg.set(_signing_current_nonce, MAX_PAYLOAD);
_signing_verification_ongoing = true;
_signing_timestamp = millis(); // Set timestamp to determine when to purge nonce
// Be a little fancy to handle turnover (prolong the time allowed to timeout after turnover)
// Note that if message is "too" quick, and arrives before turnover, it will be rejected
// but this is consider such a rare case that it is accepted and rejects are 'safe'
if (timestamp + MY_VERIFICATION_TIMEOUT_MS < millis()) timestamp = 0;
if (_signing_timestamp + MY_VERIFICATION_TIMEOUT_MS < millis()) _signing_timestamp = 0;
return true;
}
bool MySigningAtsha204Soft::checkTimer() {
if (verification_ongoing) {
if (millis() < timestamp || millis() > timestamp + MY_VERIFICATION_TIMEOUT_MS) {
bool signerCheckTimer() {
if (_signing_verification_ongoing) {
if (millis() < _signing_timestamp || millis() > _signing_timestamp + MY_VERIFICATION_TIMEOUT_MS) {
DEBUG_SIGNING_PRINTLN(F("VT")); // VT = Verification timeout
// Purge nonce
memset(current_nonce, 0xAA, 32);
verification_ongoing = false;
memset(_signing_current_nonce, 0xAA, 32);
_signing_verification_ongoing = false;
return false;
}
}
return true;
}
bool MySigningAtsha204Soft::putNonce(MyMessage &msg) {
bool signerPutNonce(MyMessage &msg) {
if (((uint8_t*)msg.getCustom())[0] != SIGNING_IDENTIFIER) {
DEBUG_SIGNING_PRINTLN(F("ISI")); // ISI = Incorrect signing identifier
return false;
}
memcpy(current_nonce, (uint8_t*)msg.getCustom(), MAX_PAYLOAD);
memcpy(_signing_current_nonce, (uint8_t*)msg.getCustom(), MAX_PAYLOAD);
return true;
}
bool MySigningAtsha204Soft::signMsg(MyMessage &msg) {
bool signerSignMsg(MyMessage &msg) {
// If we cannot fit any signature in the message, refuse to sign it
if (mGetLength(msg) > MAX_PAYLOAD-2) {
DEBUG_SIGNING_PRINTLN(F("MTOL")); // Message too large for signature to fit
@@ -146,38 +139,38 @@ bool MySigningAtsha204Soft::signMsg(MyMessage &msg) {
// Calculate signature of message
mSetSigned(msg, 1); // make sure signing flag is set before signature is calculated
calculateSignature(msg);
signerCalculateSignature(msg);
#ifdef MY_SECURE_NODE_WHITELISTING
#ifdef MY_SIGNING_NODE_WHITELISTING
// Salt the signature with the senders nodeId and the (hopefully) unique serial The Creator has provided
Sha256.init();
for (int i=0; i<32; i++) Sha256.write(hmac[i]);
Sha256.write(msg.sender);
for (int i=0; i<SHA204_SERIAL_SZ; i++) Sha256.write(node_serial_info[i]);
memcpy(hmac, Sha256.result(), 32);
_signing_sha256.init();
for (int i=0; i<32; i++) _signing_sha256.write(_signing_hmac[i]);
_signing_sha256.write(msg.sender);
for (int i=0; i<SHA204_SERIAL_SZ; i++) _signing_sha256.write(_signing_node_serial_info[i]);
memcpy(_signing_hmac, _signing_sha256.result(), 32);
DEBUG_SIGNING_PRINTLN(F("SWS")); // SWS = Signature whitelist salted
#endif
// Overwrite the first byte in the signature with the signing identifier
hmac[0] = SIGNING_IDENTIFIER;
_signing_hmac[0] = SIGNING_IDENTIFIER;
// Transfer as much signature data as the remaining space in the message permits
memcpy(&msg.data[mGetLength(msg)], hmac, MAX_PAYLOAD-mGetLength(msg));
memcpy(&msg.data[mGetLength(msg)], _signing_hmac, MAX_PAYLOAD-mGetLength(msg));
return true;
}
bool MySigningAtsha204Soft::verifyMsg(MyMessage &msg) {
if (!verification_ongoing) {
bool signerVerifyMsg(MyMessage &msg) {
if (!_signing_verification_ongoing) {
DEBUG_SIGNING_PRINTLN(F("NAVS")); // NAVS = No active verification session
return false;
} else {
// Make sure we have not expired
if (!checkTimer()) {
if (!signerCheckTimer()) {
return false;
}
verification_ongoing = false;
_signing_verification_ongoing = false;
if (msg.data[mGetLength(msg)] != SIGNING_IDENTIFIER) {
DEBUG_SIGNING_PRINTLN(F("ISI")); // ISI = Incorrect signing identifier
@@ -186,30 +179,30 @@ bool MySigningAtsha204Soft::verifyMsg(MyMessage &msg) {
// Get signature of message
DEBUG_SIGNING_PRINTBUF(F("SIM:"), (uint8_t*)&msg.data[mGetLength(msg)], MAX_PAYLOAD-mGetLength(msg)); // SIM = Signature in message
calculateSignature(msg);
signerCalculateSignature(msg);
#ifdef MY_SECURE_NODE_WHITELISTING
#ifdef MY_SIGNING_NODE_WHITELISTING
// Look up the senders nodeId in our whitelist and salt the signature with that data
for (int j=0; j < whitlist_sz; j++) {
if (whitelist[j].nodeId == msg.sender) {
for (int j=0; j < NUM_OF(_signing_whitelist); j++) {
if (_signing_whitelist[j].nodeId == msg.sender) {
DEBUG_SIGNING_PRINTLN(F("SIW")); // SIW = Sender found in whitelist
Sha256.init();
for (int i=0; i<32; i++) Sha256.write(hmac[i]);
Sha256.write(msg.sender);
for (int i=0; i<SHA204_SERIAL_SZ; i++) Sha256.write(whitelist[j].serial[i]);
memcpy(hmac, Sha256.result(), 32);
_signing_sha256.init();
for (int i=0; i<32; i++) _signing_sha256.write(_signing_hmac[i]);
_signing_sha256.write(msg.sender);
for (int i=0; i<SHA204_SERIAL_SZ; i++) _signing_sha256.write(_signing_whitelist[j].serial[i]);
memcpy(_signing_hmac, _signing_sha256.result(), 32);
break;
}
}
#endif
// Overwrite the first byte in the signature with the signing identifier
hmac[0] = SIGNING_IDENTIFIER;
_signing_hmac[0] = SIGNING_IDENTIFIER;
// Compare the caluclated signature with the provided signature
if (memcmp(&msg.data[mGetLength(msg)], hmac, MAX_PAYLOAD-mGetLength(msg))) {
DEBUG_SIGNING_PRINTBUF(F("SNOK:"), hmac, MAX_PAYLOAD-mGetLength(msg)); // SNOK = Signature bad
#ifdef MY_SECURE_NODE_WHITELISTING
if (memcmp(&msg.data[mGetLength(msg)], _signing_hmac, MAX_PAYLOAD-mGetLength(msg))) {
DEBUG_SIGNING_PRINTBUF(F("SNOK:"), _signing_hmac, MAX_PAYLOAD-mGetLength(msg)); // SNOK = Signature bad
#ifdef MY_SIGNING_NODE_WHITELISTING
DEBUG_SIGNING_PRINTLN(F("W?")); // W? = Is the sender whitelisted?
#endif
return false;
@@ -221,11 +214,11 @@ bool MySigningAtsha204Soft::verifyMsg(MyMessage &msg) {
}
// Helper to calculate signature of msg (returned in hmac)
void MySigningAtsha204Soft::calculateSignature(MyMessage &msg) {
memset(temp_message, 0, 32);
memcpy(temp_message, (uint8_t*)&msg.data[1-HEADER_SIZE], MAX_MESSAGE_LENGTH-1-(MAX_PAYLOAD-mGetLength(msg)));
void signerCalculateSignature(MyMessage &msg) {
memset(_signing_temp_message, 0, 32);
memcpy(_signing_temp_message, (uint8_t*)&msg.data[1-HEADER_SIZE], MAX_MESSAGE_LENGTH-1-(MAX_PAYLOAD-mGetLength(msg)));
DEBUG_SIGNING_PRINTBUF(F("MSG:"), (uint8_t*)&msg.data[1-HEADER_SIZE], MAX_MESSAGE_LENGTH-1-(MAX_PAYLOAD-mGetLength(msg))); // MSG = Message to sign
DEBUG_SIGNING_PRINTBUF(F("CNC:"), current_nonce, 32); // CNC = Current nonce
DEBUG_SIGNING_PRINTBUF(F("CNC:"), _signing_current_nonce, 32); // CNC = Current nonce
// ATSHA204 calculates the HMAC with a PSK and a SHA256 digest of the following data:
// 32 bytes zeroes
@@ -250,37 +243,37 @@ void MySigningAtsha204Soft::calculateSignature(MyMessage &msg) {
// 32 bytes nonce
// Calculate message digest first
Sha256.init();
for (int i=0; i<32; i++) Sha256.write(temp_message[i]);
Sha256.write(0x15); // OPCODE
Sha256.write(0x02); // param1
Sha256.write(0x08); // param2(1)
Sha256.write(0x00); // param2(2)
Sha256.write(0xEE); // SN[8]
Sha256.write(0x01); // SN[0]
Sha256.write(0x23); // SN[1]
for (int i=0; i<25; i++) Sha256.write(0x00);
for (int i=0; i<32; i++) Sha256.write(current_nonce[i]);
_signing_sha256.init();
for (int i=0; i<32; i++) _signing_sha256.write(_signing_temp_message[i]);
_signing_sha256.write(0x15); // OPCODE
_signing_sha256.write(0x02); // param1
_signing_sha256.write(0x08); // param2(1)
_signing_sha256.write(0x00); // param2(2)
_signing_sha256.write(0xEE); // SN[8]
_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]);
// Purge nonce when used
memset(current_nonce, 0xAA, 32);
memcpy(temp_message, Sha256.result(), 32);
memset(_signing_current_nonce, 0xAA, 32);
memcpy(_signing_temp_message, _signing_sha256.result(), 32);
// Feed "message" to HMAC calculator
Sha256.initHmac(hmacKey,32); // Set the key to use
for (int i=0; i<32; i++) Sha256.write(0x00); // 32 bytes zeroes
for (int i=0; i<32; i++) Sha256.write(temp_message[i]); // 32 bytes digest
Sha256.write(0x11); // OPCODE
Sha256.write(0x04); // Mode
Sha256.write(0x00); // SlotID(1)
Sha256.write(0x00); // SlotID(2)
for (int i=0; i<11; i++) Sha256.write(0x00); // 11 bytes zeroes
Sha256.write(0xEE); // SN[8]
for (int i=0; i<4; i++) Sha256.write(0x00); // 4 bytes zeroes
Sha256.write(0x01); // SN[0]
Sha256.write(0x23); // SN[1]
for (int i=0; i<2; i++) Sha256.write(0x00); // 2 bytes zeroes
_signing_sha256.initHmac(_signing_hmac_key,32); // Set the key to use
for (int i=0; i<32; i++) _signing_sha256.write(0x00); // 32 bytes zeroes
for (int i=0; i<32; i++) _signing_sha256.write(_signing_temp_message[i]); // 32 bytes digest
_signing_sha256.write(0x11); // OPCODE
_signing_sha256.write(0x04); // Mode
_signing_sha256.write(0x00); // SlotID(1)
_signing_sha256.write(0x00); // SlotID(2)
for (int i=0; i<11; i++) _signing_sha256.write(0x00); // 11 bytes zeroes
_signing_sha256.write(0xEE); // SN[8]
for (int i=0; i<4; i++) _signing_sha256.write(0x00); // 4 bytes zeroes
_signing_sha256.write(0x01); // SN[0]
_signing_sha256.write(0x23); // SN[1]
for (int i=0; i<2; i++) _signing_sha256.write(0x00); // 2 bytes zeroes
memcpy(hmac, Sha256.resultHmac(), 32);
memcpy(_signing_hmac, _signing_sha256.resultHmac(), 32);
DEBUG_SIGNING_PRINTBUF(F("HMAC:"), hmac, 32);
DEBUG_SIGNING_PRINTBUF(F("HMAC:"), _signing_hmac, 32);
}
+579
View File
@@ -0,0 +1,579 @@
/**
* 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_SIGNING_FEATURE
bool _signingNonceStatus;
#endif
#ifdef MY_OTA_FIRMWARE_FEATURE
SPIFlash _flash(MY_OTA_FLASH_SS, MY_OTA_FLASH_JDECID);
NodeFirmwareConfig _fc;
bool _fwUpdateOngoing;
unsigned long _fwLastRequestTime;
uint16_t _fwBlock;
uint8_t _fwRetry;
#endif
static inline bool isValidDistance( const uint8_t distance ) {
return distance != DISTANCE_INVALID;
}
inline void transportProcess() {
uint8_t to = 0;
if (!transportAvailable(&to))
{
#ifdef MY_OTA_FIRMWARE_FEATURE
unsigned long enter = hwMillis();
if (_fwUpdateOngoing && (enter - _fwLastRequestTime > MY_OTA_RETRY_DELAY)) {
if (!_fwRetry) {
debug(PSTR("fw upd fail\n"));
// Give up. We have requested MY_OTA_RETRY times without any packet in return.
_fwUpdateOngoing = false;
ledBlinkErr(1);
return;
}
_fwRetry--;
_fwLastRequestTime = enter;
// Time to (re-)request firmware block from controller
RequestFWBlock *firmwareRequest = (RequestFWBlock *)_msg.data;
mSetLength(_msg, sizeof(RequestFWBlock));
firmwareRequest->type = _fc.type;
firmwareRequest->version = _fc.version;
firmwareRequest->block = (_fwBlock - 1);
_sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_STREAM, ST_FIRMWARE_REQUEST, false));
}
#endif
return;
}
#ifdef MY_SIGNING_FEATURE
(void)signerCheckTimer(); // Manage signing timeout
#endif
uint8_t len = transportReceive((uint8_t *)&_msg);
(void)len; //until somebody makes use of 'len'
ledBlinkRx(1);
uint8_t command = mGetCommand(_msg);
uint8_t type = _msg.type;
uint8_t sender = _msg.sender;
//uint8_t last = _msg.last;
uint8_t destination = _msg.destination;
#ifdef MY_SIGNING_FEATURE
// Before processing message, reject unsigned messages if signing is required and check signature (if it is signed and addressed to us)
// Note that we do not care at all about any signature found if we do not require signing, nor do we care about ACKs (they are never signed)
#ifdef MY_SIGNING_REQUEST_SIGNATURES
if ((!MY_IS_GATEWAY || DO_SIGN(sender)) &&
destination == nc.nodeId &&
mGetLength(_msg) &&
!mGetAck(_msg) &&
(mGetCommand(_msg) != C_INTERNAL ||
(type != I_GET_NONCE_RESPONSE && type != I_GET_NONCE && type != I_REQUEST_SIGNING &&
type != I_ID_REQUEST && type != I_ID_RESPONSE &&
type != I_FIND_PARENT && type != I_FIND_PARENT_RESPONSE))) {
if (!mGetSigned(_msg)) {
// Got unsigned message that should have been signed
debug(PSTR("no sign\n"));
ledBlinkErr(1);
return;
}
else if (!signerVerifyMsg(_msg)) {
debug(PSTR("verify fail\n"));
ledBlinkErr(1);
return; // This signed message has been tampered with!
}
}
#endif
#endif
// Add string termination, good if we later would want to print it.
_msg.data[mGetLength(_msg)] = '\0';
debug(PSTR("read: %d-%d-%d s=%d,c=%d,t=%d,pt=%d,l=%d,sg=%d:%s\n"),
sender, _msg.last, destination, _msg.sensor, mGetCommand(_msg), type, mGetPayloadType(_msg), mGetLength(_msg), mGetSigned(_msg), _msg.getString(_convBuf));
mSetSigned(_msg,0); // Clear the sign-flag now as verification (and debug printing) is completed
if(!(mGetVersion(_msg) == PROTOCOL_VERSION)) {
debug(PSTR("ver mismatch\n"));
ledBlinkErr(1);
return;
}
if (destination == _nc.nodeId) {
// This message is addressed to this node
#if defined(MY_REPEATER_FEATURE)
if (_msg.last != _nc.parentNodeId) {
// Message is from one of the child nodes. Add it to routing table.
hwWriteConfig(EEPROM_ROUTES_ADDRESS+sender, _msg.last);
}
#endif
// Check if sender requests an ack back.
if (mGetRequestAck(_msg)) {
// Copy message
_msgTmp = _msg;
mSetRequestAck(_msgTmp,false); // Reply without ack flag (otherwise we would end up in an eternal loop)
mSetAck(_msgTmp,true);
_msgTmp.sender = _nc.nodeId;
_msgTmp.destination = sender;
_sendRoute(_msgTmp);
}
if (command == C_INTERNAL) {
if (type == I_FIND_PARENT_RESPONSE) {
if (_autoFindParent) {
// We've received a reply to a FIND_PARENT message. Check if the distance is
// shorter than we already have.
uint8_t distance = _msg.getByte();
if (isValidDistance(distance))
{
// Distance to gateway is one more for us w.r.t. parent
distance++;
if (isValidDistance(distance) && (distance < _nc.distance)) {
// Found a neighbor closer to GW than previously found
_nc.distance = distance;
_nc.parentNodeId = sender;
hwWriteConfig(EEPROM_PARENT_NODE_ID_ADDRESS, _nc.parentNodeId);
hwWriteConfig(EEPROM_DISTANCE_ADDRESS, _nc.distance);
debug(PSTR("parent=%d, d=%d\n"), _nc.parentNodeId, _nc.distance);
}
}
}
return;
#ifdef MY_SIGNING_FEATURE
} else if (type == I_GET_NONCE) {
if (signerGetNonce(_msg)) {
_sendRoute(build(_msg, _nc.nodeId, sender, NODE_SENSOR_ID, C_INTERNAL, I_GET_NONCE_RESPONSE, false));
}
return; // Nonce exchange is an internal MySensor protocol message, no need to inform caller about this
} else if (type == I_REQUEST_SIGNING) {
if (_msg.getBool()) {
// We received an indicator that the sender require us to sign all messages we send to it
SET_SIGN(sender);
} else {
// We received an indicator that the sender does not require us to sign all messages we send to it
CLEAR_SIGN(sender);
}
// Save updated table
hwWriteConfigBlock((void*)_doSign, (void*)EEPROM_SIGNING_REQUIREMENT_TABLE_ADDRESS, sizeof(_doSign));
// Inform sender about our preference if we are a gateway, but only require signing if the sender required signing
// We do not currently want a gateway to require signing from all nodes in a network just because it wants one node
// to sign it's messages
#if defined (MY_GATEWAY_FEATURE)
#ifdef MY_SIGNING_REQUEST_SIGNATURES
if (DO_SIGN(sender))
_sendRoute(build(_msg, _nc.nodeId, sender, NODE_SENSOR_ID, C_INTERNAL, I_REQUEST_SIGNING, false).set(true));
else
_sendRoute(build(_msg, _nc.nodeId, sender, NODE_SENSOR_ID, C_INTERNAL, I_REQUEST_SIGNING, false).set(false));
#else
_sendRoute(build(_msg, _nc.nodeId, sender, NODE_SENSOR_ID, C_INTERNAL, I_REQUEST_SIGNING, false).set(false));
#endif
#endif
return; // Signing request is an internal MySensor protocol message, no need to inform caller about this
} else if (type == I_GET_NONCE_RESPONSE) {
// Proceed with signing if nonce has been received
if (signerPutNonce(_msg) && signerSignMsg(_msgSign)) {
_signingNonceStatus = SIGN_OK;
}
return; // Just pass along nonce silently (no need to call callback for these)
#endif
} else if (sender == GATEWAY_ADDRESS) {
if (type == I_ID_RESPONSE && _nc.nodeId == AUTO) {
_nc.nodeId = _msg.getByte();
if (_nc.nodeId == AUTO) {
// sensor net gateway will return max id if all sensor id are taken
debug(PSTR("full\n"));
while (1); // Wait here. Nothing else we can do...
}
transportPresentNode();
// Write id to EEPROM
hwWriteConfig(EEPROM_NODE_ID_ADDRESS, _nc.nodeId);
debug(PSTR("id=%d\n"), _nc.nodeId);
} else {
_processInternalMessages();
}
return;
}
}
#ifdef MY_OTA_FIRMWARE_FEATURE
else if (command == C_STREAM) {
if (type == ST_FIRMWARE_CONFIG_RESPONSE) {
NodeFirmwareConfig *firmwareConfigResponse = (NodeFirmwareConfig *)_msg.data;
// compare with current node configuration, if they differ, start fw fetch process
if (memcmp(&_fc,firmwareConfigResponse,sizeof(NodeFirmwareConfig))) {
debug(PSTR("fw update\n"));
// copy new FW config
memcpy(&_fc,firmwareConfigResponse,sizeof(NodeFirmwareConfig));
// Init flash
if (!_flash.initialize()) {
debug(PSTR("flash init fail\n"));
_fwUpdateOngoing = false;
} else {
// erase lower 32K -> max flash size for ATMEGA328
_flash.blockErase32K(0);
// wait until flash erased
while ( _flash.busy() );
_fwBlock = _fc.blocks;
_fwUpdateOngoing = true;
// reset flags
_fwRetry = MY_OTA_RETRY+1;
_fwLastRequestTime = 0;
}
return ;
}
debug(PSTR("fw update skipped\n"));
} else if (type == ST_FIRMWARE_RESPONSE) {
if (_fwUpdateOngoing) {
// Save block to flash
debug(PSTR("fw block %d\n"), _fwBlock);
// extract FW block
ReplyFWBlock *firmwareResponse = (ReplyFWBlock *)_msg.data;
// write to flash
_flash.writeBytes( ((_fwBlock - 1) * FIRMWARE_BLOCK_SIZE) + FIRMWARE_START_OFFSET, firmwareResponse->data, FIRMWARE_BLOCK_SIZE);
// wait until flash written
while ( _flash.busy() );
_fwBlock--;
if (!_fwBlock) {
// We're finished! Do a checksum and reboot.
_fwUpdateOngoing = false;
if (transportIsValidFirmware()) {
debug(PSTR("fw checksum ok\n"));
// All seems ok, write size and signature to flash (DualOptiboot will pick this up and flash it)
uint16_t fwsize = FIRMWARE_BLOCK_SIZE * _fc.blocks;
uint8_t OTAbuffer[10] = {'F','L','X','I','M','G',':',(uint8_t)(fwsize >> 8),(uint8_t)(fwsize & 0xff),':'};
_flash.writeBytes(0, OTAbuffer, 10);
// Write the new firmware config to eeprom
hwWriteConfigBlock((void*)&_fc, (void*)EEPROM_FIRMWARE_TYPE_ADDRESS, sizeof(NodeFirmwareConfig));
hwReboot();
} else {
debug(PSTR("fw checksum fail\n"));
}
}
// reset flags
_fwRetry = MY_OTA_RETRY+1;
_fwLastRequestTime = 0;
} else {
debug(PSTR("No fw update ongoing\n"));
}
return;
}
}
#endif
#if defined(MY_GATEWAY_FEATURE)
// Hand over message to controller
gatewayTransportSend(_msg);
#endif
// Call incoming message callback if available
if (receive) {
receive(_msg);
}
return;
}
#if defined(MY_REPEATER_FEATURE)
if (_nc.nodeId != AUTO) {
// If this node have an id, relay the message
if (command == C_INTERNAL && type == I_FIND_PARENT) {
if (sender != _nc.parentNodeId) {
if (_nc.distance == DISTANCE_INVALID)
transportFindParentNode();
if (_nc.distance != DISTANCE_INVALID) {
// Relaying nodes should always answer ping messages
// Wait a random delay of 0-2 seconds to minimize collision
// between ping ack messages from other relaying nodes
wait(hwMillis() & 0x3ff);
transportSendWrite(sender, build(_msg, _nc.nodeId, sender, NODE_SENSOR_ID, C_INTERNAL, I_FIND_PARENT_RESPONSE, false).set(_nc.distance));
}
}
} else if (to == _nc.nodeId) {
// We should try to relay this message to another node
_sendRoute(_msg);
}
}
#endif
}
#ifdef MY_OTA_FIRMWARE_FEATURE
// do a crc16 on the whole received firmware
bool transportIsValidFirmware() {
// init crc
uint16_t crc = ~0;
for (uint16_t i = 0; i < _fc.blocks * FIRMWARE_BLOCK_SIZE; ++i) {
crc ^= _flash.readByte(i + FIRMWARE_START_OFFSET);
for (int8_t j = 0; j < 8; ++j) {
if (crc & 1)
crc = (crc >> 1) ^ 0xA001;
else
crc = (crc >> 1);
}
}
return crc == _fc.crc;
}
#endif
boolean transportSendWrite(uint8_t to, MyMessage &message) {
mSetVersion(message, PROTOCOL_VERSION);
uint8_t length = mGetSigned(message) ? MAX_MESSAGE_LENGTH : mGetLength(message);
message.last = _nc.nodeId;
ledBlinkTx(1);
bool ok = transportSend(to, &message, min(MAX_MESSAGE_LENGTH, HEADER_SIZE + length));
debug(PSTR("send: %d-%d-%d-%d s=%d,c=%d,t=%d,pt=%d,l=%d,sg=%d,st=%s:%s\n"),
message.sender,message.last, to, message.destination, message.sensor, mGetCommand(message), message.type,
mGetPayloadType(message), mGetLength(message), mGetSigned(message), to==BROADCAST_ADDRESS ? "bc" : (ok ? "ok":"fail"), message.getString(_convBuf));
return ok;
}
boolean transportSendRoute(MyMessage &message) {
#if defined(MY_REPEATER_FEATURE)
uint8_t last = message.last;
#endif
bool ok;
// If we still don't have any parent id, re-request and skip this message.
if (_nc.parentNodeId == AUTO) {
transportFindParentNode();
ledBlinkErr(1);
return false;
}
// If we still don't have any node id, re-request and skip this message.
if (_nc.nodeId == AUTO) {
transportRequestNodeId();
ledBlinkErr(1);
return false;
}
mSetVersion(message, PROTOCOL_VERSION);
#ifdef MY_SIGNING_FEATURE
uint8_t type = message.type;
// If destination is known to require signed messages and we are the sender, sign this message unless it is an ACK or a handshake message
if (DO_SIGN(message.destination) && message.sender == _nc.nodeId && !mGetAck(message) && mGetLength(message) &&
(mGetCommand(message) != C_INTERNAL ||
(type != I_GET_NONCE && type != I_GET_NONCE_RESPONSE && type != I_REQUEST_SIGNING &&
type != I_ID_REQUEST && type != I_ID_RESPONSE &&
type != I_FIND_PARENT && type != I_FIND_PARENT_RESPONSE))) {
// Send nonce-request
if (!_sendRoute(build(_msgTmp, _nc.nodeId, message.destination, message.sensor, C_INTERNAL, I_GET_NONCE, false).set(""))) {
debug(PSTR("nonce tr err\n"));
return false;
}
// We have to wait for the nonce to arrive before we can sign our original message
// Other messages could come in-between. We trust process() takes care of them
unsigned long enter = hwMillis();
_msgSign = message; // Copy the message to sign since message buffer might be touched in process()
while (hwMillis() - enter < MY_VERIFICATION_TIMEOUT_MS && _signingNonceStatus==SIGN_WAITING_FOR_NONCE) {
_process();
}
if (hwMillis() - enter > MY_VERIFICATION_TIMEOUT_MS) {
debug(PSTR("nonce tmo\n"));
ledBlinkErr(1);
return false;
}
if (_signingNonceStatus == SIGN_OK) {
message = _msgSign; // Write the signed message back
} else {
debug(PSTR("sign fail\n"));
ledBlinkErr(1);
return false;
}
// After this point, only the 'last' member of the message structure is allowed to be altered if the message has been signed,
// or signature will become invalid and the message rejected by the receiver
} else {
mSetSigned(message, 0); // Message is not supposed to be signed, make sure it is marked unsigned
}
#endif
#if !defined(MY_REPEATER_FEATURE)
// None repeating node... We can only send to our parent
ok = transportSendWrite(_nc.parentNodeId, message);
#else
uint8_t sender = message.sender;
uint8_t dest = message.destination;
if (dest == GATEWAY_ADDRESS) {
// Store this address in routing table (if repeater)
hwWriteConfig(EEPROM_ROUTES_ADDRESS+sender, last);
// If destination is the gateway or if we aren't a repeater, let
// our parent take care of the message
ok = transportSendWrite(_nc.parentNodeId, message);
} else {
// Relay the message
uint8_t route = hwReadConfig(EEPROM_ROUTES_ADDRESS+dest);
if (route > GATEWAY_ADDRESS && route < BROADCAST_ADDRESS) {
// This message should be forwarded to a child node. If we send message
// to this nodes pipe then all children will receive it because the are
// all listening to this nodes pipe.
//
// +----B
// -A
// +----C------D
//
// We're node C, Message comes from A and has destination D
//
// Message destination is not gateway and is in routing table for this node.
// Send it downstream
return transportSendWrite(route, message);
} else if (sender == GATEWAY_ADDRESS && dest == BROADCAST_ADDRESS) {
// Node has not yet received any id. We need to send it
// by doing a broadcast sending,
return transportSendWrite(BROADCAST_ADDRESS, message);
}
#if defined (MY_GATEWAY_FEATURE)
// Destination isn't in our routing table and isn't a broadcast address
// Nothing to do here
debug(PSTR("Destination %d unknown\n"), dest);
return false;
# else
// A message comes from a child node and we have no
// route for it.
//
// +----B
// -A
// +----C------D <-- Message comes from D
//
// We're node C
//
// Message should be passed to node A (this nodes relay)
// This message should be routed back towards sensor net gateway
ok = transportSendWrite(_nc.parentNodeId, message);
// Add this child to our "routing table" if it not already exist
hwWriteConfig(EEPROM_ROUTES_ADDRESS+sender, last);
#endif
}
#endif
if (!ok) {
// Failure when sending to parent node. The parent node might be down and we
// need to find another route to gateway.
ledBlinkErr(1);
_failedTransmissions++;
if (_autoFindParent && _failedTransmissions > SEARCH_FAILURES) {
transportFindParentNode();
}
} else {
_failedTransmissions = 0;
}
return ok;
}
void transportRequestNodeId() {
debug(PSTR("req id\n"));
transportSetAddress(_nc.nodeId);
build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_ID_REQUEST, false).set("");
transportSendWrite(_nc.parentNodeId, _msg);
wait(2000);
}
void transportPresentNode() {
// Open reading pipe for messages directed to this node (set write pipe to same)
transportSetAddress(_nc.nodeId);
// Present node and request config
#ifndef MY_GATEWAY_FEATURE
if (_nc.nodeId != AUTO) {
#ifdef MY_SIGNING_FEATURE
// Notify gateway (and possibly controller) about the signing preferences of this node
#if defined(MY_SIGNING_REQUEST_SIGNATURES)
_sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_REQUEST_SIGNING, false).set(true));
// If we do require signing, wait for the gateway to tell us how it prefer us to transmit our messages
wait(2000);
#else
_sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_REQUEST_SIGNING, false).set(false));
#endif
#endif
// Send presentation for this radio node
#ifdef MY_REPEATER_FEATURE
present(NODE_SENSOR_ID, S_ARDUINO_REPEATER_NODE);
#else
present(NODE_SENSOR_ID, S_ARDUINO_NODE);
#endif
// Send a configuration exchange request to controller
// Node sends parent node. Controller answers with latest node configuration
// which is picked up in process()
_sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_CONFIG, false).set(_nc.parentNodeId));
// Wait configuration reply.
wait(2000);
#ifdef MY_OTA_FIRMWARE_FEATURE
RequestFirmwareConfig *reqFWConfig = (RequestFirmwareConfig *)_msg.data;
mSetLength(_msg, sizeof(RequestFirmwareConfig));
mSetCommand(_msg, C_STREAM);
mSetPayloadType(_msg,P_CUSTOM);
// copy node settings to reqFWConfig
memcpy(reqFWConfig,&_fc,sizeof(NodeFirmwareConfig));
// add bootloader information
reqFWConfig->BLVersion = MY_OTA_BOOTLOADER_VERSION;
_fwUpdateOngoing = false;
_sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_STREAM, ST_FIRMWARE_CONFIG_REQUEST, false));
#endif
}
#endif
}
void transportFindParentNode() {
static boolean findingParentNode = false;
if (findingParentNode)
return;
findingParentNode = true;
_failedTransmissions = 0;
// Set distance to max
_nc.distance = 255;
// Send ping message to BROADCAST_ADDRESS (to which all relaying nodes and gateway listens and should reply to)
debug(PSTR("find parent\n"));
build(_msg, _nc.nodeId, BROADCAST_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_FIND_PARENT, false).set("");
// Write msg, but suppress recursive parent search
transportSendWrite(BROADCAST_ADDRESS, _msg);
// Wait for ping response.
wait(2000);
findingParentNode = false;
}
+109
View File
@@ -0,0 +1,109 @@
/**
* The MySensors Arduino library handles the wireless radio link and protocol
* between your home built sensors/actuators and HA controller of choice.
* The sensors forms a self healing radio network with optional repeaters. Each
* repeater and gateway builds a routing tables in EEPROM which keeps track of the
* network topology allowing messages to be routed to nodes.
*
* Created by Henrik Ekblad <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 MyTransport_h
#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
// Status when waiting for signing nonce in process
enum { SIGN_WAITING_FOR_NONCE = 0, SIGN_OK = 1 };
// FW config structure, stored in eeprom
typedef struct {
uint16_t type;
uint16_t version;
uint16_t blocks;
uint16_t crc;
} __attribute__((packed)) NodeFirmwareConfig;
typedef struct {
uint16_t type;
uint16_t version;
uint16_t blocks;
uint16_t crc;
uint16_t BLVersion;
} __attribute__((packed)) RequestFirmwareConfig;
typedef struct {
uint16_t type;
uint16_t version;
uint16_t block;
} __attribute__((packed)) RequestFWBlock;
typedef struct {
uint16_t type;
uint16_t version;
uint16_t block;
uint8_t data[FIRMWARE_BLOCK_SIZE];
} __attribute__((packed)) ReplyFWBlock;
#define AUTO 0xFF // 0-254. Id 255 is reserved for auto initialization of nodeId.
#define BROADCAST_ADDRESS ((uint8_t)0xFF)
// invalid distance when searching for parent
#define DISTANCE_INVALID (0xFF)
// Common functions in all radio drivers
#ifdef MY_OTA_FIRMWARE_FEATURE
// do a crc16 on the whole received firmware
bool transportIsValidFirmware();
#endif
void transportProcess();
void transportRequestNodeId();
void transportPresentNode();
void transportFindParentNode();
boolean transportSendRoute(MyMessage &message);
boolean transportSendWrite(uint8_t to, MyMessage &message);
// "Interface" functions for radio driver
bool transportInit();
void transportSetAddress(uint8_t address);
uint8_t transportGetAddress();
bool transportSend(uint8_t to, const void* data, uint8_t len);
bool transportAvailable(uint8_t *to);
uint8_t transportReceive(void* data);
void transportPowerDown();
#endif
@@ -0,0 +1,135 @@
/**
* The MySensors Arduino library handles the wireless radio link and protocol
* between your home built sensors/actuators and HA controller of choice.
* The sensors forms a self healing radio network with optional repeaters. Each
* repeater and gateway builds a routing tables in EEPROM which keeps track of the
* network topology allowing messages to be routed to nodes.
*
* Created by Henrik Ekblad <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 <stdint.h>
#include "drivers/RF24/RF24.h"
#include "drivers/RF24/RF24_config.h"
#if defined(MY_RF24_ENABLE_ENCRYPTION)
#include "drivers/AES/AES.h"
#endif
#define TO_ADDR(x) (MY_RF24_BASE_RADIO_ID + x)
#define WRITE_PIPE ((uint8_t)0)
#define CURRENT_NODE_PIPE ((uint8_t)1)
#define BROADCAST_PIPE ((uint8_t)2)
RF24 _rf24(MY_RF24_CE_PIN, MY_RF24_CS_PIN);
uint8_t _address;
uint8_t _paLevel;
#if defined(MY_RF24_ENABLE_ENCRYPTION)
AES _aes;
uint8_t _dataenc[32] = {0};
uint8_t _psk[] = { MY_RF24_ENCRYPTKEY };
#endif
bool transportInit() {
// Start up the radio library
_rf24.begin();
if (!_rf24.isPVariant()) {
return false;
}
_rf24.setAutoAck(1);
_rf24.setAutoAck(BROADCAST_PIPE,false); // Turn off auto ack for broadcast
_rf24.enableAckPayload();
_rf24.setChannel(MY_RF24_CHANNEL);
_rf24.setPALevel(_paLevel);
if (!_rf24.setDataRate(MY_RF24_DATARATE)) {
return false;
}
_rf24.setRetries(5,15);
_rf24.setCRCLength(RF24_CRC_16);
_rf24.enableDynamicPayloads();
// All nodes listen to broadcast pipe (for FIND_PARENT_RESPONSE messages)
_rf24.openReadingPipe(BROADCAST_PIPE, TO_ADDR(BROADCAST_ADDRESS));
#if defined(MY_RF24_ENABLE_ENCRYPTION)
_aes.set_key(_psk, 16); //set up AES-key
#endif
//_rf24.printDetails();
return true;
}
void transportSetAddress(uint8_t address) {
_address = address;
_rf24.openReadingPipe(WRITE_PIPE, TO_ADDR(address));
_rf24.openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(address));
_rf24.startListening();
}
uint8_t transportGetAddress() {
return _address;
}
bool transportSend(uint8_t to, const void* data, uint8_t len) {
#if defined(MY_RF24_ENABLE_ENCRYPTION)
memcpy(_dataenc,data,len); // copy input data because it is read-only
_aes.set_IV(0);//not sure if necessary
len = len > 16 ? 32 : 16;
_aes.cbc_encrypt(_dataenc, _dataenc, len/16); //encrypt
#endif
// Make sure radio has powered up
_rf24.powerUp();
_rf24.stopListening();
_rf24.openWritingPipe(TO_ADDR(to));
#if defined(MY_RF24_ENABLE_ENCRYPTION)
bool ok = _rf24.write(_dataenc, len, to == BROADCAST_ADDRESS);
#else
bool ok = _rf24.write(data, len, to == BROADCAST_ADDRESS);
#endif
_rf24.startListening();
return ok;
}
bool transportAvailable(uint8_t *to) {
uint8_t pipe = 255;
boolean avail = _rf24.available(&pipe);
(void)avail; //until somebody makes use of 'avail'
if (pipe == CURRENT_NODE_PIPE)
*to = _address;
else if (pipe == BROADCAST_PIPE)
*to = BROADCAST_ADDRESS;
return (_rf24.available() && pipe < 6);
}
uint8_t transportReceive(void* data) {
uint8_t len = _rf24.getDynamicPayloadSize();
_rf24.read(data, len);
#if defined(MY_RF24_ENABLE_ENCRYPTION)
_aes.set_IV(0);//not sure if necessary
_aes.cbc_decrypt((byte*)(data), (byte*)(data), len>16?2:1); // decrypt
#endif
return len;
}
void transportPowerDown() {
_rf24.powerDown();
}
@@ -17,57 +17,54 @@
* version 2 as published by the Free Software Foundation.
*/
#include "MyConfig.h"
#include "MyTransport.h"
#include "MyTransportRFM69.h"
#include <stdint.h>
#include "drivers/RFM69/RFM69.h"
MyTransportRFM69::MyTransportRFM69(uint8_t freqBand, uint8_t networkId, uint8_t slaveSelectPin, uint8_t interruptPin, bool isRFM69HW, uint8_t interruptNum)
:
MyTransport(),
radio(slaveSelectPin, interruptPin, isRFM69HW, interruptNum),
_freqBand(freqBand),
_networkId(networkId)
{
}
RFM69 _radio(MY_RF69_SPI_CS, MY_RF69_IRQ_PIN, MY_IS_RFM69HW, MY_RF69_IRQ_NUM);
uint8_t _address;
bool MyTransportRFM69::init() {
bool transportInit() {
// Start up the radio library (_address will be set later by the MySensors library)
radio.initialize(_freqBand, _address, _networkId);
#ifdef RFM69_ENABLE_ENCRYPTION
radio.encrypt(RFM69_ENCRYPTKEY);
_radio.initialize(MY_RFM69_FREQUENCY, _address, MY_RFM69_NETWORKID);
#ifdef MY_RFM69_ENABLE_ENCRYPTION
_radio.encrypt(RFM69_ENCRYPTKEY);
#endif
return true;
}
void MyTransportRFM69::setAddress(uint8_t address) {
void transportSetAddress(uint8_t address) {
_address = address;
radio.setAddress(address);
_radio.setAddress(address);
}
uint8_t MyTransportRFM69::getAddress() {
uint8_t transportGetAddress() {
return _address;
}
bool MyTransportRFM69::send(uint8_t to, const void* data, uint8_t len) {
return radio.sendWithRetry(to,data,len);
bool transportSend(uint8_t to, const void* data, uint8_t len) {
return _radio.sendWithRetry(to,data,len);
}
bool MyTransportRFM69::available(uint8_t *to) {
if (radio.TARGETID == BROADCAST_ADDRESS)
bool transportAvailable(uint8_t *to) {
if (_radio.TARGETID == BROADCAST_ADDRESS)
*to = BROADCAST_ADDRESS;
else
*to = _address;
return radio.receiveDone();
return _radio.receiveDone();
}
uint8_t MyTransportRFM69::receive(void* data) {
memcpy(data,(const void *)radio.DATA, radio.DATALEN);
uint8_t transportReceive(void* data) {
memcpy(data,(const void *)_radio.DATA, _radio.DATALEN);
// Send ack back if this message wasn't a broadcast
if (radio.TARGETID != RF69_BROADCAST_ADDR)
radio.ACKRequested();
radio.sendACK();
return radio.DATALEN;
if (_radio.TARGETID != RF69_BROADCAST_ADDR)
_radio.ACKRequested();
_radio.sendACK();
return _radio.DATALEN;
}
void MyTransportRFM69::powerDown() {
radio.sleep();
void transportPowerDown() {
_radio.sleep();
}
@@ -6,6 +6,6 @@
#ifndef Version_h
#define Version_h
#define LIBRARY_VERSION "1.5"
#define LIBRARY_VERSION "1.6"
#endif
+598
View File
@@ -0,0 +1,598 @@
#include "AES.h"
/*
---------------------------------------------------------------------------
Copyright (c) 1998-2008, Brian Gladman, Worcester, UK. All rights reserved.
LICENSE TERMS
The redistribution and use of this software (with or without changes)
is allowed without the payment of fees or royalties provided that:
1. source code distributions include the above copyright notice, this
list of conditions and the following disclaimer;
2. binary distributions include the above copyright notice, this list
of conditions and the following disclaimer in their documentation;
3. the name of the copyright holder is not used to endorse products
built using this software without specific written permission.
DISCLAIMER
This software is provided 'as is' with no explicit or implied warranties
in respect of its properties, including, but not limited to, correctness
and/or fitness for purpose.
---------------------------------------------------------------------------
Issue 09/09/2006
This is an AES implementation that uses only 8-bit byte operations on the
cipher state (there are options to use 32-bit types if available).
The combination of mix columns and byte substitution used here is based on
that developed by Karl Malbrain. His contribution is acknowledged.
*/
/* This version derived by Mark Tillotson 2012-01-23, tidied up, slimmed down
and tailored to 8-bit microcontroller abilities and Arduino datatypes.
The s-box and inverse s-box were retained as tables (0.5kB PROGMEM) but all
the other transformations are coded to save table space. Many efficiency
improvments to the routines mix_sub_columns() and inv_mix_sub_columns()
(mainly common sub-expression elimination).
Only the routines with precalculated subkey schedule are retained (together
with set_key() - this does however mean each AES object takes 240 bytes of
RAM, alas)
The CBC routines side-effect the iv argument (so that successive calls work
together correctly).
All the encryption and decryption routines work with plain == cipher for
in-place encryption, note.
*/
/* functions for finite field multiplication in the AES Galois field */
/* code was modified by george spanos <spaniakos@gmail.com>
* 16/12/14
*/
// GF(2^8) stuff
#define WPOLY 0x011B
#define DPOLY 0x008D
const static byte s_fwd [0x100] PROGMEM =
{
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16,
} ;
const static byte s_inv [0x100] PROGMEM =
{
0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d,
} ;
// times 2 in the GF(2^8)
#define f2(x) ((x) & 0x80 ? (x << 1) ^ WPOLY : x << 1)
#define d2(x) (((x) >> 1) ^ ((x) & 1 ? DPOLY : 0))
static byte s_box (byte x)
{
// return fwd_affine (pgm_read_byte (&inv [x])) ;
return pgm_read_byte (& s_fwd [x]) ;
}
// Inverse Sbox
static byte is_box (byte x)
{
// return pgm_read_byte (&inv [inv_affine (x)]) ;
return pgm_read_byte (& s_inv [x]) ;
}
static void xor_block (byte * d, byte * s)
{
for (byte i = 0 ; i < N_BLOCK ; i += 4)
{
*d++ ^= *s++ ; // some unrolling
*d++ ^= *s++ ;
*d++ ^= *s++ ;
*d++ ^= *s++ ;
}
}
static void copy_and_key (byte * d, byte * s, byte * k)
{
for (byte i = 0 ; i < N_BLOCK ; i += 4)
{
*d++ = *s++ ^ *k++ ; // some unrolling
*d++ = *s++ ^ *k++ ;
*d++ = *s++ ^ *k++ ;
*d++ = *s++ ^ *k++ ;
}
}
// #define add_round_key(d, k) xor_block (d, k)
/* SUB ROW PHASE */
static void shift_sub_rows (byte st [N_BLOCK])
{
st [0] = s_box (st [0]) ; st [4] = s_box (st [4]) ;
st [8] = s_box (st [8]) ; st [12] = s_box (st [12]) ;
byte tt = st [1] ;
st [1] = s_box (st [5]) ; st [5] = s_box (st [9]) ;
st [9] = s_box (st [13]) ; st [13] = s_box (tt) ;
tt = st[2] ; st [2] = s_box (st [10]) ; st [10] = s_box (tt) ;
tt = st[6] ; st [6] = s_box (st [14]) ; st [14] = s_box (tt) ;
tt = st[15] ;
st [15] = s_box (st [11]) ; st [11] = s_box (st [7]) ;
st [7] = s_box (st [3]) ; st [3] = s_box (tt) ;
}
static void inv_shift_sub_rows (byte st[N_BLOCK])
{
st [0] = is_box (st[0]) ; st [4] = is_box (st [4]);
st [8] = is_box (st[8]) ; st [12] = is_box (st [12]);
byte tt = st[13] ;
st [13] = is_box (st [9]) ; st [9] = is_box (st [5]) ;
st [5] = is_box (st [1]) ; st [1] = is_box (tt) ;
tt = st [2] ; st [2] = is_box (st [10]) ; st [10] = is_box (tt) ;
tt = st [6] ; st [6] = is_box (st [14]) ; st [14] = is_box (tt) ;
tt = st [3] ;
st [3] = is_box (st [7]) ; st [7] = is_box (st [11]) ;
st [11] = is_box (st [15]) ; st [15] = is_box (tt) ;
}
/* SUB COLUMNS PHASE */
static void mix_sub_columns (byte dt[N_BLOCK], byte st[N_BLOCK])
{
byte j = 5 ;
byte k = 10 ;
byte l = 15 ;
for (byte i = 0 ; i < N_BLOCK ; i += N_COL)
{
byte a = st [i] ;
byte b = st [j] ; j = (j+N_COL) & 15 ;
byte c = st [k] ; k = (k+N_COL) & 15 ;
byte d = st [l] ; l = (l+N_COL) & 15 ;
byte a1 = s_box (a), b1 = s_box (b), c1 = s_box (c), d1 = s_box (d) ;
byte a2 = f2(a1), b2 = f2(b1), c2 = f2(c1), d2 = f2(d1) ;
dt[i] = a2 ^ b2^b1 ^ c1 ^ d1 ;
dt[i+1] = a1 ^ b2 ^ c2^c1 ^ d1 ;
dt[i+2] = a1 ^ b1 ^ c2 ^ d2^d1 ;
dt[i+3] = a2^a1 ^ b1 ^ c1 ^ d2 ;
}
}
static void inv_mix_sub_columns (byte dt[N_BLOCK], byte st[N_BLOCK])
{
for (byte i = 0 ; i < N_BLOCK ; i += N_COL)
{
byte a1 = st [i] ;
byte b1 = st [i+1] ;
byte c1 = st [i+2] ;
byte d1 = st [i+3] ;
byte a2 = f2(a1), b2 = f2(b1), c2 = f2(c1), d2 = f2(d1) ;
byte a4 = f2(a2), b4 = f2(b2), c4 = f2(c2), d4 = f2(d2) ;
byte a8 = f2(a4), b8 = f2(b4), c8 = f2(c4), d8 = f2(d4) ;
byte a9 = a8 ^ a1,b9 = b8 ^ b1,c9 = c8 ^ c1,d9 = d8 ^ d1 ;
byte ac = a8 ^ a4,bc = b8 ^ b4,cc = c8 ^ c4,dc = d8 ^ d4 ;
dt[i] = is_box (ac^a2 ^ b9^b2 ^ cc^c1 ^ d9) ;
dt[(i+5)&15] = is_box (a9 ^ bc^b2 ^ c9^c2 ^ dc^d1) ;
dt[(i+10)&15] = is_box (ac^a1 ^ b9 ^ cc^c2 ^ d9^d2) ;
dt[(i+15)&15] = is_box (a9^a2 ^ bc^b1 ^ c9 ^ dc^d2) ;
}
}
/******************************************************************************/
AES::AES(){
byte ar_iv[8] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01 };
memcpy(iv,ar_iv,8);
memcpy(iv+8,ar_iv,8);
arr_pad[0] = 0x01;
arr_pad[1] = 0x02;
arr_pad[2] = 0x03;
arr_pad[3] = 0x04;
arr_pad[4] = 0x05;
arr_pad[5] = 0x06;
arr_pad[6] = 0x07;
arr_pad[7] = 0x08;
arr_pad[8] = 0x09;
arr_pad[9] = 0x0a;
arr_pad[10] = 0x0b;
arr_pad[11] = 0x0c;
arr_pad[12] = 0x0d;
arr_pad[13] = 0x0e;
arr_pad[14] = 0x0f;
}
/******************************************************************************/
byte AES::set_key (byte key [], int keylen)
{
byte hi ;
switch (keylen)
{
case 16:
case 128:
keylen = 16; // 10 rounds
round = 10 ;
break;
case 24:
case 192:
keylen = 24; // 12 rounds
round = 12 ;
break;
case 32:
case 256:
keylen = 32; // 14 rounds
round = 14 ;
break;
default:
round = 0;
return AES_FAILURE;
}
hi = (round + 1) << 4 ;
copy_n_bytes (key_sched, key, keylen) ;
byte t[4] ;
byte next = keylen ;
for (byte cc = keylen, rc = 1 ; cc < hi ; cc += N_COL)
{
for (byte i = 0 ; i < N_COL ; i++)
t[i] = key_sched [cc-4+i] ;
if (cc == next)
{
next += keylen ;
byte ttt = t[0] ;
t[0] = s_box (t[1]) ^ rc ;
t[1] = s_box (t[2]) ;
t[2] = s_box (t[3]) ;
t[3] = s_box (ttt) ;
rc = f2 (rc) ;
}
else if (keylen == 32 && (cc & 31) == 16)
{
for (byte i = 0 ; i < 4 ; i++)
t[i] = s_box (t[i]) ;
}
byte tt = cc - keylen ;
for (byte i = 0 ; i < N_COL ; i++)
key_sched [cc + i] = key_sched [tt + i] ^ t[i] ;
}
return AES_SUCCESS ;
}
/******************************************************************************/
void AES::clean ()
{
for (byte i = 0 ; i < KEY_SCHEDULE_BYTES ; i++)
key_sched [i] = 0 ;
round = 0 ;
}
/******************************************************************************/
void AES::copy_n_bytes (byte * d, byte * s, byte nn)
{
while (nn >= 4)
{
*d++ = *s++ ; // some unrolling
*d++ = *s++ ;
*d++ = *s++ ;
*d++ = *s++ ;
nn -= 4 ;
}
while (nn--)
*d++ = *s++ ;
}
/******************************************************************************/
byte AES::encrypt (byte plain [N_BLOCK], byte cipher [N_BLOCK])
{
if (round)
{
byte s1 [N_BLOCK], r ;
copy_and_key (s1, plain, (byte*) (key_sched)) ;
for (r = 1 ; r < round ; r++)
{
byte s2 [N_BLOCK] ;
mix_sub_columns (s2, s1) ;
copy_and_key (s1, s2, (byte*) (key_sched + r * N_BLOCK)) ;
}
shift_sub_rows (s1) ;
copy_and_key (cipher, s1, (byte*) (key_sched + r * N_BLOCK)) ;
}
else
return AES_FAILURE ;
return AES_SUCCESS ;
}
/******************************************************************************/
byte AES::cbc_encrypt (byte * plain, byte * cipher, int n_block, byte iv [N_BLOCK])
{
while (n_block--)
{
xor_block (iv, plain) ;
if (encrypt (iv, iv) != AES_SUCCESS)
return AES_FAILURE ;
copy_n_bytes (cipher, iv, N_BLOCK) ;
plain += N_BLOCK ;
cipher += N_BLOCK ;
}
return AES_SUCCESS ;
}
/******************************************************************************/
byte AES::cbc_encrypt (byte * plain, byte * cipher, int n_block)
{
while (n_block--)
{
xor_block (iv, plain) ;
if (encrypt (iv, iv) != AES_SUCCESS)
return AES_FAILURE ;
copy_n_bytes (cipher, iv, N_BLOCK) ;
plain += N_BLOCK ;
cipher += N_BLOCK ;
}
return AES_SUCCESS ;
}
/******************************************************************************/
byte AES::decrypt (byte plain [N_BLOCK], byte cipher [N_BLOCK])
{
if (round)
{
byte s1 [N_BLOCK] ;
copy_and_key (s1, plain, (byte*) (key_sched + round * N_BLOCK)) ;
inv_shift_sub_rows (s1) ;
for (byte r = round ; --r ; )
{
byte s2 [N_BLOCK] ;
copy_and_key (s2, s1, (byte*) (key_sched + r * N_BLOCK)) ;
inv_mix_sub_columns (s1, s2) ;
}
copy_and_key (cipher, s1, (byte*) (key_sched)) ;
}
else
return AES_FAILURE ;
return AES_SUCCESS ;
}
/******************************************************************************/
byte AES::cbc_decrypt (byte * cipher, byte * plain, int n_block, byte iv [N_BLOCK])
{
while (n_block--)
{
byte tmp [N_BLOCK] ;
copy_n_bytes (tmp, cipher, N_BLOCK) ;
if (decrypt (cipher, plain) != AES_SUCCESS)
return AES_FAILURE ;
xor_block (plain, iv) ;
copy_n_bytes (iv, tmp, N_BLOCK) ;
plain += N_BLOCK ;
cipher += N_BLOCK;
}
return AES_SUCCESS ;
}
/******************************************************************************/
byte AES::cbc_decrypt (byte * cipher, byte * plain, int n_block)
{
while (n_block--)
{
byte tmp [N_BLOCK] ;
copy_n_bytes (tmp, cipher, N_BLOCK) ;
if (decrypt (cipher, plain) != AES_SUCCESS)
return AES_FAILURE ;
xor_block (plain, iv) ;
copy_n_bytes (iv, tmp, N_BLOCK) ;
plain += N_BLOCK ;
cipher += N_BLOCK;
}
return AES_SUCCESS ;
}
/*****************************************************************************/
void AES::set_IV(unsigned long long int IVCl){
memcpy(iv,&IVCl,8);
memcpy(iv+8,&IVCl,8);
IVC = IVCl;
}
/******************************************************************************/
void AES::iv_inc(){
IVC += 1;
memcpy(iv,&IVC,8);
memcpy(iv+8,&IVC,8);
}
/******************************************************************************/
int AES::get_size(){
return size;
}
/******************************************************************************/
void AES::set_size(int sizel){
size = sizel;
}
/******************************************************************************/
void AES::get_IV(byte *out){
memcpy(out,&IVC,8);
memcpy(out+8,&IVC,8);
}
/******************************************************************************/
void AES::calc_size_n_pad(int p_size){
int s_of_p = p_size - 1;
if ( s_of_p % N_BLOCK == 0){
size = s_of_p;
}else{
size = s_of_p + (N_BLOCK-(s_of_p % N_BLOCK));
}
pad = size - s_of_p;
}
/******************************************************************************/
void AES::padPlaintext(void* in,byte* out)
{
memcpy(out,in,size);
for (int i = size-pad; i < size; i++){;
out[i] = arr_pad[pad - 1];
}
}
/******************************************************************************/
bool AES::CheckPad(byte* in,int lsize){
if (in[lsize-1] <= 0x0f){
int lpad = (int)in[lsize-1];
for (int i = lsize - 1; i >= lsize-lpad; i--){
if (arr_pad[lpad - 1] != in[i]){
return false;
}
}
}else{
return true;
}
return true;
}
/******************************************************************************/
void AES::printArray(byte output[],bool p_pad)
{
uint8_t i,j;
uint8_t loops = size/N_BLOCK;
uint8_t outp = N_BLOCK;
for (j = 0; j < loops; j += 1){
if (p_pad && (j == (loops - 1)) ) { outp = N_BLOCK - pad; }
for (i = 0; i < outp; i++)
{
printf_P(PSTR("%c"),output[j*N_BLOCK + i]);
}
}
printf_P(PSTR("\n"));
}
/******************************************************************************/
void AES::printArray(byte output[],int sizel)
{
for (int i = 0; i < sizel; i++)
{
printf_P(PSTR("%x"),output[i]);
}
printf_P(PSTR("\n"));
}
/******************************************************************************/
void AES::do_aes_encrypt(byte *plain,int size_p,byte *cipher,byte *key, int bits, byte ivl [N_BLOCK]){
calc_size_n_pad(size_p);
byte plain_p[get_size()];
padPlaintext(plain,plain_p);
int blocks = get_size() / N_BLOCK;
set_key (key, bits) ;
cbc_encrypt (plain_p, cipher, blocks, ivl);
}
/******************************************************************************/
void AES::do_aes_encrypt(byte *plain,int size_p,byte *cipher,byte *key, int bits){
calc_size_n_pad(size_p);
byte plain_p[get_size()];
padPlaintext(plain,plain_p);
int blocks = get_size() / N_BLOCK;
set_key (key, bits) ;
cbc_encrypt (plain_p, cipher, blocks);
}
/******************************************************************************/
void AES::do_aes_decrypt(byte *cipher,int size_c,byte *plain,byte *key, int bits, byte ivl [N_BLOCK]){
set_size(size_c);
int blocks = size_c / N_BLOCK;
set_key (key, bits);
cbc_decrypt (cipher,plain, blocks, ivl);
}
/******************************************************************************/
void AES::do_aes_decrypt(byte *cipher,int size_c,byte *plain,byte *key, int bits){
set_size(size_c);
int blocks = size_c / N_BLOCK;
set_key (key, bits);
cbc_decrypt (cipher,plain, blocks);
}
/******************************************************************************/
#if defined(AES_LINUX)
double AES::millis(){
gettimeofday(&tv, NULL);
return (tv.tv_sec + 0.000001 * tv.tv_usec);
}
#endif
+415
View File
@@ -0,0 +1,415 @@
#ifndef __AES_H__
#define __AES_H__
#include "AES_config.h"
/*
---------------------------------------------------------------------------
Copyright (c) 1998-2008, Brian Gladman, Worcester, UK. All rights reserved.
LICENSE TERMS
The redistribution and use of this software (with or without changes)
is allowed without the payment of fees or royalties provided that:
1. source code distributions include the above copyright notice, this
list of conditions and the following disclaimer;
2. binary distributions include the above copyright notice, this list
of conditions and the following disclaimer in their documentation;
3. the name of the copyright holder is not used to endorse products
built using this software without specific written permission.
DISCLAIMER
This software is provided 'as is' with no explicit or implied warranties
in respect of its properties, including, but not limited to, correctness
and/or fitness for purpose.
---------------------------------------------------------------------------
Issue 09/09/2006
This is an AES implementation that uses only 8-bit byte operations on the
cipher state.
*/
/* code was modified by george spanos <spaniakos@gmail.com>
* 16/12/14
*/
class AES
{
public:
/* The following calls are for a precomputed key schedule
NOTE: If the length_type used for the key length is an
unsigned 8-bit character, a key length of 256 bits must
be entered as a length in bytes (valid inputs are hence
128, 192, 16, 24 and 32).
*/
/** \fn AES()
* \brief AES constructor
*
* This function initialized an instance of AES.
*/
AES();
/** Set the cipher key for the pre-keyed version.
* @param key[] pointer to the key string.
* @param keylen Integer that indicates the length of the key.
* @note NOTE: If the length_type used for the key length is an unsigned 8-bit character,
* a key length of 256 bits must be entered as a length in bytes
* (valid inputs are hence 128, 192, 16, 24 and 32).
*
*/
byte set_key (byte key[], int keylen) ;
/** clean up subkeys after use.
*
*/
void clean () ; // delete key schedule after use
/** copying and xoring utilities.
* @param *AESt byte pointer of the AEStination array.
* @param *src byte pointer of the source array.
* @param n byte, indicating the sizeof the bytes to be copied.
* @note this is an alternative for memcpy(void *s1,const void *s2, site_t n),
* i have not updated the function in the implementation yet, but it is considered a future plan.
*
*/
void copy_n_bytes (byte * AESt, byte * src, byte n) ;
/** Encrypt a single block of 16 bytes .
* @param plain[N_BLOCK] Array of the plaintext.
* @param cipher[N_BLOCK] Array of the ciphertext.
* @note The N_BLOCK is defined in AES_config.h as,
* @code #define N_ROW 4
* #define N_COL 4
* #define N_BLOCK (N_ROW * N_COL)
* @endcode
* Changed to that will change the Block_size.
* @Return 0 if SUCCESS or -1 if FAILURE
*
*/
byte encrypt (byte plain [N_BLOCK], byte cipher [N_BLOCK]) ;
/** CBC encrypt a number of blocks (input and return an IV).
*
* @param *plain Pointer, points to the plaintex.
* @param *cipher Pointer, points to the ciphertext that will be created.
* @param n_block integer, indicated the number of blocks to be ciphered.
* @param iv[N_BLOCK] byte Array that holds the IV (initialization vector).
* @Return 0 if SUCCESS or -1 if FAILURE
*
*/
byte cbc_encrypt (byte * plain, byte * cipher, int n_block, byte iv [N_BLOCK]) ;
/** CBC encrypt a number of blocks (input and return an IV).
*
* @param *plain Pointer, points to the plaintex.
* @param *cipher Pointer, points to the ciphertext that will be created.
* @param n_block integer, indicated the number of blocks to be ciphered.
* @Return 0 if SUCCESS or -1 if FAILURE
*
*/
byte cbc_encrypt (byte * plain, byte * cipher, int n_block) ;
/** Decrypt a single block of 16 bytes
* @param cipher[N_BLOCK] Array of the ciphertext.
* @param plain[N_BLOCK] Array of the plaintext.
* @note The N_BLOCK is defined in AES_config.h as,
* @code #define N_ROW 4
* #define N_COL 4
* #define N_BLOCK (N_ROW * N_COL)
* @endcode
* Changed to that will change the Block_size.
* @Return 0 if SUCCESS or -1 if FAILURE
*
*/
byte decrypt (byte cipher [N_BLOCK], byte plain [N_BLOCK]) ;
/** CBC decrypt a number of blocks (input and return an IV)
*
* @param *cipher Pointer, points to the ciphertext that will be created.
* @param *plain Pointer, points to the plaintex.
* @param n_block integer, indicated the number of blocks to be ciphered.
* @param iv[N_BLOCK] byte Array that holds the IV (initialization vector).
* @Return 0 if SUCCESS or -1 if FAILURE
*
*/
byte cbc_decrypt (byte * cipher, byte * plain, int n_block, byte iv [N_BLOCK]) ;
/** CBC decrypt a number of blocks (input and return an IV)
*
* @param *cipher Pointer, points to the ciphertext that will be created.
* @param *plain Pointer, points to the plaintex.
* @param n_block integer, indicated the number of blocks to be ciphered.
* @Return 0 if SUCCESS or -1 if FAILURE
*
*/
byte cbc_decrypt (byte * cipher, byte * plain, int n_block) ;
/** Sets IV (initialization vector) and IVC (IV counter).
* This function changes the ivc and iv variables needed for AES.
*
* @param IVCl int or hex value of iv , ex. 0x0000000000000001
* @note example:
* @code unsigned long long int my_iv = 01234567; @endcode
*/
void set_IV(unsigned long long int IVCl);
/** increase the iv (initialization vector) and IVC (IV counter) by 1
*
* This function increased the VI by one step in order to have a different IV each time
*
*/
void iv_inc();
/** Getter method for size
*
* This function return the size
* @return an integer, that is the size of the of the padded plaintext,
* thus, the size of the ciphertext.
*/
int get_size();
/** Setter method for size
*
* This function sets the size of the plaintext+pad
*
*/
void set_size(int sizel);
/** Getter method for IV
*
* This function return the IV
* @param out byte pointer that gets the IV.
* @return none, the IV is writed to the out pointer.
*/
void get_IV(byte *out);
/** Calculates the size of the plaintext and the padding.
*
* Calculates the size of theplaintext with the padding
* and the size of the padding needed. Moreover it stores them in their class variables.
*
* @param p_size the size of the byte array ex sizeof(plaintext)
*/
void calc_size_n_pad(int p_size);
/** Pads the plaintext
*
* This function pads the plaintext and returns an char array with the
* plaintext and the padding in order for the plaintext to be compatible with
* 16bit size blocks required by AES
*
* @param in the string of the plaintext in a byte array
* @param out The string of the out array.
* @return no return, The padded plaintext is stored in the out pointer.
*/
void padPlaintext(void* in,byte* out);
/** Check the if the padding is correct.
*
* This functions checks the padding of the plaintext.
*
* @param in the string of the plaintext in a byte array
* @param size the size of the string
* @return true if correct / false if not
*/
bool CheckPad(byte* in,int size);
/** Prints the array given.
*
* This function prints the given array and pad,
* It is mainlly used for debugging purpuses or to output the string.
*
* @param output[] the string of the text in a byte array
* @param p_pad optional, used to print with out the padding characters
*/
void printArray(byte output[],bool p_pad = true);
/** Prints the array given.
*
* This function prints the given array in Hexadecimal.
*
* @param output[] the string of the text in a byte array
* @param sizel the size of the array.
*/
void printArray(byte output[],int sizel);
/** User friendly implementation of AES-CBC encryption.
*
* @param *plain pointer to the plaintext
* @param size_p size of the plaintext
* @param *cipher pointer to the ciphertext
* @param *key pointer to the key that will be used.
* @param bits bits of the encryption/decrpytion
* @param ivl[N_BLOCK] the initialization vector IV that will be used for encryption.
* @note The key will be stored in class variable.
*/
void do_aes_encrypt(byte *plain,int size_p,byte *cipher,byte *key, int bits, byte ivl [N_BLOCK]);
/** User friendly implementation of AES-CBC encryption.
*
* @param *plain pointer to the plaintext
* @param size_p size of the plaintext
* @param *cipher pointer to the ciphertext
* @param *key pointer to the key that will be used.
* @param bits bits of the encryption/decrpytion
* @note The key will be stored in class variable.
*/
void do_aes_encrypt(byte *plain,int size_p,byte *cipher,byte *key, int bits);
/** User friendly implementation of AES-CBC decryption.
*
* @param *cipher pointer to the ciphertext
* @param size_c size of the ciphertext
* @param *plain pointer to the plaintext
* @param *key pointer to the key that will be used.
* @param bits bits of the encryption/decrpytion
* @param ivl[N_BLOCK] the initialization vector IV that will be used for decryption.
* @note The key will be stored in class variable.
*/
void do_aes_decrypt(byte *cipher,int size_c,byte *plain,byte *key, int bits, byte ivl [N_BLOCK]);
/** User friendly implementation of AES-CBC decryption.
*
* @param *cipher pointer to the ciphertext
* @param size_c size of the ciphertext
* @param *plain pointer to the plaintext
* @param *key pointer to the key that will be used.
* @param bits bits of the encryption/decrpytion
* @note The key will be stored in class variable.
*/
void do_aes_decrypt(byte *cipher,int size_c,byte *plain,byte *key, int bits);
#if defined(AES_LINUX)
/**
* used in linux in order to retrieve the time in milliseconds.
*
* @return returns the milliseconds in a double format.
*/
double millis();
#endif
private:
int round ;/**< holds the number of rounds to be used. */
byte key_sched [KEY_SCHEDULE_BYTES] ;/**< holds the pre-computed key for the encryption/decrpytion. */
unsigned long long int IVC;/**< holds the initialization vector counter in numerical format. */
byte iv[16];/**< holds the initialization vector that will be used in the cipher. */
int pad;/**< holds the size of the padding. */
int size;/**< hold the size of the plaintext to be ciphered */
#if defined(AES_LINUX)
timeval tv;/**< holds the time value on linux */
byte arr_pad[15];/**< holds the hexadecimal padding values on linux */
#else
byte arr_pad[15];// = { 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f };/**< holds the hexadecimal padding values */
#endif
} ;
#endif
/**
* @example aes.ino
* <b>For Arduino</b><br>
* <b>Updated: spaniakos 2015 </b><br>
*
* This is an example of how to use AES in CBC mode easily.
* The text and keys can be either in HEX or String format.<br />
*/
/**
* @example aes.cpp
* <b>For Rasberry pi</b><br>
* <b>Updated: spaniakos 2015 </b><br>
*
* This is an example of how to use AES in CBC mode easily.
* The text and keys can be either in HEX or String format.<br />
*/
/**
* @example test_vectors.ino
* <b>For Arduino</b><br>
* <b>Updated: spaniakos 2015 </b><br>
*
* This is an example of monte carlo test vectors, in order to justify the effectiveness of the algorithm.<br />
* plus is a classical approach to the AES encryption library with out the easy to use add-on modifications.
*/
/**
* @example test_vectors.cpp
* <b>For Rasberry pi</b><br>
* <b>Updated: spaniakos 2015 </b><br>
*
* This is an example of monte carlo test vectors, in order to justify the effectiveness of the algorithm.<br />
* plus is a classical approach to the AES encryption library with out the easy to use add-on modifications.
*/
/**
* @mainpage AES library for Arduino and Raspberry pi.
*
* @section Goals design Goals
*
* This library is AESigned to be...
* @li Fast and efficient.
* @li Able to effectively encrypt and decrypt any size of string.
* @li Able to encrypt and decrypt using AES
* @li Able to encrypt and decrypt using AES-CBC
* @li Easy for the user to use in his programs.
*
* @section Acknowledgements Acknowledgements
* This is an AES library for the Arduino, based on tzikis's AES library, which you can find <a href= "https://github.com/tzikis/arduino">here:</a>.<br />
* Tzikis library was based on scottmac`s library, which you can find <a href="https://github.com/scottmac/arduino">here:</a><br />
*
* @section Installation Installation
* <h3>Arduino</h3>
* Create a folder named _AES_ in the _libraries_ folder inside your Arduino sketch folder. If the
* libraries folder doesn't exist, create it. Then copy everything inside. (re)launch the Arduino IDE.<br />
* You're done. Time for a mojito
*
* <h3>Raspberry pi</h3>
* <b>install</b><br /><br />
*
* sudo make install<br />
* cd examples_Rpi<br />
* make<br /><br />
*
* <b>What to do after changes to the library</b><br /><br />
* sudo make clean<br />
* sudo make install<br />
* cd examples_Rpi<br />
* make clean<br />
* make<br /><br />
* <b>What to do after changes to a sketch</b><br /><br />
* cd examples_Rpi<br />
* make <sketch><br /><br />
* or <br />
* make clean<br />
* make<br /><br /><br />
* <b>How to start a sketch</b><br /><br />
* cd examples_Rpi<br />
* sudo ./<sketch><br /><br />
*
* @section News News
*
* If issues are discovered with the documentation, please report them <a href="https://github.com/spaniakos/spaniakos.github.io/issues"> here</a>
* @section Useful Useful References
*
* Please refer to:
*
* @li <a href="http://spaniakos.github.io/AES/classAES.html"><b>AES</b> Class Documentation</a>
* @li <a href="https://github.com/spaniakos/AES/archive/master.zip"><b>Download</b></a>
* @li <a href="https://github.com/spaniakos/AES/"><b>Source Code</b></a>
* @li <a href="http://spaniakos.github.io/">All spaniakos Documentation Main Page</a>
*
* @section Board_Support Board Support
*
* Most standard Arduino based boards are supported:
* - Arduino
* - Intel Galileo support
* - Raspberry Pi Support
*
* - The library has not been tested to other boards, but it should suppport ATMega 328 based boards,Mega Boards,Arduino Due,ATTiny board
*/
@@ -0,0 +1,44 @@
/* code was modified by george spanos <spaniakos@gmail.com>
* 16/12/14
*/
#ifndef __AES_CONFIG_H__
#define __AES_CONFIG_H__
#if (defined(__linux) || defined(linux)) && !defined(__ARDUINO_X86__)
#define AES_LINUX
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <unistd.h>
#else
#include <Arduino.h>
#endif
#include <stdint.h>
#include <string.h>
#if defined(__ARDUINO_X86__) || (defined (__linux) || defined (linux))
#undef PROGMEM
#define PROGMEM __attribute__(( section(".progmem.data") ))
#define pgm_read_byte(p) (*(p))
typedef unsigned char byte;
#define printf_P printf
#define PSTR(x) (x)
#else
#include <avr/pgmspace.h>
#endif
#define N_ROW 4
#define N_COL 4
#define N_BLOCK (N_ROW * N_COL)
#define N_MAX_ROUNDS 14
#define KEY_SCHEDULE_BYTES ((N_MAX_ROUNDS + 1) * N_BLOCK)
#define AES_SUCCESS (0)
#define AES_FAILURE (-1)
#endif
File diff suppressed because it is too large Load Diff
+62
View File
@@ -0,0 +1,62 @@
#############################################################################
#
# Makefile for AES on Raspberry Pi
#
# License: GPL (General Public License)
# Author: Georgios Spanos (spaniakos) <spaniakos@gmail.com>
# Date: 02/12/2014
#
# Description:
# ------------
# use make all and mak install to install the library
# You can change the install directory by editing the LIBDIR line
#
PREFIX=/usr/local
# Library parameters
# where to put the lib
LIBDIR=$(PREFIX)/lib
# lib name
LIB=libAES
# shared library name
LIBNAME=$(LIB).so.1.0
# Where to put the header files
HEADER_DIR=${PREFIX}/include/AES
# The recommended compiler flags for the Raspberry Pi
CCFLAGS=-Ofast -mfpu=vfp -mfloat-abi=hard -march=armv6zk -mtune=arm1176jzf-s
# make all
# reinstall the library after each recompilation
all: libAES
# Make the library
libAES: AES.o
g++ -shared -Wl,-soname,$@.so.1 ${CCFLAGS} -o ${LIBNAME} $^
# Library parts
AES.o: AES.cpp
g++ -Wall -fPIC ${CCFLAGS} -c $^
# clear build files
clean:
rm -rf *.o ${LIB}.*
rm -rf ${LIBDIR}/${LIB}.*
rm -rf ${HEADER_DIR}
install: all install-libs install-headers
# Install the library to LIBPATH
install-libs:
@echo "[Installing Libs]"
@if ( test ! -d $(PREFIX)/lib ) ; then mkdir -p $(PREFIX)/lib ; fi
@install -m 0755 ${LIBNAME} ${LIBDIR}
@ln -sf ${LIBDIR}/${LIBNAME} ${LIBDIR}/${LIB}.so.1
@ln -sf ${LIBDIR}/${LIBNAME} ${LIBDIR}/${LIB}.so
@ldconfig
install-headers:
@echo "[Installing Headers]"
@if ( test ! -d ${HEADER_DIR} ) ; then mkdir -p ${HEADER_DIR} ; fi
@install -m 0644 *.h ${HEADER_DIR}
+91
View File
@@ -0,0 +1,91 @@
### Arduino
just run the sketched aftel loading the libraries.
### Raspberry pi
install
```
sudo make install
cd examples_Rpi
make
```
What to do after changes to the library
```
sudo make clean
sudo make install
cd examples_Rpi
make clean
make
```
What to do after changes to a sketch
```
cd examples_Rpi
make <sketch>
or
make clean
make
```
How to start a sketch
```
cd examples_Rpi
sudo ./<sketch>
```
This is a derived work from Brian Gladman's AES implementation for byte-oriented
processors, here's the licence terms:
/*
---------------------------------------------------------------------------
Copyright (c) 1998-2008, Brian Gladman, Worcester, UK. All rights reserved.
LICENSE TERMS
The redistribution and use of this software (with or without changes)
is allowed without the payment of fees or royalties provided that:
1. source code distributions include the above copyright notice, this
list of conditions and the following disclaimer;
2. binary distributions include the above copyright notice, this list
of conditions and the following disclaimer in their documentation;
3. the name of the copyright holder is not used to endorse products
built using this software without specific written permission.
DISCLAIMER
This software is provided 'as is' with no explicit or implied warranties
in respect of its properties, including, but not limited to, correctness
and/or fitness for purpose.
---------------------------------------------------------------------------
Issue 09/09/2006
This is an AES implementation that uses only 8-bit byte operations on the
cipher state (there are options to use 32-bit types if available).
The combination of mix columns and byte substitution used here is based on
that developed by Karl Malbrain. His contribution is acknowledged.
*/
/* This version derived by Mark Tillotson 2012-01-23, tidied up, slimmed down
and tailored to 8-bit microcontroller abilities and Arduino datatypes.
The s-box and inverse s-box were retained as tables (0.5kB PROGMEM) but all
the other transformations are coded to save table space. Many efficiency
improvments to the routines mix_sub_columns() and inv_mix_sub_columns()
(mainly common sub-expression elimination).
Only the routines with precalculated subkey schedule are retained (together
with set_key() - this does however mean each AES object takes 240 bytes of
RAM, alas)
The CBC routines side-effect the iv argument (so that successive calls work
together correctly).
All the encryption and decryption routines work with plain == cipher for
in-place encryption, note.
*/
@@ -0,0 +1,63 @@
#include <AES.h>
#include "./printf.h"
AES aes ;
byte *key = (unsigned char*)"0123456789010123";
byte plain[] = "Add NodeAdd NodeAdd NodeAdd NodeAdd Node";
//real iv = iv x2 ex: 01234567 = 0123456701234567
unsigned long long int my_iv = 36753562;
void setup ()
{
Serial.begin (57600) ;
printf_begin();
delay(500);
printf("\n===testng mode\n") ;
// otfly_test () ;
// otfly_test256 () ;
}
void loop ()
{
prekey_test () ;
delay(2000);
}
void prekey (int bits)
{
aes.iv_inc();
byte iv [N_BLOCK] ;
byte plain_p[48];
byte cipher [48] ;
byte check [48] ;
unsigned long ms = micros ();
aes.set_IV(my_iv);
aes.get_IV(iv);
aes.do_aes_encrypt(plain,41,cipher,key,bits,iv);
Serial.print("Encryption took: ");
Serial.println(micros() - ms);
ms = micros ();
aes.set_IV(my_iv);
aes.get_IV(iv);
aes.do_aes_decrypt(cipher,48,check,key,bits,iv);
Serial.print("Decryption took: ");
Serial.println(micros() - ms);
printf("\n\nPLAIN :");
aes.printArray(plain,(bool)true);
printf("\nCIPHER:");
aes.printArray(cipher,(bool)false);
printf("\nCHECK :");
aes.printArray(check,(bool)true);
printf("\nIV :");
aes.printArray(iv,16);
printf("\n============================================================\n");
}
void prekey_test ()
{
prekey (128) ;
}
@@ -0,0 +1,16 @@
Monte Carlo test vectors:
http://csrc.nist.gov/groups/STM/cavp/documents/aes/aesmct.zip
(Compare the Monte Carlo output to the ECBMCT128.rsp file)
Known Answer Tests:
http://csrc.nist.gov/groups/STM/cavp/documents/aes/KAT_AES.zip
- Compare the other test outputs to
ECBVarTxt128.rsp
ECBVarKey128.rsp
ECBVarTxt192.rsp
ECBVarKey192.rsp
ECBVarTxt256.rsp
ECBVarKey256.rsp
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,191 @@
#include <AES.h>
AES aes ;
byte key [2*N_BLOCK] ;
byte plain [N_BLOCK] ;
byte iv [N_BLOCK] ;
byte cipher [N_BLOCK] ;
byte check [N_BLOCK] ;
void loop ()
{}
void setup ()
{
Serial.begin (57600) ;
Serial.print ("AES library test vectors") ;
monte_carlo (128) ;
for (int keysize = 128 ; keysize <= 256 ; keysize += 64)
{
prekey_test_var_plaintext (keysize) ;
prekey_test_var_key (keysize) ;
}
}
void prekey_test_var_plaintext (int bits)
{
Serial.println () ;
Serial.print ("ECB Varying Plaintext ") ;
Serial.print (bits) ;
Serial.println (" bits") ;
Serial.println () ;
byte succ ;
set_bits (bits, key, 0) ; // all zero key
succ = aes.set_key (key, bits) ;
if (succ != SUCCESS)
Serial.println ("Failure set_key") ;
for (int bitcount = 1 ; bitcount <= 128 ; bitcount++)
{
Serial.print ("COUNT = ") ; Serial.println (bitcount-1) ;
print_value ("KEY = ", key, bits) ;
set_bits (128, plain, bitcount) ;
print_value ("PLAINTEXT = ", plain, 128) ;
succ = aes.encrypt (plain, cipher) ;
if (succ != SUCCESS)
Serial.println ("Failure encrypt") ;
print_value ("CIPHERTEXT = ", cipher, 128) ;
succ = aes.decrypt (cipher, check) ;
if (succ != SUCCESS)
Serial.println ("Failure decrypt") ;
//print_value ("CHECK = ", check, 128) ;
check_same (plain, check, 128) ;
Serial.println () ;
}
}
void prekey_test_var_key (int bits)
{
Serial.println () ;
Serial.print ("ECB Varying Key ") ;
Serial.print (bits) ;
Serial.println (" bits") ;
Serial.println () ;
byte succ ;
set_bits (128, plain, 0) ;
for (int bitcount = 1 ; bitcount <= bits ; bitcount++)
{
set_bits (bits, key, bitcount) ; // all zero key
succ = aes.set_key (key, bits) ;
if (succ != SUCCESS)
Serial.println ("Failure set_key") ;
Serial.print ("COUNT = ") ; Serial.println (bitcount-1) ;
print_value ("KEY = ", key, bits) ;
print_value ("PLAINTEXT = ", plain, 128) ;
succ = aes.encrypt (plain, cipher) ;
if (succ != SUCCESS)
Serial.println ("Failure encrypt") ;
print_value ("CIPHERTEXT = ", cipher, 128) ;
succ = aes.decrypt (cipher, check) ;
if (succ != SUCCESS)
Serial.println ("Failure decrypt") ;
check_same (plain, check, 128) ;
Serial.println () ;
}
}
void set_bits (int bits, byte * a, int count)
{
bits >>= 3 ;
byte bcount = count >> 3 ;
for (byte i = 0 ; i < bcount ; i++)
a [i] = 0xFF ;
if ((count & 7) != 0)
a [bcount++] = 0xFF & (0xFF00 >> (count & 7)) ;
for (byte i = bcount ; i < bits ; i++)
a [i] = 0x00 ;
}
void check_same (byte * a, byte * b, int bits)
{
bits >>= 3 ;
for (byte i = 0 ; i < bits ; i++)
if (a[i] != b[i])
{
Serial.println ("Failure plain != check") ;
return ;
}
}
char hex[] = "0123456789abcdef" ;
void print_value (const char * str, byte * a, int bits)
{
Serial.print (str) ;
bits >>= 3 ;
for (int i = 0 ; i < bits ; i++)
{
byte b = a[i] ;
Serial.print (hex [b >> 4]) ;
Serial.print (hex [b & 15]) ;
}
Serial.println () ;
}
byte monteplain [] =
{ 0xb9, 0x14, 0x5a, 0x76, 0x8b, 0x7d, 0xc4, 0x89,
0xa0, 0x96, 0xb5, 0x46, 0xf4, 0x3b, 0x23, 0x1f } ;
byte montekey [] =
{ 0x13, 0x9a, 0x35, 0x42, 0x2f, 0x1d, 0x61, 0xde,
0x3c, 0x91, 0x78, 0x7f, 0xe0, 0x50, 0x7a, 0xfd } ;
void monte_carlo (int bits)
{
Serial.println () ;
Serial.print ("Monte Carlo ") ;
Serial.print (bits) ;
Serial.println (" bits") ;
Serial.println () ;
byte succ ;
for (int i = 0 ; i < 16 ; i++)
{
plain [i] = monteplain [i] ;
key [i] = montekey [i] ;
}
for (int i = 0 ; i < 100 ; i++)
{
Serial.print ("COUNT = ") ; Serial.println (i) ;
print_value ("KEY = ", key, bits) ;
print_value ("PLAINTEXT = ", plain, 128) ;
succ = aes.set_key (key, bits) ;
for (int j = 0 ; j < 1000 ; j++)
{
succ = aes.encrypt (plain, cipher) ;
aes.copy_n_bytes (plain, cipher, 16) ;
}
print_value ("CIPHERTEXT = ", cipher, 128) ;
Serial.println () ;
if (bits == 128)
{
for (byte k = 0 ; k < 16 ; k++)
key [k] ^= cipher [k] ;
}
else if (bits == 192)
{
}
else
{
}
}
}
@@ -0,0 +1,39 @@
#############################################################################
#
# Makefile for AES examples on Raspberry Pi
#
# License: GPL (General Public License)
# Author: gnulnulf <arco@appeltaart.mine.nu>
# Date: 2013/02/07 (version 1.0)
#
# Description:
# ------------
# use make all and make install to install the examples
# You can change the install directory by editing the prefix line
#
prefix := /usr/local
# The recommended compiler flags for the Raspberry Pi
CCFLAGS=-Ofast -mfpu=vfp -mfloat-abi=hard -march=armv6zk -mtune=arm1176jzf-s
#CCFLAGS=
# define all programs
PROGRAMS = aes test_vectors
SOURCES = ${PROGRAMS:=.cpp}
all: ${PROGRAMS}
${PROGRAMS}: ${SOURCES}
g++ ${CCFLAGS} -Wall -I../ -lAES $@.cpp -o $@
clean:
rm -rf $(PROGRAMS)
install: all
test -d $(prefix) || mkdir $(prefix)
test -d $(prefix)/bin || mkdir $(prefix)/bin
for prog in $(PROGRAMS); do \
install -m 0755 $$prog $(prefix)/bin; \
done
.PHONY: install
@@ -0,0 +1,16 @@
Monte Carlo test vectors:
http://csrc.nist.gov/groups/STM/cavp/documents/aes/aesmct.zip
(Compare the Monte Carlo output to the ECBMCT128.rsp file)
Known Answer Tests:
http://csrc.nist.gov/groups/STM/cavp/documents/aes/KAT_AES.zip
- Compare the other test outputs to
ECBVarTxt128.rsp
ECBVarKey128.rsp
ECBVarTxt192.rsp
ECBVarKey192.rsp
ECBVarTxt256.rsp
ECBVarKey256.rsp
@@ -0,0 +1,47 @@
#include <AES.h>
#include "printf.h"
AES aes;
void prekey_test ();
void prekey (int bits, int blocks);
byte key[] = "01234567899876543210012345678998";
byte plain[] = "TESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTEST";
//real iv = iv x2 ex: 01234567 = 0123456701234567
unsigned long long int my_iv = 01234567;
int main(int argc, char** argv)
{
printf("\n===testng mode\n") ;
for (int i=0;i<1;i++){
prekey_test () ;
}
}
void prekey (int bits)
{
byte iv [N_BLOCK] ;
byte plain_p[sizeof(plain) + (N_BLOCK - (sizeof(plain) % 16)) - 1];
byte cipher[sizeof(plain_p)];
aes.do_aes_encrypt(plain,sizeof(plain),cipher,key,bits);
aes.get_IV(iv);
aes.do_aes_dencrypt(cipher,aes.get_size(),plain_p,key,bits,iv);
//normally u have sizeof(cipher) but if its in the same sketch you cannot determin it dynamically
printf("\n\nPLAIN :");
aes.printArray(plain);
printf("\nCIPHER:");
aes.printArray(cipher);
printf("\nPlain2:");
aes.printArray(plain_p);
printf("\n============================================================\n");
}
void prekey_test ()
{
prekey (128) ;
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,183 @@
#include <AES.h>
#include "printf.h"
AES aes ;
byte key [2*N_BLOCK] ;
byte plain [N_BLOCK] ;
byte iv [N_BLOCK] ;
byte cipher [N_BLOCK] ;
byte check [N_BLOCK] ;
void print_value (const char * str, byte * a, int bits);
void prekey_test_var_plaintext (int bits);
void prekey_test_var_key (int bits);
void set_bits (int bits, byte * a, int count);
void check_same (byte * a, byte * b, int bits);
void monte_carlo (int bits);
int main(int argc, char** argv)
{
printf ("AES library test vectors") ;
monte_carlo (128) ;
for (int keysize = 128 ; keysize <= 256 ; keysize += 64)
{
prekey_test_var_plaintext (keysize) ;
prekey_test_var_key (keysize) ;
}
}
void prekey_test_var_plaintext (int bits)
{
printf ("\nECB Varying Plaintext %i bits\n",bits) ;
byte succ ;
set_bits (bits, key, 0) ; // all zero key
succ = aes.set_key (key, bits) ;
if (succ != SUCCESS)
printf("Failure set_key\n") ;
for (int bitcount = 1 ; bitcount <= 128 ; bitcount++)
{
printf ("COUNT = %i \n",bitcount-1);
print_value ("KEY = ", key, bits) ;
set_bits (128, plain, bitcount) ;
print_value ("PLAINTEXT = ", plain, 128) ;
succ = aes.encrypt (plain, cipher) ;
if (succ != SUCCESS)
printf ("Failure encrypt\n") ;
print_value ("CIPHERTEXT = ", cipher, 128) ;
succ = aes.decrypt (cipher, check) ;
if (succ != SUCCESS)
printf ("Failure decrypt\n") ;
//print_value ("CHECK = ", check, 128) ;
check_same (plain, check, 128) ;
printf ("\n") ;
}
}
void prekey_test_var_key (int bits)
{
printf ("\nECB Varying key %i bits\n",bits);
byte succ ;
set_bits (128, plain, 0) ;
for (int bitcount = 1 ; bitcount <= bits ; bitcount++)
{
set_bits (bits, key, bitcount) ; // all zero key
succ = aes.set_key (key, bits) ;
if (succ != SUCCESS)
printf ("Failure set_key\n") ;
printf ("COUNT = %i\n",bitcount-1);
print_value ("KEY = ", key, bits) ;
print_value ("PLAINTEXT = ", plain, 128) ;
succ = aes.encrypt (plain, cipher) ;
if (succ != SUCCESS)
printf ("Failure encrypt\n") ;
print_value ("CIPHERTEXT = ", cipher, 128) ;
succ = aes.decrypt (cipher, check) ;
if (succ != SUCCESS)
printf ("Failure decrypt\n") ;
check_same (plain, check, 128) ;
printf("\n");
}
}
void set_bits (int bits, byte * a, int count)
{
bits >>= 3 ;
byte bcount = count >> 3 ;
for (byte i = 0 ; i < bcount ; i++)
a [i] = 0xFF ;
if ((count & 7) != 0)
a [bcount++] = 0xFF & (0xFF00 >> (count & 7)) ;
for (byte i = bcount ; i < bits ; i++)
a [i] = 0x00 ;
}
void check_same (byte * a, byte * b, int bits)
{
bits >>= 3 ;
for (byte i = 0 ; i < bits ; i++)
if (a[i] != b[i])
{
printf ("Failure plain != check\n") ;
return ;
}
}
char hex[] = "0123456789abcdef" ;
void print_value (const char * str, byte * a, int bits)
{
printf ("%s",str) ;
bits >>= 3 ;
for (int i = 0 ; i < bits ; i++)
{
byte b = a[i] ;
printf("%c%c",hex[b >> 4],hex [b & 15]);
//Serial.print (hex [b >> 4]) ;
//Serial.print (hex [b & 15]) ;
}
printf("\n") ;
}
byte monteplain [] =
{ 0xb9, 0x14, 0x5a, 0x76, 0x8b, 0x7d, 0xc4, 0x89,
0xa0, 0x96, 0xb5, 0x46, 0xf4, 0x3b, 0x23, 0x1f } ;
byte montekey [] =
{ 0x13, 0x9a, 0x35, 0x42, 0x2f, 0x1d, 0x61, 0xde,
0x3c, 0x91, 0x78, 0x7f, 0xe0, 0x50, 0x7a, 0xfd } ;
void monte_carlo (int bits)
{
printf ("\nMonte Carlo %i bits\n",bits);
byte succ;
for (int i = 0 ; i < 16 ; i++)
{
plain [i] = monteplain [i] ;
key [i] = montekey [i] ;
}
for (int i = 0 ; i < 100 ; i++)
{
printf ("COUNT = %i\n",i);
print_value ("KEY = ", key, bits) ;
print_value ("PLAINTEXT = ", plain, 128) ;
succ = aes.set_key (key, bits) ;
for (int j = 0 ; j < 1000 ; j++)
{
succ = aes.encrypt (plain, cipher) ;
aes.copy_n_bytes (plain, cipher, 16) ;
}
print_value ("CIPHERTEXT = ", cipher, 128) ;
printf("\n");
if (bits == 128)
{
for (byte k = 0 ; k < 16 ; k++)
key [k] ^= cipher [k] ;
}
else if (bits == 192)
{
}
else
{
}
}
}
@@ -0,0 +1,7 @@
AES KEYWORD1
set_key KEYWORD2
clean KEYWORD2
encrypt KEYWORD2
decrypt KEYWORD2
cbc_encrypt KEYWORD2
cbc_decrypt KEYWORD2
+59
View File
@@ -0,0 +1,59 @@
/*
Copyright (C) 2011 J. Coliz <maniacbug@ymail.com>
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.
*/
/* Galileo support from spaniakos <spaniakos@gmail.com> */
/**
* @file printf.h
*
* Setup necessary to direct stdout to the Arduino Serial library, which
* enables 'printf'
*/
#ifndef __PRINTF_H__
#define __PRINTF_H__
#if defined (ARDUINO) && !defined (__arm__) && !defined(__ARDUINO_X86__)
int serial_putc( char c, FILE * )
{
Serial.write( c );
return c;
}
void printf_begin(void)
{
fdevopen( &serial_putc, 0 );
}
#elif defined (__arm__)
void printf_begin(void){}
#elif defined(__ARDUINO_X86__)
int serial_putc( char c, FILE * )
{
Serial.write( c );
return c;
}
void printf_begin(void)
{
//JESUS - For reddirect stdout to /dev/ttyGS0 (Serial Monitor port)
stdout = freopen("/dev/ttyGS0","w",stdout);
delay(500);
printf("redirecting to Serial...");
//JESUS -----------------------------------------------------------
}
#else
#error This example is only for use on Arduino.
#endif // ARDUINO
#endif // __PRINTF_H__
@@ -28,9 +28,9 @@
/** DigitalPin version YYYYMMDD */
#define DIGITAL_IO_VERSION 20140217
//------------------------------------------------------------------------------
#include <DigitalPin.h>
#include <I2cConstants.h>
#include <PinIO.h>
#include <SoftI2cMaster.h>
#include <SoftSPI.h>
#endif // DigitalIO_h
#include "DigitalPin.h"
#include "I2cConstants.h"
#include "PinIO.h"
#include "SoftI2cMaster.h"
#include "SoftSPI.h"
#endif // DigitalIO_h
@@ -25,7 +25,7 @@
* @details Two Wire Interface library.
* @{
*/
#include <PinIO.h>
#include "PinIO.h"
#include <util/atomic.h>
#include <Arduino.h>
//==============================================================================
@@ -65,4 +65,4 @@ void PinIO::config(bool mode, bool data) {
writeI(data);
}
}
/** @} */
/** @} */
@@ -25,7 +25,7 @@
* @details Software Two Wire Interface library.
* @{
*/
#include <SoftI2cMaster.h>
#include "SoftI2cMaster.h"
//------------------------------------------------------------------------------
/**
* Start an I2C transfer with possible continuation.
@@ -33,8 +33,8 @@
#include <Arduino.h>
#endif // ARDUINO
#include <util/delay_basic.h>
#include <DigitalPin.h>
#include <I2cConstants.h>
#include "DigitalPin.h"
#include "I2cConstants.h"
//------------------------------------------------------------------------------
// State codes.
@@ -28,7 +28,7 @@
#ifndef SoftSPI_h
#define SoftSPI_h
#include <DigitalPin.h>
#include "DigitalPin.h"
//------------------------------------------------------------------------------
/** Nop for timing. */
#define nop asm volatile ("nop\n\t")
@@ -11,6 +11,7 @@
#include "RF24.h"
/****************************************************************************/
#ifdef MY_DEBUG_VERBOSE
static void print_hex( uint8_t v, const bool prefix0x = false )
{
if (prefix0x)
@@ -19,6 +20,7 @@ static void print_hex( uint8_t v, const bool prefix0x = false )
Serial.print(v >> 4, HEX);
Serial.print(v & 0x0F, HEX);
}
#endif
void RF24::csn(bool mode)
{
@@ -27,10 +29,10 @@ void RF24::csn(bool mode)
// divider of 4 is the minimum we want.
// CLK:BUS 8Mhz:2Mhz, 16Mhz:4Mhz, or 20Mhz:5Mhz
#ifdef ARDUINO
#if ( !defined(RF24_TINY) && !defined (__arm__) && !defined (SOFTSPI)) || defined (CORE_TEENSY)
_SPI.setBitOrder(MSBFIRST);
_SPI.setDataMode(SPI_MODE0);
_SPI.setClockDivider(SPI_CLOCK_DIV2);
#if ( !defined(RF24_TINY) && !defined (__arm__) && !defined (MY_SOFTSPI)) || defined (CORE_TEENSY)
//_SPI.setBitOrder(MSBFIRST);
//_SPI.setDataMode(SPI_MODE0);
//_SPI.setClockDivider(SPI_CLOCK_DIV2);
#endif
#endif
@@ -140,7 +142,7 @@ uint8_t RF24::write_register(uint8_t reg, uint8_t value)
{
uint8_t status;
#ifdef DEBUG
#ifdef MY_DEBUG_VERBOSE
Serial.print(F("write_register(")); print_hex(reg, true); Serial.print(F(",")); print_hex(value, true); Serial.println(F(")"));
#endif
@@ -296,7 +298,7 @@ uint8_t RF24::get_status(void)
/****************************************************************************/
void RF24::print_feature(void)
{
#ifdef DEBUG
#ifdef MY_DEBUG_VERBOSE
Serial.print(F("FEATURE="));
print_hex(read_register(FEATURE), true);
Serial.println(F(""));
@@ -463,7 +465,7 @@ void RF24::printDetails(void)
Serial.print(F("EN_RXADDR\t")); print_byte_register(EN_RXADDR);
Serial.print(F("RF_CH\t\t")); print_byte_register(RF_CH);
Serial.print(F("RF_SETUP\t")); print_byte_register(RF_SETUP);
Serial.print(F("CONFIG\t\t")); print_byte_register(CONFIG);
Serial.print(F("CONFIG\t\t")); print_byte_register(NRF_CONFIG);
Serial.print(F("DYNPD/FEATURE\t")); print_byte_register(DYNPD,2);
Serial.print(F("Data Rate\t")); Serial.println(PGM_READ_PTR(&rf24_datarate_e_str_P[getDataRate()]));
@@ -545,7 +547,7 @@ void RF24::begin(void)
// Enable PTX, do not write CE high so radio will remain in standby I mode ( 130us max to transition to RX or TX instead of 1500us from powerUp )
// PTX should use only 22uA of power
write_register(CONFIG, ( read_register(CONFIG) ) & ~_BV(PRIM_RX) );
write_register(NRF_CONFIG, ( read_register(NRF_CONFIG) ) & ~_BV(PRIM_RX) );
// Dump register values
printDetails();
@@ -558,7 +560,7 @@ void RF24::startListening(void)
#if !defined (RF24_TINY)
powerUp();
#endif
write_register(CONFIG, read_register(CONFIG) | _BV(PRIM_RX));
write_register(NRF_CONFIG, read_register(NRF_CONFIG) | _BV(PRIM_RX));
write_register(RF24_STATUS, _BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) );
// Restore the pipe0 adddress, if exists
@@ -609,7 +611,7 @@ void RF24::stopListening(void)
}
//flush_rx();
write_register(CONFIG, ( read_register(CONFIG) ) & ~_BV(PRIM_RX) );
write_register(NRF_CONFIG, ( read_register(NRF_CONFIG) ) & ~_BV(PRIM_RX) );
#if defined (RF24_TINY)
// for 3 pins solution TX mode is only left with additonal powerDown/powerUp cycle
@@ -629,7 +631,7 @@ void RF24::stopListening(void)
void RF24::powerDown(void)
{
ce(LOW); // Guarantee CE is low on powerDown
write_register(CONFIG,read_register(CONFIG) & ~_BV(PWR_UP));
write_register(NRF_CONFIG,read_register(NRF_CONFIG) & ~_BV(PWR_UP));
}
/****************************************************************************/
@@ -637,11 +639,11 @@ void RF24::powerDown(void)
//Power up now. Radio will not power down unless instructed by MCU for config changes etc.
void RF24::powerUp(void)
{
uint8_t cfg = read_register(CONFIG);
uint8_t cfg = read_register(NRF_CONFIG);
// if not powered up then power up and wait for the radio to initialize
if (!(cfg & _BV(PWR_UP))){
write_register(CONFIG,read_register(CONFIG) | _BV(PWR_UP));
write_register(NRF_CONFIG,read_register(NRF_CONFIG) | _BV(PWR_UP));
// For nRF24L01+ to go from power down mode to TX or RX mode it must first pass through stand-by mode.
// There must be a delay of Tpd2stby (see Table 16.) after the nRF24L01+ leaves power down mode before
@@ -654,8 +656,8 @@ void RF24::powerUp(void)
/******************************************************************/
#if defined (FAILURE_HANDLING)
void RF24::errNotify(){
#ifdef DEBUG
Serial.println(F("HARDWARE FAIL"));
#ifdef MY_DEBUG
Serial.println(F("RF24 HARDWARE FAIL"));
#endif
failureDetected = 1;
}
@@ -680,6 +682,9 @@ bool RF24::write( const void* buf, uint8_t len, const bool multicast )
return 0;
}
#endif
#if defined(ARDUINO_ARCH_ESP8266)
yield();
#endif
}
ce(LOW);
@@ -874,7 +879,7 @@ bool RF24::txStandBy(uint32_t timeout){
void RF24::maskIRQ(bool tx, bool fail, bool rx){
write_register(CONFIG, ( read_register(CONFIG) ) | fail << MASK_MAX_RT | tx << MASK_TX_DS | rx << MASK_RX_DR );
write_register(NRF_CONFIG, ( read_register(NRF_CONFIG) ) | fail << MASK_MAX_RT | tx << MASK_TX_DS | rx << MASK_RX_DR );
}
/****************************************************************************/
@@ -1307,7 +1312,7 @@ rf24_datarate_e RF24::getDataRate( void )
void RF24::setCRCLength(rf24_crclength_e length)
{
uint8_t config = read_register(CONFIG) & ~( _BV(CRCO) | _BV(EN_CRC)) ;
uint8_t config = read_register(NRF_CONFIG) & ~( _BV(CRCO) | _BV(EN_CRC)) ;
// switch uses RAM (evil!)
if ( length == RF24_CRC_DISABLED )
@@ -1323,7 +1328,7 @@ void RF24::setCRCLength(rf24_crclength_e length)
config |= _BV(EN_CRC);
config |= _BV( CRCO );
}
write_register( CONFIG, config ) ;
write_register( NRF_CONFIG, config ) ;
}
/****************************************************************************/
@@ -1332,7 +1337,7 @@ rf24_crclength_e RF24::getCRCLength(void)
{
rf24_crclength_e result = RF24_CRC_DISABLED;
uint8_t config = read_register(CONFIG) & ( _BV(CRCO) | _BV(EN_CRC)) ;
uint8_t config = read_register(NRF_CONFIG) & ( _BV(CRCO) | _BV(EN_CRC)) ;
uint8_t AA = read_register(EN_AA);
if ( config & _BV(EN_CRC ) || AA)
@@ -1350,8 +1355,8 @@ rf24_crclength_e RF24::getCRCLength(void)
void RF24::disableCRC( void )
{
uint8_t disable = read_register(CONFIG) & ~_BV(EN_CRC) ;
write_register( CONFIG, disable ) ;
uint8_t disable = read_register(NRF_CONFIG) & ~_BV(EN_CRC) ;
write_register( NRF_CONFIG, disable ) ;
}
/****************************************************************************/
@@ -47,8 +47,8 @@ typedef enum { RF24_CRC_DISABLED = 0, RF24_CRC_8, RF24_CRC_16 } rf24_crclength_e
class RF24
{
private:
#ifdef SOFTSPI
SoftSPI<SOFT_SPI_MISO_PIN, SOFT_SPI_MOSI_PIN, SOFT_SPI_SCK_PIN, SPI_MODE> spi;
#ifdef MY_SOFTSPI
SoftSPI<MY_SOFT_SPI_MISO_PIN, MY_SOFT_SPI_MOSI_PIN, MY_SOFT_SPI_SCK_PIN, SPI_MODE> spi;
#elif defined (SPI_UART)
SPIUARTClass uspi;
#endif
@@ -926,7 +926,7 @@ private:
/**
* Print the current feature register value to serial
*
* @warning Does nothing if DEBUG is not defined
* @warning Does nothing if MY_DEBUG is not defined
*/
void print_feature(void);
@@ -11,7 +11,7 @@
#ifndef __RF24_CONFIG_H__
#define __RF24_CONFIG_H__
#include <MyConfig.h>
#include "MyConfig.h"
#if ARDUINO < 100
#include <WProgram.h>
@@ -24,7 +24,7 @@
/*** USER DEFINES: ***/
//#define FAILURE_HANDLING
//#define SPI_UART // Requires library from https://github.com/TMRh20/Sketches/tree/master/SPI_UART
//#define SOFTSPI // Requires library from https://github.com/greiman/DigitalIO
//#define MY_SOFTSPI // Requires library from https://github.com/greiman/DigitalIO
/**********************/
// Define _BV for non-Arduino platforms and for Arduino DUE
@@ -36,14 +36,16 @@
#if defined SPI_UART
#include <SPI_UART.h>
#define _SPI uspi
#elif defined SOFTSPI
// change these pins to your liking
//const uint8_t SOFT_SPI_MISO_PIN = 16; // <-- Moved to MyConfig.h
//const uint8_t SOFT_SPI_MOSI_PIN = 15; // <-- Moved to MyConfig.h
//const uint8_t SOFT_SPI_SCK_PIN = 14; // <-- Moved to MyConfig.h
#elif defined MY_SOFTSPI
#include "drivers/AVR/DigitalIO/DigitalIO.h"
const uint8_t SPI_MODE = 0;
#define _SPI spi
// change these pins to your liking
const uint8_t SOFT_SPI_MISO_PIN = MY_SOFT_SPI_MOSI_PIN;
const uint8_t SOFT_SPI_MOSI_PIN = MY_SOFT_SPI_MISO_PIN;
const uint8_t SOFT_SPI_SCK_PIN = MY_SOFT_SPI_SCK_PIN;
const uint8_t SPI_MODE = 0;
#define _SPI spi
#else
#include <SPI.h>
@@ -24,7 +24,7 @@
*/
/* Memory Map */
#define CONFIG 0x00
#define NRF_CONFIG 0x00
#define EN_AA 0x01
#define EN_RXADDR 0x02
#define SETUP_AW 0x03
@@ -32,7 +32,7 @@
// Please maintain this license information along with authorship
// and copyright notices in any redistribution of this code
#include <SPIFlash.h>
#include "SPIFlash.h"
uint8_t SPIFlash::UNIQUEID[8];
@@ -296,4 +296,4 @@ void SPIFlash::wakeup() {
/// cleanup
void SPIFlash::end() {
SPI.end();
}
}
@@ -120,4 +120,4 @@ protected:
#endif
};
#endif
#endif
@@ -0,0 +1 @@
tests/bin

Some files were not shown because too many files have changed in this diff Show More