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:
+268
-57
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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() {
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
+30
-72
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
+21
-8
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
+85
-86
@@ -27,13 +27,28 @@
|
||||
#if defined(ARDUINO_ARCH_AVR)
|
||||
|
||||
#include "MySigning.h"
|
||||
#include "MySigningAtsha204.h"
|
||||
|
||||
#define SIGNING_IDENTIFIER (1)
|
||||
|
||||
// Uncomment this to get some useful serial debug info (Serial.print and Serial.println expected)
|
||||
//#define DEBUG_SIGNING
|
||||
|
||||
ATSHA204Class atsha204(MY_SIGNING_ATSHA204_PIN);
|
||||
unsigned long _signing_timestamp;
|
||||
bool _signing_verification_ongoing = false;
|
||||
uint8_t _signing_current_nonce[NONCE_NUMIN_SIZE_PASSTHROUGH+SHA204_SERIAL_SZ+1];
|
||||
uint8_t _signing_temp_message[SHA_MSG_SIZE];
|
||||
uint8_t _singning_rx_buffer[SHA204_RSP_SIZE_MAX];
|
||||
uint8_t _singning_tx_buffer[SHA204_CMD_SIZE_MAX];
|
||||
|
||||
#ifdef MY_SIGNING_NODE_WHITELISTING
|
||||
const whitelist_entry_t _signing_whitelist[] = MY_SIGNING_NODE_WHITELISTING;
|
||||
#endif
|
||||
|
||||
void signerCalculateSignature(MyMessage &msg);
|
||||
uint8_t* signerSha256(const uint8_t* data, size_t sz);
|
||||
|
||||
|
||||
#ifdef DEBUG_SIGNING
|
||||
#define DEBUG_SIGNING_PRINTLN(args) Serial.println(args)
|
||||
#else
|
||||
@@ -59,79 +74,63 @@ static void DEBUG_SIGNING_PRINTBUF(const __FlashStringHelper* str, uint8_t* buf,
|
||||
#endif
|
||||
|
||||
|
||||
MySigningAtsha204::MySigningAtsha204(bool requestSignatures,
|
||||
#ifdef MY_SECURE_NODE_WHITELISTING
|
||||
uint8_t nof_whitelist_entries, const whitelist_entry_t* the_whitelist,
|
||||
#endif
|
||||
uint8_t atshaPin)
|
||||
:
|
||||
MySigning(requestSignatures),
|
||||
atsha204(atshaPin),
|
||||
#ifdef MY_SECURE_NODE_WHITELISTING
|
||||
whitelist(the_whitelist),
|
||||
whitlist_sz(nof_whitelist_entries),
|
||||
#endif
|
||||
verification_ongoing(false)
|
||||
{
|
||||
}
|
||||
|
||||
bool MySigningAtsha204::getNonce(MyMessage &msg) {
|
||||
bool signerGetNonce(MyMessage &msg) {
|
||||
// Generate random number for use as nonce
|
||||
// We used a basic whitening technique that takes the first byte of a new random value and builds up a 32-byte random value
|
||||
// This 32-byte random value is then hashed (SHA256) to produce the resulting nonce
|
||||
for (int i = 0; i < 32; i++) {
|
||||
if (atsha204.sha204m_execute(SHA204_RANDOM, RANDOM_NO_SEED_UPDATE, 0, 0, NULL,
|
||||
RANDOM_COUNT, tx_buffer, RANDOM_RSP_SIZE, rx_buffer) != SHA204_SUCCESS) {
|
||||
RANDOM_COUNT, _singning_tx_buffer, RANDOM_RSP_SIZE, _singning_rx_buffer) != SHA204_SUCCESS) {
|
||||
DEBUG_SIGNING_PRINTLN(F("FTGN")); // FTGN = Failed to generate nonce
|
||||
return false;
|
||||
}
|
||||
current_nonce[i] = rx_buffer[SHA204_BUFFER_POS_DATA];
|
||||
_signing_current_nonce[i] = _singning_rx_buffer[SHA204_BUFFER_POS_DATA];
|
||||
}
|
||||
memcpy(current_nonce, sha256(current_nonce, 32), MAX_PAYLOAD);
|
||||
memcpy(_signing_current_nonce, signerSha256(_signing_current_nonce, 32), MAX_PAYLOAD);
|
||||
|
||||
// We set the part of the 32-byte nonce that does not fit into a message to 0xAA
|
||||
memset(¤t_nonce[MAX_PAYLOAD], 0xAA, sizeof(current_nonce)-MAX_PAYLOAD);
|
||||
memset(&_signing_current_nonce[MAX_PAYLOAD], 0xAA, sizeof(_signing_current_nonce)-MAX_PAYLOAD);
|
||||
|
||||
// Replace the first byte in the nonce with our signing identifier
|
||||
current_nonce[0] = SIGNING_IDENTIFIER;
|
||||
_signing_current_nonce[0] = SIGNING_IDENTIFIER;
|
||||
|
||||
// Transfer the first part of the nonce to the message
|
||||
msg.set(current_nonce, MAX_PAYLOAD);
|
||||
verification_ongoing = true;
|
||||
timestamp = millis(); // Set timestamp to determine when to purge nonce
|
||||
msg.set(_signing_current_nonce, MAX_PAYLOAD);
|
||||
_signing_verification_ongoing = true;
|
||||
_signing_timestamp = millis(); // Set timestamp to determine when to purge nonce
|
||||
// Be a little fancy to handle turnover (prolong the time allowed to timeout after turnover)
|
||||
// Note that if message is "too" quick, and arrives before turnover, it will be rejected
|
||||
// but this is consider such a rare case that it is accepted and rejects are 'safe'
|
||||
if (timestamp + MY_VERIFICATION_TIMEOUT_MS < millis()) timestamp = 0;
|
||||
if (_signing_timestamp + MY_VERIFICATION_TIMEOUT_MS < millis()) _signing_timestamp = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MySigningAtsha204::checkTimer() {
|
||||
if (verification_ongoing) {
|
||||
if (millis() < timestamp || millis() > timestamp + MY_VERIFICATION_TIMEOUT_MS) {
|
||||
bool signerCheckTimer() {
|
||||
if (_signing_verification_ongoing) {
|
||||
if (millis() < _signing_timestamp || millis() > _signing_timestamp + MY_VERIFICATION_TIMEOUT_MS) {
|
||||
DEBUG_SIGNING_PRINTLN(F("VT")); // VT = Verification timeout
|
||||
// Purge nonce
|
||||
memset(current_nonce, 0x00, NONCE_NUMIN_SIZE_PASSTHROUGH);
|
||||
verification_ongoing = false;
|
||||
memset(_signing_current_nonce, 0x00, NONCE_NUMIN_SIZE_PASSTHROUGH);
|
||||
_signing_verification_ongoing = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MySigningAtsha204::putNonce(MyMessage &msg) {
|
||||
bool signerPutNonce(MyMessage &msg) {
|
||||
if (((uint8_t*)msg.getCustom())[0] != SIGNING_IDENTIFIER) {
|
||||
DEBUG_SIGNING_PRINTLN(F("ISI")); // ISI = Incorrect signing identifier
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(current_nonce, (uint8_t*)msg.getCustom(), MAX_PAYLOAD);
|
||||
memcpy(_signing_current_nonce, (uint8_t*)msg.getCustom(), MAX_PAYLOAD);
|
||||
// We set the part of the 32-byte nonce that does not fit into a message to 0xAA
|
||||
memset(¤t_nonce[MAX_PAYLOAD], 0xAA, sizeof(current_nonce)-MAX_PAYLOAD);
|
||||
memset(&_signing_current_nonce[MAX_PAYLOAD], 0xAA, sizeof(_signing_current_nonce)-MAX_PAYLOAD);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MySigningAtsha204::signMsg(MyMessage &msg) {
|
||||
bool signerSignMsg(MyMessage &msg) {
|
||||
// If we cannot fit any signature in the message, refuse to sign it
|
||||
if (mGetLength(msg) > MAX_PAYLOAD-2) {
|
||||
DEBUG_SIGNING_PRINTLN(F("MTOL")); // Message too large for signature to fit
|
||||
@@ -140,37 +139,37 @@ bool MySigningAtsha204::signMsg(MyMessage &msg) {
|
||||
|
||||
// Calculate signature of message
|
||||
mSetSigned(msg, 1); // make sure signing flag is set before signature is calculated
|
||||
calculateSignature(msg);
|
||||
signerCalculateSignature(msg);
|
||||
|
||||
#ifdef MY_SECURE_NODE_WHITELISTING
|
||||
#ifdef MY_SIGNING_NODE_WHITELISTING
|
||||
// Salt the signature with the senders nodeId and the unique serial of the ATSHA device
|
||||
memcpy(current_nonce, &rx_buffer[SHA204_BUFFER_POS_DATA], 32); // We can reuse the nonce buffer now since it is no longer needed
|
||||
current_nonce[32] = msg.sender;
|
||||
atsha204.getSerialNumber(¤t_nonce[33]);
|
||||
(void)sha256(current_nonce, 32+1+SHA204_SERIAL_SZ); // we can 'void' sha256 because the hash is already put in the correct place
|
||||
memcpy(_signing_current_nonce, &_singning_rx_buffer[SHA204_BUFFER_POS_DATA], 32); // We can reuse the nonce buffer now since it is no longer needed
|
||||
_signing_current_nonce[32] = msg.sender;
|
||||
atsha204.getSerialNumber(&_signing_current_nonce[33]);
|
||||
(void)signerSha256(_signing_current_nonce, 32+1+SHA204_SERIAL_SZ); // we can 'void' sha256 because the hash is already put in the correct place
|
||||
DEBUG_SIGNING_PRINTLN(F("SWS")); // SWS = Signature whitelist salted
|
||||
#endif
|
||||
|
||||
// Overwrite the first byte in the signature with the signing identifier
|
||||
rx_buffer[SHA204_BUFFER_POS_DATA] = SIGNING_IDENTIFIER;
|
||||
_singning_rx_buffer[SHA204_BUFFER_POS_DATA] = SIGNING_IDENTIFIER;
|
||||
|
||||
// Transfer as much signature data as the remaining space in the message permits
|
||||
memcpy(&msg.data[mGetLength(msg)], &rx_buffer[SHA204_BUFFER_POS_DATA], MAX_PAYLOAD-mGetLength(msg));
|
||||
memcpy(&msg.data[mGetLength(msg)], &_singning_rx_buffer[SHA204_BUFFER_POS_DATA], MAX_PAYLOAD-mGetLength(msg));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MySigningAtsha204::verifyMsg(MyMessage &msg) {
|
||||
if (!verification_ongoing) {
|
||||
bool signerVerifyMsg(MyMessage &msg) {
|
||||
if (!_signing_verification_ongoing) {
|
||||
DEBUG_SIGNING_PRINTLN(F("NAVS")); // NAVS = No active verification session
|
||||
return false;
|
||||
} else {
|
||||
// Make sure we have not expired
|
||||
if (!checkTimer()) {
|
||||
if (!signerCheckTimer()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
verification_ongoing = false;
|
||||
_signing_verification_ongoing = false;
|
||||
|
||||
if (msg.data[mGetLength(msg)] != SIGNING_IDENTIFIER) {
|
||||
DEBUG_SIGNING_PRINTLN(F("ISI")); // ISI = Incorrect signing identifier
|
||||
@@ -178,29 +177,29 @@ bool MySigningAtsha204::verifyMsg(MyMessage &msg) {
|
||||
}
|
||||
|
||||
DEBUG_SIGNING_PRINTBUF(F("SIM:"), (uint8_t*)&msg.data[mGetLength(msg)], MAX_PAYLOAD-mGetLength(msg)); // SIM = Signature in message
|
||||
calculateSignature(msg); // Get signature of message
|
||||
signerCalculateSignature(msg); // Get signature of message
|
||||
|
||||
#ifdef MY_SECURE_NODE_WHITELISTING
|
||||
#ifdef MY_SIGNING_NODE_WHITELISTING
|
||||
// Look up the senders nodeId in our whitelist and salt the signature with that data
|
||||
for (int j=0; j < whitlist_sz; j++) {
|
||||
if (whitelist[j].nodeId == msg.sender) {
|
||||
for (int j=0; j < NUM_OF(_signing_whitelist); j++) {
|
||||
if (_signing_whitelist[j].nodeId == msg.sender) {
|
||||
DEBUG_SIGNING_PRINTLN(F("SIW")); // SIW = Sender found in whitelist
|
||||
memcpy(current_nonce, &rx_buffer[SHA204_BUFFER_POS_DATA], 32); // We can reuse the nonce buffer now since it is no longer needed
|
||||
current_nonce[32] = msg.sender;
|
||||
memcpy(¤t_nonce[33], whitelist[j].serial, SHA204_SERIAL_SZ);
|
||||
(void)sha256(current_nonce, 32+1+SHA204_SERIAL_SZ); // we can 'void' sha256 because the hash is already put in the correct place
|
||||
memcpy(_signing_current_nonce, &_singning_rx_buffer[SHA204_BUFFER_POS_DATA], 32); // We can reuse the nonce buffer now since it is no longer needed
|
||||
_signing_current_nonce[32] = msg.sender;
|
||||
memcpy(&_signing_current_nonce[33], _signing_whitelist[j].serial, SHA204_SERIAL_SZ);
|
||||
(void)signerSha256(_signing_current_nonce, 32+1+SHA204_SERIAL_SZ); // we can 'void' sha256 because the hash is already put in the correct place
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Overwrite the first byte in the signature with the signing identifier
|
||||
rx_buffer[SHA204_BUFFER_POS_DATA] = SIGNING_IDENTIFIER;
|
||||
_singning_rx_buffer[SHA204_BUFFER_POS_DATA] = SIGNING_IDENTIFIER;
|
||||
|
||||
// Compare the caluclated signature with the provided signature
|
||||
if (memcmp(&msg.data[mGetLength(msg)], &rx_buffer[SHA204_BUFFER_POS_DATA], MAX_PAYLOAD-mGetLength(msg))) {
|
||||
DEBUG_SIGNING_PRINTBUF(F("SNOK:"), &rx_buffer[SHA204_BUFFER_POS_DATA], MAX_PAYLOAD-mGetLength(msg)); // SNOK = Signature bad
|
||||
#ifdef MY_SECURE_NODE_WHITELISTING
|
||||
if (memcmp(&msg.data[mGetLength(msg)], &_singning_rx_buffer[SHA204_BUFFER_POS_DATA], MAX_PAYLOAD-mGetLength(msg))) {
|
||||
DEBUG_SIGNING_PRINTBUF(F("SNOK:"), &_singning_rx_buffer[SHA204_BUFFER_POS_DATA], MAX_PAYLOAD-mGetLength(msg)); // SNOK = Signature bad
|
||||
#ifdef MY_SIGNING_NODE_WHITELISTING
|
||||
DEBUG_SIGNING_PRINTLN(F("W?")); // W? = Is the sender whitelisted?
|
||||
#endif
|
||||
return false;
|
||||
@@ -211,60 +210,60 @@ bool MySigningAtsha204::verifyMsg(MyMessage &msg) {
|
||||
}
|
||||
}
|
||||
|
||||
// Helper to calculate signature of msg (returned in rx_buffer[SHA204_BUFFER_POS_DATA])
|
||||
void MySigningAtsha204::calculateSignature(MyMessage &msg) {
|
||||
memset(temp_message, 0, 32);
|
||||
memcpy(temp_message, (uint8_t*)&msg.data[1-HEADER_SIZE], MAX_MESSAGE_LENGTH-1-(MAX_PAYLOAD-mGetLength(msg)));
|
||||
// Helper to calculate signature of msg (returned in _singning_rx_buffer[SHA204_BUFFER_POS_DATA])
|
||||
void signerCalculateSignature(MyMessage &msg) {
|
||||
memset(_signing_temp_message, 0, 32);
|
||||
memcpy(_signing_temp_message, (uint8_t*)&msg.data[1-HEADER_SIZE], MAX_MESSAGE_LENGTH-1-(MAX_PAYLOAD-mGetLength(msg)));
|
||||
|
||||
// Program the data to sign into the ATSHA204
|
||||
DEBUG_SIGNING_PRINTBUF(F("MSG:"), (uint8_t*)&msg.data[1-HEADER_SIZE], MAX_MESSAGE_LENGTH-1-(MAX_PAYLOAD-mGetLength(msg))); // MSG = Message to sign
|
||||
DEBUG_SIGNING_PRINTBUF(F("CNC:"), current_nonce, 32); // CNC = Current nonce
|
||||
(void)atsha204.sha204m_execute(SHA204_WRITE, SHA204_ZONE_DATA | SHA204_ZONE_COUNT_FLAG, 8 << 3, 32, temp_message,
|
||||
WRITE_COUNT_LONG, tx_buffer, WRITE_RSP_SIZE, rx_buffer);
|
||||
DEBUG_SIGNING_PRINTBUF(F("CNC:"), _signing_current_nonce, 32); // CNC = Current nonce
|
||||
(void)atsha204.sha204m_execute(SHA204_WRITE, SHA204_ZONE_DATA | SHA204_ZONE_COUNT_FLAG, 8 << 3, 32, _signing_temp_message,
|
||||
WRITE_COUNT_LONG, _singning_tx_buffer, WRITE_RSP_SIZE, _singning_rx_buffer);
|
||||
|
||||
// Program the nonce to use for the signature (has to be done just before GENDIG due to chip limitations)
|
||||
(void)atsha204.sha204m_execute(SHA204_NONCE, NONCE_MODE_PASSTHROUGH, 0, NONCE_NUMIN_SIZE_PASSTHROUGH, current_nonce,
|
||||
NONCE_COUNT_LONG, tx_buffer, NONCE_RSP_SIZE_SHORT, rx_buffer);
|
||||
(void)atsha204.sha204m_execute(SHA204_NONCE, NONCE_MODE_PASSTHROUGH, 0, NONCE_NUMIN_SIZE_PASSTHROUGH, _signing_current_nonce,
|
||||
NONCE_COUNT_LONG, _singning_tx_buffer, NONCE_RSP_SIZE_SHORT, _singning_rx_buffer);
|
||||
|
||||
// Purge nonce when used
|
||||
memset(current_nonce, 0x00, NONCE_NUMIN_SIZE_PASSTHROUGH);
|
||||
memset(_signing_current_nonce, 0x00, NONCE_NUMIN_SIZE_PASSTHROUGH);
|
||||
|
||||
// Generate digest of data and nonce
|
||||
(void)atsha204.sha204m_execute(SHA204_GENDIG, GENDIG_ZONE_DATA, 8, 0, NULL,
|
||||
GENDIG_COUNT_DATA, tx_buffer, GENDIG_RSP_SIZE, rx_buffer);
|
||||
GENDIG_COUNT_DATA, _singning_tx_buffer, GENDIG_RSP_SIZE, _singning_rx_buffer);
|
||||
|
||||
// Calculate HMAC of message+nonce digest and secret key
|
||||
(void)atsha204.sha204m_execute(SHA204_HMAC, HMAC_MODE_SOURCE_FLAG_MATCH, 0, 0, NULL,
|
||||
HMAC_COUNT, tx_buffer, HMAC_RSP_SIZE, rx_buffer);
|
||||
HMAC_COUNT, _singning_tx_buffer, HMAC_RSP_SIZE, _singning_rx_buffer);
|
||||
|
||||
// Put device back to sleep
|
||||
atsha204.sha204c_sleep();
|
||||
|
||||
DEBUG_SIGNING_PRINTBUF(F("HMAC:"), &rx_buffer[SHA204_BUFFER_POS_DATA], 32);
|
||||
DEBUG_SIGNING_PRINTBUF(F("HMAC:"), &_singning_rx_buffer[SHA204_BUFFER_POS_DATA], 32);
|
||||
}
|
||||
|
||||
// Helper to calculate a generic SHA256 digest of provided buffer (only supports one block)
|
||||
// The pointer to the hash is returned, but the hash is also stored in rx_buffer[SHA204_BUFFER_POS_DATA])
|
||||
uint8_t* MySigningAtsha204::sha256(const uint8_t* data, size_t sz) {
|
||||
// The pointer to the hash is returned, but the hash is also stored in _singning_rx_buffer[SHA204_BUFFER_POS_DATA])
|
||||
uint8_t* signerSha256(const uint8_t* data, size_t sz) {
|
||||
// Initiate SHA256 calculator
|
||||
(void)atsha204.sha204m_execute(SHA204_SHA, SHA_INIT, 0, 0, NULL,
|
||||
SHA_COUNT_SHORT, tx_buffer, SHA_RSP_SIZE_SHORT, rx_buffer);
|
||||
SHA_COUNT_SHORT, _singning_tx_buffer, SHA_RSP_SIZE_SHORT, _singning_rx_buffer);
|
||||
|
||||
// Calculate a hash
|
||||
memset(temp_message, 0x00, SHA_MSG_SIZE);
|
||||
memcpy(temp_message, data, sz);
|
||||
temp_message[sz] = 0x80;
|
||||
memset(_signing_temp_message, 0x00, SHA_MSG_SIZE);
|
||||
memcpy(_signing_temp_message, data, sz);
|
||||
_signing_temp_message[sz] = 0x80;
|
||||
// Write length data to the last bytes
|
||||
temp_message[SHA_MSG_SIZE-2] = (sz >> 5);
|
||||
temp_message[SHA_MSG_SIZE-1] = (sz << 3);
|
||||
DEBUG_SIGNING_PRINTBUF(F("DTH:"), temp_message, SHA_MSG_SIZE); // DTH = Data to hash
|
||||
(void)atsha204.sha204m_execute(SHA204_SHA, SHA_CALC, 0, SHA_MSG_SIZE, temp_message,
|
||||
SHA_COUNT_LONG, tx_buffer, SHA_RSP_SIZE_LONG, rx_buffer);
|
||||
_signing_temp_message[SHA_MSG_SIZE-2] = (sz >> 5);
|
||||
_signing_temp_message[SHA_MSG_SIZE-1] = (sz << 3);
|
||||
DEBUG_SIGNING_PRINTBUF(F("DTH:"), _signing_temp_message, SHA_MSG_SIZE); // DTH = Data to hash
|
||||
(void)atsha204.sha204m_execute(SHA204_SHA, SHA_CALC, 0, SHA_MSG_SIZE, _signing_temp_message,
|
||||
SHA_COUNT_LONG, _singning_tx_buffer, SHA_RSP_SIZE_LONG, _singning_rx_buffer);
|
||||
|
||||
// Put device back to sleep
|
||||
atsha204.sha204c_sleep();
|
||||
|
||||
DEBUG_SIGNING_PRINTBUF(F("SHA:"), &rx_buffer[SHA204_BUFFER_POS_DATA], 32);
|
||||
return &rx_buffer[SHA204_BUFFER_POS_DATA];
|
||||
DEBUG_SIGNING_PRINTBUF(F("SHA:"), &_singning_rx_buffer[SHA204_BUFFER_POS_DATA], 32);
|
||||
return &_singning_rx_buffer[SHA204_BUFFER_POS_DATA];
|
||||
}
|
||||
#endif // #if defined(ARDUINO_ARCH_AVR)
|
||||
+94
-101
@@ -28,13 +28,27 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "MyConfig.h"
|
||||
#include "MySigning.h"
|
||||
#include "MySigningAtsha204Soft.h"
|
||||
#include "utility/sha256.h"
|
||||
#include "drivers/ATSHA204/sha256.h"
|
||||
|
||||
#define SIGNING_IDENTIFIER (1)
|
||||
|
||||
|
||||
Sha256Class _signing_sha256;
|
||||
unsigned long _signing_timestamp;
|
||||
bool _signing_verification_ongoing = false;
|
||||
uint8_t _signing_current_nonce[NONCE_NUMIN_SIZE_PASSTHROUGH];
|
||||
uint8_t _signing_temp_message[32];
|
||||
static uint8_t _signing_hmac_key[32] = { MY_SIGNING_SOFT_HMAC_KEY };
|
||||
uint8_t _signing_hmac[32];
|
||||
|
||||
#ifdef MY_SIGNING_NODE_WHITELISTING
|
||||
const uint8_t _signing_node_serial_info[SHA204_SERIAL_SZ] = {MY_SIGNING_SOFT_SERIAL};
|
||||
const whitelist_entry_t _signing_whitelist[] = MY_SIGNING_NODE_WHITELISTING;
|
||||
#endif
|
||||
|
||||
void signerCalculateSignature(MyMessage &msg);
|
||||
|
||||
// Uncomment this to get some useful serial debug info (Serial.print and Serial.println expected)
|
||||
//#define DEBUG_SIGNING
|
||||
|
||||
@@ -63,81 +77,60 @@ static void DEBUG_SIGNING_PRINTBUF(const __FlashStringHelper* str, uint8_t* buf,
|
||||
#define DEBUG_SIGNING_PRINTBUF(str, buf, sz)
|
||||
#endif
|
||||
|
||||
// Initialize hmacKey from MyConfig.h (codebender didn't like static initialization in constructor)
|
||||
uint8_t MySigningAtsha204Soft::hmacKey[32] = { MY_HMAC_KEY };
|
||||
|
||||
MySigningAtsha204Soft::MySigningAtsha204Soft(bool requestSignatures,
|
||||
#ifdef MY_SECURE_NODE_WHITELISTING
|
||||
uint8_t nof_whitelist_entries, const whitelist_entry_t* the_whitelist,
|
||||
const uint8_t* the_serial,
|
||||
#endif
|
||||
uint8_t randomseedPin)
|
||||
:
|
||||
MySigning(requestSignatures),
|
||||
#ifdef MY_SECURE_NODE_WHITELISTING
|
||||
whitlist_sz(nof_whitelist_entries),
|
||||
whitelist(the_whitelist),
|
||||
node_serial_info(the_serial),
|
||||
#endif
|
||||
Sha256(),
|
||||
verification_ongoing(false),
|
||||
rndPin(randomseedPin)
|
||||
{
|
||||
}
|
||||
|
||||
bool MySigningAtsha204Soft::getNonce(MyMessage &msg) {
|
||||
bool signerGetNonce(MyMessage &msg) {
|
||||
// Set randomseed
|
||||
randomSeed(analogRead(rndPin));
|
||||
randomSeed(analogRead(MY_SIGNING_SOFT_RANDOMSEED_PIN));
|
||||
|
||||
// We used a basic whitening technique that takes the first byte of a new random value and builds up a 32-byte random value
|
||||
// This 32-byte random value is then hashed (SHA256) to produce the resulting nonce
|
||||
Sha256.init();
|
||||
_signing_sha256.init();
|
||||
for (int i = 0; i < 32; i++) {
|
||||
Sha256.write(random(255));
|
||||
_signing_sha256.write(random(255));
|
||||
}
|
||||
memcpy(current_nonce, Sha256.result(), MAX_PAYLOAD);
|
||||
memcpy(_signing_current_nonce, _signing_sha256.result(), MAX_PAYLOAD);
|
||||
|
||||
// We set the part of the 32-byte nonce that does not fit into a message to 0xAA
|
||||
memset(¤t_nonce[MAX_PAYLOAD], 0xAA, sizeof(current_nonce)-MAX_PAYLOAD);
|
||||
memset(&_signing_current_nonce[MAX_PAYLOAD], 0xAA, sizeof(_signing_current_nonce)-MAX_PAYLOAD);
|
||||
|
||||
// Replace the first byte in the nonce with our signing identifier
|
||||
current_nonce[0] = SIGNING_IDENTIFIER;
|
||||
_signing_current_nonce[0] = SIGNING_IDENTIFIER;
|
||||
|
||||
// Transfer the first part of the nonce to the message
|
||||
msg.set(current_nonce, MAX_PAYLOAD);
|
||||
verification_ongoing = true;
|
||||
timestamp = millis(); // Set timestamp to determine when to purge nonce
|
||||
msg.set(_signing_current_nonce, MAX_PAYLOAD);
|
||||
_signing_verification_ongoing = true;
|
||||
_signing_timestamp = millis(); // Set timestamp to determine when to purge nonce
|
||||
// Be a little fancy to handle turnover (prolong the time allowed to timeout after turnover)
|
||||
// Note that if message is "too" quick, and arrives before turnover, it will be rejected
|
||||
// but this is consider such a rare case that it is accepted and rejects are 'safe'
|
||||
if (timestamp + MY_VERIFICATION_TIMEOUT_MS < millis()) timestamp = 0;
|
||||
if (_signing_timestamp + MY_VERIFICATION_TIMEOUT_MS < millis()) _signing_timestamp = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MySigningAtsha204Soft::checkTimer() {
|
||||
if (verification_ongoing) {
|
||||
if (millis() < timestamp || millis() > timestamp + MY_VERIFICATION_TIMEOUT_MS) {
|
||||
bool signerCheckTimer() {
|
||||
if (_signing_verification_ongoing) {
|
||||
if (millis() < _signing_timestamp || millis() > _signing_timestamp + MY_VERIFICATION_TIMEOUT_MS) {
|
||||
DEBUG_SIGNING_PRINTLN(F("VT")); // VT = Verification timeout
|
||||
// Purge nonce
|
||||
memset(current_nonce, 0xAA, 32);
|
||||
verification_ongoing = false;
|
||||
memset(_signing_current_nonce, 0xAA, 32);
|
||||
_signing_verification_ongoing = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MySigningAtsha204Soft::putNonce(MyMessage &msg) {
|
||||
bool signerPutNonce(MyMessage &msg) {
|
||||
if (((uint8_t*)msg.getCustom())[0] != SIGNING_IDENTIFIER) {
|
||||
DEBUG_SIGNING_PRINTLN(F("ISI")); // ISI = Incorrect signing identifier
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(current_nonce, (uint8_t*)msg.getCustom(), MAX_PAYLOAD);
|
||||
memcpy(_signing_current_nonce, (uint8_t*)msg.getCustom(), MAX_PAYLOAD);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MySigningAtsha204Soft::signMsg(MyMessage &msg) {
|
||||
bool signerSignMsg(MyMessage &msg) {
|
||||
// If we cannot fit any signature in the message, refuse to sign it
|
||||
if (mGetLength(msg) > MAX_PAYLOAD-2) {
|
||||
DEBUG_SIGNING_PRINTLN(F("MTOL")); // Message too large for signature to fit
|
||||
@@ -146,38 +139,38 @@ bool MySigningAtsha204Soft::signMsg(MyMessage &msg) {
|
||||
|
||||
// Calculate signature of message
|
||||
mSetSigned(msg, 1); // make sure signing flag is set before signature is calculated
|
||||
calculateSignature(msg);
|
||||
signerCalculateSignature(msg);
|
||||
|
||||
#ifdef MY_SECURE_NODE_WHITELISTING
|
||||
#ifdef MY_SIGNING_NODE_WHITELISTING
|
||||
// Salt the signature with the senders nodeId and the (hopefully) unique serial The Creator has provided
|
||||
Sha256.init();
|
||||
for (int i=0; i<32; i++) Sha256.write(hmac[i]);
|
||||
Sha256.write(msg.sender);
|
||||
for (int i=0; i<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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
+26
-29
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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}
|
||||
@@ -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
|
||||
@@ -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__
|
||||
+6
-6
@@ -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);
|
||||
}
|
||||
}
|
||||
/** @} */
|
||||
/** @} */
|
||||
+1
-1
@@ -25,7 +25,7 @@
|
||||
* @details Software Two Wire Interface library.
|
||||
* @{
|
||||
*/
|
||||
#include <SoftI2cMaster.h>
|
||||
#include "SoftI2cMaster.h"
|
||||
//------------------------------------------------------------------------------
|
||||
/**
|
||||
* Start an I2C transfer with possible continuation.
|
||||
+2
-2
@@ -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
-9
@@ -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>
|
||||
+1
-1
@@ -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
|
||||
+2
-2
@@ -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();
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -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
Reference in New Issue
Block a user