Transport update (#722)
* Major transport rework * Adjust signal report calculations * Minor driver optimizations * Implement sanity check in legacy RFM69 driver * Additional RFM69 rework * RFM95 encryption * Polishing
This commit is contained in:
@@ -37,7 +37,13 @@
|
||||
#if defined(MY_RADIO_NRF24)
|
||||
#define MY_CAP_RADIO "N"
|
||||
#elif defined(MY_RADIO_RFM69)
|
||||
#if !defined(MY_RFM69_NEW_DRIVER)
|
||||
// old RFM69 driver
|
||||
#define MY_CAP_RADIO "R"
|
||||
#else
|
||||
// new RFM69 driver
|
||||
#define MY_CAP_RADIO "P"
|
||||
#endif
|
||||
#elif defined(MY_RADIO_RFM95)
|
||||
#define MY_CAP_RADIO "L"
|
||||
#elif defined(MY_RS485)
|
||||
@@ -50,6 +56,8 @@
|
||||
#define MY_CAP_TYPE "G"
|
||||
#elif defined(MY_REPEATER_FEATURE)
|
||||
#define MY_CAP_TYPE "R"
|
||||
#elif defined(MY_PASSIVE_NODE)
|
||||
#define MY_CAP_TYPE "P"
|
||||
#else
|
||||
#define MY_CAP_TYPE "N"
|
||||
#endif
|
||||
|
||||
+16
-16
@@ -32,25 +32,25 @@
|
||||
#define MyEepromAddresses_h
|
||||
|
||||
// EEPROM variable sizes, in bytes
|
||||
#define SIZE_NODE_ID (1) //!< Size node ID
|
||||
#define SIZE_PARENT_NODE_ID (1) //!< Size parent node ID
|
||||
#define SIZE_DISTANCE (1) //!< Size GW distance
|
||||
#define SIZE_ROUTES (256) //!< Size routing table
|
||||
#define SIZE_CONTROLLER_CONFIG (24) //!< Size controller config
|
||||
#define SIZE_FIRMWARE_TYPE (2) //!< Size firmware type
|
||||
#define SIZE_FIRMWARE_VERSION (2) //!< Size firmware version
|
||||
#define SIZE_FIRMWARE_BLOCKS (2) //!< Size firmware blocks
|
||||
#define SIZE_FIRMWARE_CRC (2) //!< Size firmware CRC
|
||||
#define SIZE_SIGNING_REQUIREMENT_TABLE (32) //!< Size signing requirement table
|
||||
#define SIZE_WHITELIST_REQUIREMENT_TABLE (32) //!< Size whitelist requirement table
|
||||
#define SIZE_SIGNING_SOFT_HMAC_KEY (32) //!< Size soft signing HMAC key
|
||||
#define SIZE_SIGNING_SOFT_SERIAL (9) //!< Size soft signing serial
|
||||
#define SIZE_RF_ENCRYPTION_AES_KEY (16) //!< Size RF AES encryption key
|
||||
#define SIZE_NODE_LOCK_COUNTER (1) //!< Size node lock counter
|
||||
#define SIZE_NODE_ID (1u) //!< Size node ID
|
||||
#define SIZE_PARENT_NODE_ID (1u) //!< Size parent node ID
|
||||
#define SIZE_DISTANCE (1u) //!< Size GW distance
|
||||
#define SIZE_ROUTES (256u) //!< Size routing table
|
||||
#define SIZE_CONTROLLER_CONFIG (24u) //!< Size controller config
|
||||
#define SIZE_FIRMWARE_TYPE (2u) //!< Size firmware type
|
||||
#define SIZE_FIRMWARE_VERSION (2u) //!< Size firmware version
|
||||
#define SIZE_FIRMWARE_BLOCKS (2u) //!< Size firmware blocks
|
||||
#define SIZE_FIRMWARE_CRC (2u) //!< Size firmware CRC
|
||||
#define SIZE_SIGNING_REQUIREMENT_TABLE (32u) //!< Size signing requirement table
|
||||
#define SIZE_WHITELIST_REQUIREMENT_TABLE (32u) //!< Size whitelist requirement table
|
||||
#define SIZE_SIGNING_SOFT_HMAC_KEY (32u) //!< Size soft signing HMAC key
|
||||
#define SIZE_SIGNING_SOFT_SERIAL (9u) //!< Size soft signing serial
|
||||
#define SIZE_RF_ENCRYPTION_AES_KEY (16u) //!< Size RF AES encryption key
|
||||
#define SIZE_NODE_LOCK_COUNTER (1u) //!< Size node lock counter
|
||||
|
||||
|
||||
/** @brief EEPROM start address */
|
||||
#define EEPROM_START 0
|
||||
#define EEPROM_START (0u)
|
||||
/** @brief Address node ID */
|
||||
#define EEPROM_NODE_ID_ADDRESS EEPROM_START
|
||||
/** @brief Address parent node ID */
|
||||
|
||||
@@ -56,12 +56,12 @@ bool gatewayTransportSend(MyMessage &message)
|
||||
setIndication(INDICATION_GW_TX);
|
||||
char *topic = protocolFormatMQTTTopic(MY_MQTT_PUBLISH_TOPIC_PREFIX, message);
|
||||
debug(PSTR("Sending message on topic: %s\n"), topic);
|
||||
#ifdef MY_MQTT_CLIENT_PUBLISH_RETAIN
|
||||
bool retain = mGetCommand(message) == C_SET ||
|
||||
(mGetCommand(message) == C_INTERNAL && message.type == I_BATTERY_LEVEL);
|
||||
#else
|
||||
#ifdef MY_MQTT_CLIENT_PUBLISH_RETAIN
|
||||
bool retain = mGetCommand(message) == C_SET ||
|
||||
(mGetCommand(message) == C_INTERNAL && message.type == I_BATTERY_LEVEL);
|
||||
#else
|
||||
bool retain = false;
|
||||
#endif
|
||||
#endif
|
||||
return _MQTT_client.publish(topic, message.getString(_convBuffer), retain);
|
||||
}
|
||||
|
||||
|
||||
+1
-3
@@ -89,7 +89,6 @@ int8_t hwSleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mo
|
||||
*/
|
||||
bool hwUniqueID(unique_id_t* uniqueID);
|
||||
|
||||
#if defined(MY_DEBUG) || defined(MY_SPECIAL_DEBUG)
|
||||
/**
|
||||
* CPU voltage
|
||||
* @return CPU voltage in mV
|
||||
@@ -107,9 +106,8 @@ uint16_t hwCPUFrequency();
|
||||
* @return free memory in bytes
|
||||
*/
|
||||
uint16_t hwFreeMem();
|
||||
#endif
|
||||
|
||||
#ifdef MY_DEBUG
|
||||
#if defined(DEBUG_OUTPUT_ENABLED)
|
||||
void hwDebugPrint(const char *fmt, ... );
|
||||
#endif
|
||||
|
||||
|
||||
+1
-1
@@ -279,7 +279,7 @@ void hwDebugPrint(const char *fmt, ... )
|
||||
#else
|
||||
// prepend timestamp
|
||||
MY_SERIALDEVICE.print(hwMillis());
|
||||
MY_SERIALDEVICE.print(" ");
|
||||
MY_SERIALDEVICE.print(F(" "));
|
||||
#endif
|
||||
va_list args;
|
||||
va_start (args, fmt );
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
* CE NA
|
||||
* IRQ PA3 (default)
|
||||
*
|
||||
* Wiring RFM69 radio / SPI1
|
||||
* Wiring RF24 radio / SPI1
|
||||
* --------------------------------------------------
|
||||
* CLK PA5
|
||||
* MISO PA6
|
||||
|
||||
+34
-29
@@ -161,35 +161,40 @@ typedef enum {
|
||||
|
||||
/// @brief Type of internal messages (for internal messages)
|
||||
typedef enum {
|
||||
I_BATTERY_LEVEL = 0, //!< Battery level
|
||||
I_TIME = 1, //!< Time (request/response)
|
||||
I_VERSION = 2, //!< Version
|
||||
I_ID_REQUEST = 3, //!< ID request
|
||||
I_ID_RESPONSE = 4, //!< ID response
|
||||
I_INCLUSION_MODE = 5, //!< Inclusion mode
|
||||
I_CONFIG = 6, //!< Config (request/response)
|
||||
I_FIND_PARENT_REQUEST = 7, //!< Find parent
|
||||
I_FIND_PARENT_RESPONSE = 8, //!< Find parent response
|
||||
I_LOG_MESSAGE = 9, //!< Log message
|
||||
I_CHILDREN = 10, //!< Children
|
||||
I_SKETCH_NAME = 11, //!< Sketch name
|
||||
I_SKETCH_VERSION = 12, //!< Sketch version
|
||||
I_REBOOT = 13, //!< Reboot request
|
||||
I_GATEWAY_READY = 14, //!< Gateway ready
|
||||
I_SIGNING_PRESENTATION = 15, //!< Provides signing related preferences (first byte is preference version)
|
||||
I_NONCE_REQUEST = 16, //!< Request for a nonce
|
||||
I_NONCE_RESPONSE = 17, //!< Payload is nonce data
|
||||
I_HEARTBEAT_REQUEST = 18, //!< Heartbeat request
|
||||
I_PRESENTATION = 19, //!< Presentation message
|
||||
I_DISCOVER_REQUEST = 20, //!< Discover request
|
||||
I_DISCOVER_RESPONSE = 21, //!< Discover response
|
||||
I_HEARTBEAT_RESPONSE = 22, //!< Heartbeat response
|
||||
I_LOCKED = 23, //!< Node is locked (reason in string-payload)
|
||||
I_PING = 24, //!< Ping sent to node, payload incremental hop counter
|
||||
I_PONG = 25, //!< In return to ping, sent back to sender, payload incremental hop counter
|
||||
I_REGISTRATION_REQUEST = 26, //!< Register request to GW
|
||||
I_REGISTRATION_RESPONSE = 27, //!< Register response from GW
|
||||
I_DEBUG = 28 //!< Debug message
|
||||
I_BATTERY_LEVEL = 0, //!< Battery level
|
||||
I_TIME = 1, //!< Time (request/response)
|
||||
I_VERSION = 2, //!< Version
|
||||
I_ID_REQUEST = 3, //!< ID request
|
||||
I_ID_RESPONSE = 4, //!< ID response
|
||||
I_INCLUSION_MODE = 5, //!< Inclusion mode
|
||||
I_CONFIG = 6, //!< Config (request/response)
|
||||
I_FIND_PARENT_REQUEST = 7, //!< Find parent
|
||||
I_FIND_PARENT_RESPONSE = 8, //!< Find parent response
|
||||
I_LOG_MESSAGE = 9, //!< Log message
|
||||
I_CHILDREN = 10, //!< Children
|
||||
I_SKETCH_NAME = 11, //!< Sketch name
|
||||
I_SKETCH_VERSION = 12, //!< Sketch version
|
||||
I_REBOOT = 13, //!< Reboot request
|
||||
I_GATEWAY_READY = 14, //!< Gateway ready
|
||||
I_SIGNING_PRESENTATION = 15, //!< Provides signing related preferences (first byte is preference version)
|
||||
I_NONCE_REQUEST = 16, //!< Request for a nonce
|
||||
I_NONCE_RESPONSE = 17, //!< Payload is nonce data
|
||||
I_HEARTBEAT_REQUEST = 18, //!< Heartbeat request
|
||||
I_PRESENTATION = 19, //!< Presentation message
|
||||
I_DISCOVER_REQUEST = 20, //!< Discover request
|
||||
I_DISCOVER_RESPONSE = 21, //!< Discover response
|
||||
I_HEARTBEAT_RESPONSE = 22, //!< Heartbeat response
|
||||
I_LOCKED = 23, //!< Node is locked (reason in string-payload)
|
||||
I_PING = 24, //!< Ping sent to node, payload incremental hop counter
|
||||
I_PONG = 25, //!< In return to ping, sent back to sender, payload incremental hop counter
|
||||
I_REGISTRATION_REQUEST = 26, //!< Register request to GW
|
||||
I_REGISTRATION_RESPONSE = 27, //!< Register response from GW
|
||||
I_DEBUG = 28, //!< Debug message
|
||||
I_SIGNAL_REPORT_REQUEST = 29, //!< Device signal strength request
|
||||
I_SIGNAL_REPORT_REVERSE = 30, //!< Internal
|
||||
I_SIGNAL_REPORT_RESPONSE = 31, //!< Device signal strength response (RSSI)
|
||||
I_PRE_SLEEP_NOTIFICATION = 32, //!< Message sent before node is going to sleep
|
||||
I_POST_SLEEP_NOTIFICATION = 33 //!< Message sent after node woke up (if enabled)
|
||||
} mysensor_internal;
|
||||
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@
|
||||
#define MY_OTA_BOOTLOADER_MINOR_VERSION (0u) //!< Bootloader version minor
|
||||
#define MY_OTA_BOOTLOADER_VERSION (MY_OTA_BOOTLOADER_MINOR_VERSION * 256 + MY_OTA_BOOTLOADER_MAJOR_VERSION) //!< Bootloader version
|
||||
|
||||
#if defined(MY_DEBUG)
|
||||
#if defined(MY_DEBUG_VERBOSE_OTA_UPDATE)
|
||||
#define OTA_DEBUG(x,...) hwDebugPrint(x, ##__VA_ARGS__) //!< debug
|
||||
#else
|
||||
#define OTA_DEBUG(x,...) //!< debug NULL
|
||||
|
||||
+37
-30
@@ -24,6 +24,14 @@
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
// debug output
|
||||
#if defined(MY_DEBUG_VERBOSE_CORE)
|
||||
#define CORE_DEBUG(x,...) DEBUG_OUTPUT(x, ##__VA_ARGS__) //!< debug
|
||||
#else
|
||||
#define CORE_DEBUG(x,...) //!< debug NULL
|
||||
#endif
|
||||
|
||||
|
||||
// message buffers
|
||||
MyMessage _msg; // Buffer for incoming messages
|
||||
MyMessage _msgTmp; // Buffer for temporary messages (acks and nonces among others)
|
||||
@@ -31,7 +39,7 @@ MyMessage _msgTmp; // Buffer for temporary messages (acks and nonces among othe
|
||||
// core configuration
|
||||
static coreConfig_t _coreConfig;
|
||||
|
||||
#if defined(MY_DEBUG)
|
||||
#if defined(DEBUG_OUTPUT_ENABLED)
|
||||
char _convBuf[MAX_PAYLOAD*2+1];
|
||||
#endif
|
||||
|
||||
@@ -327,6 +335,8 @@ bool sendHeartbeat(const bool ack)
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool present(const uint8_t childSensorId, const uint8_t sensorType, const char *description,
|
||||
const bool ack)
|
||||
{
|
||||
@@ -392,30 +402,21 @@ bool _processInternalMessages(void)
|
||||
if (receiveTime) {
|
||||
receiveTime(_msg.getULong());
|
||||
}
|
||||
} else if (type == I_CHILDREN) {
|
||||
#if defined(MY_REPEATER_FEATURE)
|
||||
} else if (type == I_CHILDREN) {
|
||||
if (_msg.data[0] == 'C') {
|
||||
#if defined(MY_REPEATER_FEATURE) && defined(MY_SENSOR_NETWORK)
|
||||
// Clears child relay data for this node
|
||||
setIndication(INDICATION_CLEAR_ROUTING);
|
||||
transportClearRoutingTable();
|
||||
(void)_sendRoute(build(_msgTmp, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_CHILDREN).set("OK"));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
} else if (type == I_DEBUG) {
|
||||
#if defined(MY_DEBUG) || defined(MY_SPECIAL_DEBUG)
|
||||
#if defined(MY_SPECIAL_DEBUG)
|
||||
const char debug_msg = _msg.data[0];
|
||||
if (debug_msg == 'R') { // routing table
|
||||
#if defined(MY_REPEATER_FEATURE)
|
||||
for (uint16_t cnt = 0; cnt < SIZE_ROUTES; cnt++) {
|
||||
const uint8_t route = transportGetRoute(cnt);
|
||||
if (route != BROADCAST_ADDRESS) {
|
||||
CORE_DEBUG(PSTR("MCO:PIM:ROUTE N=%d,R=%d\n"), cnt, route);
|
||||
uint8_t outBuf[2] = { (uint8_t)cnt,route };
|
||||
(void)_sendRoute(build(_msgTmp, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_DEBUG).set(outBuf,
|
||||
2));
|
||||
wait(200);
|
||||
}
|
||||
}
|
||||
#if defined(MY_REPEATER_FEATURE) && defined(MY_SENSOR_NETWORK)
|
||||
transportReportRoutingTable();
|
||||
#endif
|
||||
} else if (debug_msg == 'V') { // CPU voltage
|
||||
(void)_sendRoute(build(_msgTmp, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL,
|
||||
@@ -428,7 +429,7 @@ bool _processInternalMessages(void)
|
||||
I_DEBUG).set(hwFreeMem()));
|
||||
} else if (debug_msg == 'E') { // clear MySensors eeprom area and reboot
|
||||
(void)_sendRoute(build(_msgTmp, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_DEBUG).set("OK"));
|
||||
for (int i = EEPROM_START; i<EEPROM_LOCAL_CONFIG_ADDRESS; i++) {
|
||||
for (uint16_t i = EEPROM_START; i<EEPROM_LOCAL_CONFIG_ADDRESS; i++) {
|
||||
hwWriteConfig(i, 0xFF);
|
||||
}
|
||||
setIndication(INDICATION_REBOOT);
|
||||
@@ -521,7 +522,7 @@ int8_t _sleep(const uint32_t sleepingMS, const bool smartSleep, const uint8_t in
|
||||
// OTA FW feature: do not sleep if FW update ongoing
|
||||
#if defined(MY_OTA_FIRMWARE_FEATURE)
|
||||
if (isFirmwareUpdateOngoing()) {
|
||||
debug(PSTR("!MCO:SLP:FWUPD\n")); // sleeping not possible, FW update ongoing
|
||||
CORE_DEBUG(PSTR("!MCO:SLP:FWUPD\n")); // sleeping not possible, FW update ongoing
|
||||
wait(sleepingMS);
|
||||
return MY_SLEEP_NOT_POSSIBLE;
|
||||
}
|
||||
@@ -559,18 +560,20 @@ int8_t _sleep(const uint32_t sleepingMS, const bool smartSleep, const uint8_t in
|
||||
return MY_SLEEP_NOT_POSSIBLE;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (smartSleep) {
|
||||
// notify controller about going to sleep
|
||||
(void)sendHeartbeat();
|
||||
// notify controller about going to sleep, payload indicates smartsleep waiting time in MS
|
||||
(void)_sendRoute(build(_msgTmp, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL,
|
||||
I_PRE_SLEEP_NOTIFICATION).set((uint32_t)MY_SMART_SLEEP_WAIT_DURATION_MS));
|
||||
wait(MY_SMART_SLEEP_WAIT_DURATION_MS); // listen for incoming messages
|
||||
}
|
||||
#else
|
||||
(void)smartSleep;
|
||||
#endif
|
||||
|
||||
#if defined(MY_SENSOR_NETWORK)
|
||||
CORE_DEBUG(PSTR("MCO:SLP:TPD\n")); // sleep, power down transport
|
||||
transportPowerDown();
|
||||
transportDisable();
|
||||
#endif
|
||||
setIndication(INDICATION_SLEEP);
|
||||
|
||||
#if defined (MY_DEFAULT_TX_LED_PIN) || defined(MY_DEFAULT_RX_LED_PIN) || defined(MY_DEFAULT_ERR_LED_PIN)
|
||||
// Wait until leds finish their blinking pattern
|
||||
@@ -579,10 +582,7 @@ int8_t _sleep(const uint32_t sleepingMS, const bool smartSleep, const uint8_t in
|
||||
}
|
||||
#endif
|
||||
|
||||
setIndication(INDICATION_SLEEP);
|
||||
|
||||
int8_t result = MY_SLEEP_NOT_POSSIBLE; // default
|
||||
|
||||
if (interrupt1 != INTERRUPT_NOT_DEFINED && interrupt2 != INTERRUPT_NOT_DEFINED) {
|
||||
// both IRQs
|
||||
result = hwSleep(interrupt1, mode1, interrupt2, mode2, sleepingTimeMS);
|
||||
@@ -593,9 +593,16 @@ int8_t _sleep(const uint32_t sleepingMS, const bool smartSleep, const uint8_t in
|
||||
// no IRQ
|
||||
result = hwSleep(sleepingTimeMS);
|
||||
}
|
||||
|
||||
setIndication(INDICATION_WAKEUP);
|
||||
CORE_DEBUG(PSTR("MCO:SLP:WUP=%d\n"), result); // sleep wake-up
|
||||
#if defined(MY_SENSOR_NETWORK)
|
||||
transportReInitialise();
|
||||
#endif
|
||||
if (smartSleep) {
|
||||
// notify controller about waking up, payload indicates sleeping time in MS
|
||||
(void)_sendRoute(build(_msgTmp, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL,
|
||||
I_POST_SLEEP_NOTIFICATION).set(sleepingTimeMS));
|
||||
}
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
@@ -651,8 +658,8 @@ void _nodeLock(const char* str)
|
||||
doYield();
|
||||
(void)_sendRoute(build(_msgTmp, GATEWAY_ADDRESS, NODE_SENSOR_ID,C_INTERNAL, I_LOCKED).set(str));
|
||||
#if defined(MY_SENSOR_NETWORK)
|
||||
transportPowerDown();
|
||||
CORE_DEBUG(PSTR("MCO:NLK:TPD\n")); // power down transport
|
||||
transportSleep();
|
||||
CORE_DEBUG(PSTR("MCO:NLK:TSL\n")); // sleep transport
|
||||
#endif
|
||||
setIndication(INDICATION_SLEEP);
|
||||
(void)hwSleep((unsigned long)1000*60*30); // Sleep for 30 min before resending LOCKED message
|
||||
|
||||
+18
-14
@@ -50,14 +50,13 @@
|
||||
* | | MCO | REG | NOT NEEDED | No registration needed (i.e. GW)
|
||||
* |!| MCO | SND | NODE NOT REG | Node is not registered, cannot send message
|
||||
* | | MCO | PIM | NODE REG=%%d | Registration response received, registration status (REG)
|
||||
* | | MCO | PIM | ROUTE N=%%d,R=%%d | Routing table, messages to node (N) are routed via node (R)
|
||||
* | | MCO | SLP | MS=%%lu,SMS=%%d,I1=%%d,M1=%%d,I2=%%d,M2=%%d | Sleep node, time (MS), smartSleep (SMS), Int1/M1, Int2/M2
|
||||
* | | MCO | SLP | TPD | Sleep node, powerdown transport
|
||||
* | | MCO | SLP | WUP=%%d | Node woke-up, reason/IRQ (WUP)
|
||||
* |!| MCO | SLP | FWUPD | Sleeping not possible, FW update ongoing
|
||||
* |!| MCO | SLP | REP | Sleeping not possible, repeater feature enabled
|
||||
* |!| MCO | SLP | TNR | Transport not ready, attempt to reconnect until timeout (MY_SLEEP_TRANSPORT_RECONNECT_TIMEOUT_MS)
|
||||
* | | MCO | NLK | NODE LOCKED. UNLOCK: GND PIN %%d AND RESET | Node locked during booting, see signing chapter for additional information
|
||||
* | | MCO | NLK | TPD | Powerdown transport
|
||||
* | | MCO | NLK | TSL | Set transport to sleep
|
||||
*
|
||||
*
|
||||
* @brief API declaration for MySensorsCore
|
||||
@@ -86,16 +85,6 @@
|
||||
#define VALUE_NOT_DEFINED ((uint8_t)255) //!< Value not defined
|
||||
#define FUNCTION_NOT_SUPPORTED ((uint16_t)0) //!< Function not supported
|
||||
|
||||
|
||||
#ifdef MY_DEBUG
|
||||
#define debug(x,...) hwDebugPrint(x, ##__VA_ARGS__) //!< debug, to be removed (follow-up PR)
|
||||
#define CORE_DEBUG(x,...) hwDebugPrint(x, ##__VA_ARGS__) //!< debug
|
||||
#else
|
||||
#define debug(x,...) //!< debug NULL, to be removed (follow-up PR)
|
||||
#define CORE_DEBUG(x,...) //!< debug NULL
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* @brief Controller configuration
|
||||
*
|
||||
@@ -166,7 +155,6 @@ bool sendSketchInfo(const char *name, const char *version, const bool ack = fals
|
||||
*/
|
||||
bool send(MyMessage &msg, const bool ack = false);
|
||||
|
||||
|
||||
/**
|
||||
* Send this nodes battery level to gateway.
|
||||
* @param level Level between 0-100(%)
|
||||
@@ -183,6 +171,22 @@ bool sendBatteryLevel(const uint8_t level, const bool ack = false);
|
||||
*/
|
||||
bool sendHeartbeat(const bool ack = false);
|
||||
|
||||
/**
|
||||
* Send this nodes signal strength to gateway.
|
||||
* @param level Signal strength can be rssi if the radio provide it, or another kind of calculation
|
||||
* @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 sendSignalStrength(const int16_t level, const bool ack = false);
|
||||
|
||||
/**
|
||||
* Send this nodes TX power level to gateway.
|
||||
* @param level For instance, can be TX power level in dbm
|
||||
* @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 sendTXPowerLevel(const uint8_t level, const bool ack = false);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@
|
||||
#define SIGNING_PRESENTATION_REQUIRE_WHITELISTING (1 << 1)
|
||||
|
||||
#if defined(MY_DEBUG_VERBOSE_SIGNING)
|
||||
#define SIGN_DEBUG(x,...) debug(x, ##__VA_ARGS__)
|
||||
#define SIGN_DEBUG(x,...) hwDebugPrint(x, ##__VA_ARGS__)
|
||||
#else
|
||||
#define SIGN_DEBUG(x,...)
|
||||
#endif
|
||||
|
||||
+199
-21
@@ -19,6 +19,15 @@
|
||||
|
||||
#include "MyTransport.h"
|
||||
|
||||
// debug
|
||||
#if defined(MY_DEBUG_VERBOSE_TRANSPORT)
|
||||
#define TRANSPORT_DEBUG(x,...) DEBUG_OUTPUT(x, ##__VA_ARGS__) //!< debug
|
||||
extern char _convBuf[MAX_PAYLOAD * 2 + 1];
|
||||
#else
|
||||
#define TRANSPORT_DEBUG(x,...) //!< debug NULL
|
||||
#endif
|
||||
|
||||
|
||||
// SM: transitions and update states
|
||||
static transportState_t stInit = { stInitTransition, stInitUpdate };
|
||||
static transportState_t stParent = { stParentTransition, stParentUpdate };
|
||||
@@ -34,7 +43,12 @@ static transportSM_t _transportSM;
|
||||
static transportConfig_t _transportConfig;
|
||||
|
||||
// callback transportOk
|
||||
transportCallback_t transportReady_cb = NULL;
|
||||
transportCallback_t _transportReady_cb = NULL;
|
||||
|
||||
// enhanced ID assignment
|
||||
#if !defined(MY_GATEWAY_FEATURE) && (MY_NODE_ID == AUTO)
|
||||
static uint8_t _transportToken = AUTO;
|
||||
#endif
|
||||
|
||||
// global variables
|
||||
extern MyMessage _msg; // incoming message
|
||||
@@ -90,6 +104,12 @@ void stInitUpdate(void)
|
||||
} else {
|
||||
TRANSPORT_DEBUG(PSTR("TSM:INIT:TSP OK\n"));
|
||||
_transportSM.transportActive = true;
|
||||
#if defined (MY_PASSIVE_NODE)
|
||||
_transportConfig.passiveMode = true;
|
||||
TRANSPORT_DEBUG(PSTR("TSM:INIT:TSP PSM\n")); // transport passive mode
|
||||
#else
|
||||
_transportConfig.passiveMode = false;
|
||||
#endif
|
||||
#if defined(MY_GATEWAY_FEATURE)
|
||||
// Set configuration for gateway
|
||||
TRANSPORT_DEBUG(PSTR("TSM:INIT:GW MODE\n"));
|
||||
@@ -126,7 +146,7 @@ void stParentTransition(void)
|
||||
setIndication(INDICATION_FIND_PARENT);
|
||||
_transportSM.uplinkOk = false;
|
||||
_transportSM.preferredParentFound = false;
|
||||
#if defined(MY_PARENT_NODE_IS_STATIC)
|
||||
#if defined(MY_PARENT_NODE_IS_STATIC) || defined(MY_PASSIVE_NODE)
|
||||
TRANSPORT_DEBUG(PSTR("TSM:FPAR:STATP=%d\n"), (uint8_t)MY_PARENT_NODE_ID); // static parent
|
||||
_transportSM.findingParentNode = false;
|
||||
_transportConfig.distanceGW = 1u; // assumption, CHKUPL:GWDC will update this variable
|
||||
@@ -146,7 +166,7 @@ void stParentTransition(void)
|
||||
// stParentUpdate
|
||||
void stParentUpdate(void)
|
||||
{
|
||||
#if defined(MY_PARENT_NODE_IS_STATIC)
|
||||
#if defined(MY_PARENT_NODE_IS_STATIC) || defined(MY_PASSIVE_NODE)
|
||||
// skipping find parent
|
||||
setIndication(INDICATION_GOT_PARENT);
|
||||
transportSwitchSM(stID);
|
||||
@@ -185,8 +205,17 @@ void stIDTransition(void)
|
||||
if (_transportConfig.nodeId == AUTO) {
|
||||
// send ID request
|
||||
setIndication(INDICATION_REQ_NODEID);
|
||||
#if !defined(MY_GATEWAY_FEATURE) && (MY_NODE_ID == AUTO)
|
||||
_transportToken = (uint8_t)(hwMillis() & 0xFF);
|
||||
if (_transportToken == AUTO) {
|
||||
_transportToken++; // AUTO as token not allowed
|
||||
}
|
||||
const uint8_t sensorID = _transportToken;
|
||||
#else
|
||||
const uint8_t sensorID = NODE_SENSOR_ID;
|
||||
#endif
|
||||
TRANSPORT_DEBUG(PSTR("TSM:ID:REQ\n")); // request node ID
|
||||
(void)transportRouteMessage(build(_msgTmp, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL,
|
||||
(void)transportRouteMessage(build(_msgTmp, GATEWAY_ADDRESS, sensorID, C_INTERNAL,
|
||||
I_ID_REQUEST).set(""));
|
||||
}
|
||||
}
|
||||
@@ -266,8 +295,8 @@ void stReadyTransition(void)
|
||||
_transportSM.failureCounter = 0u; // reset failure counter
|
||||
_transportSM.failedUplinkTransmissions = 0u; // reset failed uplink TX counter
|
||||
// callback
|
||||
if (transportReady_cb) {
|
||||
transportReady_cb();
|
||||
if (_transportReady_cb) {
|
||||
_transportReady_cb();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -314,15 +343,16 @@ void stFailureTransition(void)
|
||||
_transportSM.transportActive = false; // transport inactive
|
||||
setIndication(INDICATION_ERR_INIT_TRANSPORT);
|
||||
#if defined(MY_SENSOR_NETWORK)
|
||||
TRANSPORT_DEBUG(PSTR("TSM:FAIL:PDT\n")); // power down transport, no need until re-init
|
||||
transportPowerDown();
|
||||
TRANSPORT_DEBUG(PSTR("TSM:FAIL:DIS\n")); // disable transport, no need until re-init
|
||||
transportDisable(); // sleep
|
||||
#endif
|
||||
}
|
||||
|
||||
void stFailureUpdate(void)
|
||||
{
|
||||
if (transportTimeInState() > ( isTransportExtendedFailure()? MY_TRANSPORT_TIMEOUT_EXT_FAILURE_STATE:
|
||||
MY_TRANSPORT_TIMEOUT_FAILURE_STATE) ) {
|
||||
if (transportTimeInState() > ( isTransportExtendedFailure()?
|
||||
MY_TRANSPORT_TIMEOUT_EXT_FAILURE_STATE_MS:
|
||||
MY_TRANSPORT_TIMEOUT_FAILURE_STATE_MS) ) {
|
||||
TRANSPORT_DEBUG(PSTR("TSM:FAIL:RE-INIT\n")); // attempt to re-initialise transport
|
||||
transportSwitchSM(stInit);
|
||||
}
|
||||
@@ -389,6 +419,29 @@ void transportInitialise(void)
|
||||
transportSwitchSM(stInit);
|
||||
}
|
||||
|
||||
void transportDisable(void)
|
||||
{
|
||||
if (RADIO_CAN_POWER_OFF == true) {
|
||||
TRANSPORT_DEBUG(PSTR("TSF:TDI:TPD\n")); // power down transport
|
||||
transportPowerDown();
|
||||
} else {
|
||||
TRANSPORT_DEBUG(PSTR("TSF:TDI:TSL\n")); // send transport to sleep
|
||||
transportSleep();
|
||||
}
|
||||
}
|
||||
void transportReInitialise(void)
|
||||
{
|
||||
if (RADIO_CAN_POWER_OFF == true) {
|
||||
TRANSPORT_DEBUG(PSTR("TSF:TRI:TPU\n")); // transport power up
|
||||
transportPowerUp();
|
||||
transportSetAddress(_transportConfig.nodeId);
|
||||
} else {
|
||||
TRANSPORT_DEBUG(PSTR("TSF:TRI:TSB\n")); // transport standby
|
||||
transportStandBy();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool transportWaitUntilReady(const uint32_t waitingMS)
|
||||
{
|
||||
// check if transport ready
|
||||
@@ -495,16 +548,23 @@ bool transportRouteMessage(MyMessage &message)
|
||||
route = _transportConfig.parentNodeId; // not a repeater, all traffic routed via parent
|
||||
#endif
|
||||
}
|
||||
// send message
|
||||
// send message, HAL
|
||||
const bool result = transportSendWrite(route, message);
|
||||
#if !defined(MY_GATEWAY_FEATURE)
|
||||
// update counter
|
||||
if (route == _transportConfig.parentNodeId) {
|
||||
|
||||
if (!result) {
|
||||
setIndication(INDICATION_ERR_TX);
|
||||
_transportSM.failedUplinkTransmissions++;
|
||||
} else {
|
||||
_transportSM.failedUplinkTransmissions = 0u;
|
||||
// update uplink quality monitor
|
||||
const int16_t signalStrengthRSSI = transportGetSignalReport(SR_TX_RSSI);
|
||||
_transportSM.uplinkQualityRSSI = static_cast<transportRSSI_t>((1 - UPLINK_QUALITY_WEIGHT) *
|
||||
_transportSM.uplinkQualityRSSI
|
||||
+ (UPLINK_QUALITY_WEIGHT * transportRSSItoInternal(signalStrengthRSSI)));
|
||||
|
||||
}
|
||||
}
|
||||
#else
|
||||
@@ -559,7 +619,7 @@ uint8_t transportPingNode(const uint8_t targetId)
|
||||
// Wait for ping reply or timeout
|
||||
(void)transportWait(2000, C_INTERNAL, I_PONG);
|
||||
}
|
||||
// make sure missing I_PONG msg does not block pinging function by leaving pignActive=true
|
||||
// make sure missing I_PONG msg does not block pinging function by leaving pingActive=true
|
||||
_transportSM.pingActive = false;
|
||||
return _transportSM.pingResponse;
|
||||
} else {
|
||||
@@ -579,13 +639,13 @@ void transportProcessMessage(void)
|
||||
(void)signerCheckTimer();
|
||||
// receive message
|
||||
setIndication(INDICATION_RX);
|
||||
uint8_t payloadLength = transportReceive((uint8_t *)&_msg);
|
||||
uint8_t payloadLength = transportReceive((uint8_t *)
|
||||
&_msg.last); // last is the first byte of the payload buffer
|
||||
// get message length and limit size
|
||||
|
||||
const uint8_t msgLength = min(mGetLength(_msg), (uint8_t)MAX_PAYLOAD);
|
||||
// calculate expected length
|
||||
const uint8_t expectedMessageLength = HEADER_SIZE + (mGetSigned(_msg) ? MAX_PAYLOAD : msgLength);
|
||||
#if defined(MY_RF24_ENABLE_ENCRYPTION)
|
||||
#if defined(MY_RF24_ENABLE_ENCRYPTION) || defined(MY_RFM95_ENABLE_ENCRYPTION)
|
||||
// payload length = a multiple of blocksize length for decrypted messages, i.e. cannot be used for payload length check
|
||||
payloadLength = expectedMessageLength;
|
||||
#endif
|
||||
@@ -670,7 +730,11 @@ void transportProcessMessage(void)
|
||||
if (type == I_ID_RESPONSE) {
|
||||
#if (MY_NODE_ID == AUTO)
|
||||
// only active if node ID dynamic
|
||||
(void)transportAssignNodeID(_msg.getByte());
|
||||
if ((_msg.sensor == _transportToken) || (_msg.sensor == AUTO)) {
|
||||
(void)transportAssignNodeID(_msg.getByte());
|
||||
} else {
|
||||
TRANSPORT_DEBUG(PSTR("!TSF:MSG:ID TK INVALID\n"));
|
||||
}
|
||||
#endif
|
||||
return; // no further processing required
|
||||
}
|
||||
@@ -701,7 +765,7 @@ void transportProcessMessage(void)
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
#endif // !defined(MY_GATEWAY_FEATURE)
|
||||
// general
|
||||
if (type == I_PING) {
|
||||
TRANSPORT_DEBUG(PSTR("TSF:MSG:PINGED,ID=%d,HP=%d\n"), sender, _msg.getByte()); // node pinged
|
||||
@@ -726,6 +790,24 @@ void transportProcessMessage(void)
|
||||
if (_processInternalMessages()) {
|
||||
return; // no further processing required
|
||||
}
|
||||
if (type == I_SIGNAL_REPORT_REVERSE) {
|
||||
if (_msg.data[1] == '!') {
|
||||
const int16_t value = transportSignalReport(_msg.data[0]);
|
||||
(void)_msg.set(value);
|
||||
_msg.type = I_SIGNAL_REPORT_RESPONSE;
|
||||
}
|
||||
}
|
||||
if (type == I_SIGNAL_REPORT_REQUEST) {
|
||||
if (_msg.data[1] != '!') {
|
||||
const int16_t value = transportSignalReport(_msg.data[0]);
|
||||
(void)transportRouteMessage(build(_msgTmp, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL,
|
||||
I_SIGNAL_REPORT_RESPONSE).set(value));
|
||||
} else {
|
||||
(void)transportRouteMessage(build(_msgTmp, _msg.sender, NODE_SENSOR_ID, C_INTERNAL,
|
||||
I_SIGNAL_REPORT_REVERSE).set(_msg.data, 2));
|
||||
}
|
||||
return;
|
||||
}
|
||||
} else if (command == C_STREAM) {
|
||||
#if defined(MY_OTA_FIRMWARE_FEATURE)
|
||||
if(firmwareOTAUpdateProcess()) {
|
||||
@@ -835,6 +917,7 @@ void transportProcessMessage(void)
|
||||
TRANSPORT_DEBUG(PSTR("!TSF:MSG:REL MSG,NREP\n")); // message relaying request, but not a repeater
|
||||
#endif
|
||||
}
|
||||
(void)last; //avoid cppcheck warning
|
||||
}
|
||||
|
||||
void transportInvokeSanityCheck(void)
|
||||
@@ -890,22 +973,27 @@ bool transportSendWrite(const uint8_t to, MyMessage &message)
|
||||
|
||||
// send
|
||||
setIndication(INDICATION_TX);
|
||||
bool result = transportSend(to, &message, min((uint8_t)MAX_MESSAGE_LENGTH, totalMsgLength));
|
||||
bool result = transportSend(to, &message, min((uint8_t)MAX_MESSAGE_LENGTH, totalMsgLength),
|
||||
_transportConfig.passiveMode);
|
||||
// broadcasting (workaround counterfeits)
|
||||
result |= (to == BROADCAST_ADDRESS);
|
||||
|
||||
TRANSPORT_DEBUG(PSTR("%sTSF:MSG:SEND,%d-%d-%d-%d,s=%d,c=%d,t=%d,pt=%d,l=%d,sg=%d,ft=%d,st=%s:%s\n"),
|
||||
(result ? "" : "!"), message.sender, message.last, to, message.destination, message.sensor,
|
||||
(_transportConfig.passiveMode ? "?" : result ? "" : "!"), message.sender, message.last, to,
|
||||
message.destination,
|
||||
message.sensor,
|
||||
mGetCommand(message), message.type,
|
||||
mGetPayloadType(message), mGetLength(message), mGetSigned(message),
|
||||
_transportSM.failedUplinkTransmissions, (result ? "OK" : "NACK"), message.getString(_convBuf));
|
||||
_transportSM.failedUplinkTransmissions,
|
||||
(result ? "OK" : "NACK"),
|
||||
message.getString(_convBuf));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void transportRegisterReadyCallback(transportCallback_t cb)
|
||||
{
|
||||
transportReady_cb = cb;
|
||||
_transportReady_cb = cb;
|
||||
}
|
||||
|
||||
uint8_t transportGetNodeId(void)
|
||||
@@ -966,3 +1054,93 @@ uint8_t transportGetRoute(const uint8_t node)
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
void transportReportRoutingTable(void)
|
||||
{
|
||||
#if defined(MY_REPEATER_FEATURE)
|
||||
for (uint16_t cnt = 0; cnt < SIZE_ROUTES; cnt++) {
|
||||
const uint8_t route = transportGetRoute(cnt);
|
||||
if (route != BROADCAST_ADDRESS) {
|
||||
TRANSPORT_DEBUG(PSTR("TSF:RRT:ROUTE N=%d,R=%d\n"), cnt, route);
|
||||
uint8_t outBuf[2] = { (uint8_t)cnt,route };
|
||||
(void)_sendRoute(build(_msgTmp, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_DEBUG).set(outBuf,
|
||||
2));
|
||||
wait(200);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void transportTogglePassiveMode(const bool OnOff)
|
||||
{
|
||||
#if !defined (MY_PASSIVE_NODE)
|
||||
_transportConfig.passiveMode = OnOff;
|
||||
#else
|
||||
(void)OnOff;
|
||||
#endif
|
||||
}
|
||||
|
||||
int16_t transportGetSignalReport(const signalReport_t signalReport)
|
||||
{
|
||||
int16_t result;
|
||||
switch (signalReport) {
|
||||
case SR_RX_RSSI:
|
||||
result = transportGetReceivingRSSI();
|
||||
break;
|
||||
case SR_TX_RSSI:
|
||||
result = transportGetSendingRSSI();
|
||||
break;
|
||||
case SR_RX_SNR:
|
||||
result = transportGetReceivingSNR();
|
||||
break;
|
||||
case SR_TX_SNR:
|
||||
result = transportGetSendingSNR();
|
||||
break;
|
||||
case SR_TX_POWER_LEVEL:
|
||||
result = transportGetTxPowerLevel();
|
||||
break;
|
||||
case SR_TX_POWER_PERCENT:
|
||||
result = transportGetTxPowerPercent();
|
||||
break;
|
||||
case SR_UPLINK_QUALITY:
|
||||
result = transportInternalToRSSI(_transportSM.uplinkQualityRSSI);
|
||||
break;
|
||||
default:
|
||||
result = 0;
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int16_t transportSignalReport(const char command)
|
||||
{
|
||||
signalReport_t reportCommand;
|
||||
switch (command) {
|
||||
case 'S':
|
||||
// SNR (if available) of incoming message (this)
|
||||
reportCommand = SR_RX_SNR;
|
||||
break;
|
||||
case 'R':
|
||||
// RSSI (if available) of incoming message (this)
|
||||
reportCommand = SR_RX_RSSI;
|
||||
break;
|
||||
case 'P':
|
||||
// TX powerlevel in %
|
||||
reportCommand = SR_TX_POWER_PERCENT;
|
||||
break;
|
||||
case 'T':
|
||||
// TX powerlevel in dBm
|
||||
reportCommand = SR_TX_POWER_LEVEL;
|
||||
break;
|
||||
case 'U':
|
||||
// Uplink quality
|
||||
reportCommand = SR_UPLINK_QUALITY;
|
||||
break;
|
||||
default:
|
||||
reportCommand = SR_NOT_DEFINED;
|
||||
break;
|
||||
}
|
||||
const uint16_t result = transportGetSignalReport(reportCommand);
|
||||
TRANSPORT_DEBUG(PSTR("TSF:SIR:CMD=%d,VAL=%d\n"), reportCommand, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
+141
-125
@@ -35,7 +35,7 @@
|
||||
* - TSM:FPAR from <b>stParent</b> Find parent
|
||||
* - TSM:ID from <b>stID</b> Check/request node ID, if dynamic node ID set
|
||||
* - TSM:UPL from <b>stUplink</b> Verify uplink connection by pinging GW
|
||||
* - TSM:READY from <b>stReady</b> Transport is ready and fully operational
|
||||
* - TSM:READY from <b>stReady</b> Transport is ready and fully operational
|
||||
* - TSM:FAIL from <b>stFailure</b> Failure in transport link or transport HW
|
||||
* - Transport support function (<b>TSF</b>)
|
||||
* - TSF:CKU from @ref transportCheckUplink(), checks connection to GW
|
||||
@@ -49,73 +49,84 @@
|
||||
* - TSF:SAN from @ref transportInvokeSanityCheck(), calls transport-specific sanity check
|
||||
* - TSF:RTE from @ref transportRouteMessage(), sends message
|
||||
* - TSF:SND from @ref transportSendRoute(), sends message if transport is ready (exposed)
|
||||
|
||||
* - TSF:TDI from @ref transportDisable()
|
||||
* - TSF:TRI from @ref transportReInitialise()
|
||||
* - TSF:SIR from @ref transportSignalReport()
|
||||
*
|
||||
* Transport debug log messages:
|
||||
*
|
||||
* |E| SYS | SUB | Message | Comment
|
||||
* |-|------|-----------|-----------------------|---------------------------------------------------------------------
|
||||
* | | TSM | INIT | | <b>Transition to stInit state</b>
|
||||
* | | TSM | INIT | STATID=%%d | Node ID is static
|
||||
* | | TSM | INIT | TSP OK | Transport device configured and fully operational
|
||||
* | | TSM | INIT | GW MODE | Node is set up as GW, thus omitting ID and findParent states
|
||||
* |!| TSM | INIT | TSP FAIL | Transport device initialization failed
|
||||
* | | TSM | FPAR | | <b>Transition to stParent state</b>
|
||||
* | | TSM | FPAR | STATP=%%d | Static parent set, skip finding parent
|
||||
* | | TSM | FPAR | OK | Parent node identified
|
||||
* |!| TSM | FPAR | NO REPLY | No potential parents replied to find parent request
|
||||
* |!| TSM | FPAR | FAIL | Finding parent failed
|
||||
* | | TSM | ID | | <b>Transition to stID state</b>
|
||||
* | | TSM | ID | OK,ID=%%d | Node ID is valid
|
||||
* | | TSM | ID | REQ | Request node ID from controller
|
||||
* |!| TSM | ID | FAIL,ID=%%d | ID verification failed, ID invalid
|
||||
* | | TSM | UPL | | <b>Transition to stUplink state</b>
|
||||
* | | TSM | UPL | OK | Uplink OK, GW returned ping
|
||||
* | | TSF | UPL | DGWC,O=%%d,N=%%d | Uplink check revealed changed network topology, old distance (O), new distance (N)
|
||||
* |!| TSM | UPL | FAIL | Uplink check failed, i.e. GW could not be pinged
|
||||
* | | TSM | READY | SRT | Save routing table
|
||||
* | | TSM | READY | ID=%%d,PAR=%%d,DIS=%%d| <b>Transition to stReady</b> Transport ready, node ID (ID), parent node ID (PAR), distance to GW (DIS)
|
||||
* |!| TSM | READY | UPL FAIL,SNP | Too many failed uplink transmissions, search new parent
|
||||
* |!| TSM | READY | FAIL,STATP | Too many failed uplink transmissions, static parent enforced
|
||||
* | | TSM | FAIL | CNT=%%d | <b>Transition to stFailure state</b>, consecutive failure counter (CNT)
|
||||
* | | TSM | FAIL | PDT | Power-down transport
|
||||
* | | TSM | FAIL | RE-INIT | Attempt to re-initialize transport
|
||||
* | | TSF | CKU | OK | Uplink OK
|
||||
* | | TSF | CKU | OK,FCTRL | Uplink OK, flood control prevents pinging GW in too short intervals
|
||||
* | | TSF | CKU | DGWC,O=%%d,N=%%d | Uplink check revealed changed network topology, old distance (O), new distance (N)
|
||||
* | | TSF | CKU | FAIL | No reply received when checking uplink
|
||||
* | | TSF | SID | OK,ID=%%d | Node ID assigned
|
||||
* |!| TSF | SID | FAIL,ID=%%d | Assigned ID is invalid
|
||||
* | | TSF | PNG | SEND,TO=%%d | Send ping to destination (TO)
|
||||
* | | TSF | WUR | MS=%%lu | Wait until transport ready, timeout (MS)
|
||||
* | | TSF | MSG | ACK REQ | ACK message requested
|
||||
* | | TSF | MSG | ACK | ACK message, do not proceed but forward to callback
|
||||
* | | TSF | MSG | FPAR RES,ID=%%d,D=%%d | Response to find parent received from node (ID) with distance (D) to GW
|
||||
* | | TSF | MSG | FPAR PREF FOUND | Preferred parent found, i.e. parent defined via MY_PARENT_NODE_ID
|
||||
* | | TSF | MSG | FPAR OK,ID=%%d,D=%%d | Find parent response from node (ID) is valid, distance (D) to GW
|
||||
* | | TSF | MSG | FPAR INACTIVE | Find parent response received, but no find parent request active, skip response
|
||||
* | | TSF | MSG | FPAR REQ,ID=%%d | Find parent request from node (ID)
|
||||
* | | TSF | MSG | PINGED,ID=%%d,HP=%%d | Node pinged by node (ID) with (HP) hops
|
||||
* | | TSF | MSG | PONG RECV,HP=%%d | Pinged node replied with (HP) hops
|
||||
* | | TSF | MSG | BC | Broadcast message received
|
||||
* | | TSF | MSG | GWL OK | Link to GW ok
|
||||
* | | TSF | MSG | FWD BC MSG | Controlled broadcast message forwarding
|
||||
* | | TSF | MSG | REL MSG | Relay message
|
||||
* | | TSF | MSG | REL PxNG,HP=%%d | Relay PING/PONG message, increment hop counter (HP)
|
||||
* |!| TSF | MSG | LEN,%%d!=%%d | Invalid message length, (actual!=expected)
|
||||
* |!| TSF | MSG | PVER,%%d!=%%d | Message protocol version mismatch (actual!=expected)
|
||||
* |!| TSF | MSG | SIGN VERIFY FAIL | Signing verification failed
|
||||
* |!| TSF | MSG | REL MSG,NORP | Node received a message for relaying, but node is not a repeater, message skipped
|
||||
* |!| TSF | MSG | SIGN FAIL | Signing message failed
|
||||
* |!| TSF | MSG | GWL FAIL | GW uplink failed
|
||||
* | | TSF | SAN | OK | Sanity check passed
|
||||
* |!| TSF | SAN | FAIL | Sanity check failed, attempt to re-initialize radio
|
||||
* | | TSF | CRT | OK | Clearing routing table successful
|
||||
* | | TSF | LRT | OK | Loading routing table successful
|
||||
* | | TSF | SRT | OK | Saving routing table successful
|
||||
* |!| TSF | RTE | FPAR ACTIVE | Finding parent active, message not sent
|
||||
* |!| TSF | RTE | DST %%d UNKNOWN | Routing for destination (DST) unknown, send message to parent
|
||||
* |!| TSF | SND | TNR | Transport not ready, message cannot be sent
|
||||
* |E| SYS | SUB | Message | Comment
|
||||
* |-|-------|-----------|---------------------------|---------------------------------------------------------------------
|
||||
* | | TSM | INIT | | <b>Transition to stInit state</b>
|
||||
* | | TSM | INIT | STATID=%%d | Node ID is static
|
||||
* | | TSM | INIT | TSP OK | Transport device configured and fully operational
|
||||
* | | TSM | INIT | TSP PSM | Transport passive mode set
|
||||
* | | TSM | INIT | GW MODE | Node is set up as GW, thus omitting ID and findParent states
|
||||
* |!| TSM | INIT | TSP FAIL | Transport device initialization failed
|
||||
* | | TSM | FPAR | | <b>Transition to stParent state</b>
|
||||
* | | TSM | FPAR | STATP=%%d | Static parent set, skip finding parent
|
||||
* | | TSM | FPAR | OK | Parent node identified
|
||||
* |!| TSM | FPAR | NO REPLY | No potential parents replied to find parent request
|
||||
* |!| TSM | FPAR | FAIL | Finding parent failed
|
||||
* | | TSM | ID | | <b>Transition to stID state</b>
|
||||
* | | TSM | ID | OK,ID=%%d | Node ID is valid
|
||||
* | | TSM | ID | REQ | Request node ID from controller
|
||||
* |!| TSM | ID | FAIL,ID=%%d | ID verification failed, ID invalid
|
||||
* | | TSM | UPL | | <b>Transition to stUplink state</b>
|
||||
* | | TSM | UPL | OK | Uplink OK, GW returned ping
|
||||
* | | TSF | UPL | DGWC,O=%%d,N=%%d | Uplink check revealed changed network topology, old distance (O), new distance (N)
|
||||
* |!| TSM | UPL | FAIL | Uplink check failed, i.e. GW could not be pinged
|
||||
* | | TSM | READY | SRT | Save routing table
|
||||
* | | TSM | READY | ID=%%d,PAR=%%d,DIS=%%d | <b>Transition to stReady</b> Transport ready, node ID (ID), parent node ID (PAR), distance to GW (DIS)
|
||||
* |!| TSM | READY | UPL FAIL,SNP | Too many failed uplink transmissions, search new parent
|
||||
* |!| TSM | READY | FAIL,STATP | Too many failed uplink transmissions, static parent enforced
|
||||
* | | TSM | FAIL | CNT=%%d | <b>Transition to stFailure state</b>, consecutive failure counter (CNT)
|
||||
* | | TSM | FAIL | DIS | Disable transport
|
||||
* | | TSM | FAIL | RE-INIT | Attempt to re-initialize transport
|
||||
* | | TSF | CKU | OK | Uplink OK
|
||||
* | | TSF | CKU | OK,FCTRL | Uplink OK, flood control prevents pinging GW in too short intervals
|
||||
* | | TSF | CKU | DGWC,O=%%d,N=%%d | Uplink check revealed changed network topology, old distance (O), new distance (N)
|
||||
* | | TSF | CKU | FAIL | No reply received when checking uplink
|
||||
* | | TSF | SID | OK,ID=%%d | Node ID assigned
|
||||
* |!| TSF | SID | FAIL,ID=%%d | Assigned ID is invalid
|
||||
* | | TSF | PNG | SEND,TO=%%d | Send ping to destination (TO)
|
||||
* | | TSF | WUR | MS=%%lu | Wait until transport ready, timeout (MS)
|
||||
* | | TSF | MSG | ACK REQ | ACK message requested
|
||||
* | | TSF | MSG | ACK | ACK message, do not proceed but forward to callback
|
||||
* | | TSF | MSG | FPAR RES,ID=%%d,D=%%d | Response to find parent received from node (ID) with distance (D) to GW
|
||||
* | | TSF | MSG | FPAR PREF FOUND | Preferred parent found, i.e. parent defined via MY_PARENT_NODE_ID
|
||||
* | | TSF | MSG | FPAR OK,ID=%%d,D=%%d | Find parent response from node (ID) is valid, distance (D) to GW
|
||||
* | | TSF | MSG | FPAR INACTIVE | Find parent response received, but no find parent request active, skip response
|
||||
* | | TSF | MSG | FPAR REQ,ID=%%d | Find parent request from node (ID)
|
||||
* | | TSF | MSG | PINGED,ID=%%d,HP=%%d | Node pinged by node (ID) with (HP) hops
|
||||
* | | TSF | MSG | PONG RECV,HP=%%d | Pinged node replied with (HP) hops
|
||||
* | | TSF | MSG | BC | Broadcast message received
|
||||
* | | TSF | MSG | GWL OK | Link to GW ok
|
||||
* | | TSF | MSG | FWD BC MSG | Controlled broadcast message forwarding
|
||||
* | | TSF | MSG | REL MSG | Relay message
|
||||
* | | TSF | MSG | REL PxNG,HP=%%d | Relay PING/PONG message, increment hop counter (HP)
|
||||
* |!| TSF | MSG | LEN,%%d!=%%d | Invalid message length, (actual!=expected)
|
||||
* |!| TSF | MSG | PVER,%%d!=%%d | Message protocol version mismatch (actual!=expected)
|
||||
* |!| TSF | MSG | SIGN VERIFY FAIL | Signing verification failed
|
||||
* |!| TSF | MSG | REL MSG,NORP | Node received a message for relaying, but node is not a repeater, message skipped
|
||||
* |!| TSF | MSG | SIGN FAIL | Signing message failed
|
||||
* |!| TSF | MSG | GWL FAIL | GW uplink failed
|
||||
* |!| TSF | MSG | ID TK INVALID | Token for ID request invalid
|
||||
* | | TSF | SAN | OK | Sanity check passed
|
||||
* |!| TSF | SAN | FAIL | Sanity check failed, attempt to re-initialize radio
|
||||
* | | TSF | CRT | OK | Clearing routing table successful
|
||||
* | | TSF | LRT | OK | Loading routing table successful
|
||||
* | | TSF | SRT | OK | Saving routing table successful
|
||||
* |!| TSF | RTE | FPAR ACTIVE | Finding parent active, message not sent
|
||||
* |!| TSF | RTE | DST %%d UNKNOWN | Routing for destination (DST) unknown, send message to parent
|
||||
* | | TSF | RRT | ROUTE N=%%d,R=%%d | Routing table, messages to node (N) are routed via node (R)
|
||||
* |!| TSF | SND | TNR | Transport not ready, message cannot be sent
|
||||
* | | TSF | TDI | TSL | Set transport to sleep
|
||||
* | | TSF | TDI | TPD | Power down transport
|
||||
* | | TSF | TRI | TRI | Reinitialise transport
|
||||
* | | TSF | TRI | TSB | Set transport to standby
|
||||
* | | TSF | SIR | CMD=%d,VAL=%d | Get signal report
|
||||
*
|
||||
*
|
||||
* Incoming / outgoing messages:
|
||||
*
|
||||
@@ -125,8 +136,12 @@
|
||||
* - TSF:MSG:READ,sender-last-destination,s=%%d,c=%%d,t=%%d,pt=%%d,l=%%d,sg=%%d:%%s
|
||||
*
|
||||
* Sending a message
|
||||
* - [!]TSF:MSG:SEND,sender-last-next-destination,s=%%d,c=%%d,t=%%d,pt=%%d,l=%%d,sg=%%d,ft=%%d,st=%%s:%%s
|
||||
* - [!/?]TSF:MSG:SEND,sender-last-next-destination,s=%%d,c=%%d,t=%%d,pt=%%d,l=%%d,sg=%%d,ft=%%d,st=%%s:%%s
|
||||
*
|
||||
* Prepended char:
|
||||
* - <b>none</b>=sending OK
|
||||
* - <b>!</b>=error sending
|
||||
* - <b>?</b>=sending status unknown
|
||||
* Message fields:
|
||||
* - <b>s</b>=sensor ID
|
||||
* - <b>c</b>=command
|
||||
@@ -145,29 +160,25 @@
|
||||
#define MyTransport_h
|
||||
|
||||
#include "MySensorsCore.h"
|
||||
#include "hal/transport/MyTransportHAL.h"
|
||||
|
||||
// debug
|
||||
#if defined(MY_DEBUG)
|
||||
#define TRANSPORT_DEBUG(x,...) hwDebugPrint(x, ##__VA_ARGS__) //!< debug
|
||||
extern char _convBuf[MAX_PAYLOAD * 2 + 1];
|
||||
#else
|
||||
#define TRANSPORT_DEBUG(x,...) //!< debug NULL
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef MY_TRANSPORT_MAX_TX_FAILURES
|
||||
#if defined(MY_REPEATER_FEATURE)
|
||||
#define MY_TRANSPORT_MAX_TX_FAILURES (10u) //!< search for a new parent node after this many transmission failures, higher threshold for repeating nodes
|
||||
#else
|
||||
#define MY_TRANSPORT_MAX_TX_FAILURES (5u) //!< search for a new parent node after this many transmission failures, lower threshold for non-repeating nodes
|
||||
#endif
|
||||
|
||||
#define MY_TRANSPORT_MAX_TSM_FAILURES (7u) //!< Max. number of consecutive TSM failure state entries (3bits)
|
||||
|
||||
#ifndef MY_TRANSPORT_TIMEOUT_FAILURE_STATE
|
||||
#define MY_TRANSPORT_TIMEOUT_FAILURE_STATE (10*1000ul) //!< Duration failure state (in ms)
|
||||
#endif
|
||||
#ifndef MY_TRANSPORT_TIMEOUT_EXT_FAILURE_STATE
|
||||
#define MY_TRANSPORT_TIMEOUT_EXT_FAILURE_STATE (60*1000ul) //!< Duration extended failure state (in ms)
|
||||
|
||||
#ifndef MY_TRANSPORT_MAX_TSM_FAILURES
|
||||
#define MY_TRANSPORT_MAX_TSM_FAILURES (7u) //!< Max. number of consecutive TSM failure state entries (3bits)
|
||||
#endif
|
||||
|
||||
#ifndef MY_TRANSPORT_TIMEOUT_FAILURE_STATE_MS
|
||||
#define MY_TRANSPORT_TIMEOUT_FAILURE_STATE_MS (10*1000ul) //!< Duration failure state (in ms)
|
||||
#endif
|
||||
#ifndef MY_TRANSPORT_TIMEOUT_EXT_FAILURE_STATE_MS
|
||||
#define MY_TRANSPORT_TIMEOUT_EXT_FAILURE_STATE_MS (60*1000ul) //!< Duration extended failure state (in ms)
|
||||
#endif
|
||||
#ifndef MY_TRANSPORT_STATE_TIMEOUT_MS
|
||||
#define MY_TRANSPORT_STATE_TIMEOUT_MS (2*1000ul) //!< general state timeout (in ms)
|
||||
@@ -185,6 +196,8 @@ extern char _convBuf[MAX_PAYLOAD * 2 + 1];
|
||||
#define MAX_HOPS (254u) //!< maximal mumber of hops for ping/pong
|
||||
#define INVALID_HOPS (255u) //!< invalid hops
|
||||
#define MAX_SUBSEQ_MSGS (5u) //!< Maximum number of subsequentially processed messages in FIFO (to prevent transport deadlock if HW issue)
|
||||
#define UPLINK_QUALITY_WEIGHT (0.05f) //!< UPLINK_QUALITY_WEIGHT
|
||||
|
||||
|
||||
// parent node check
|
||||
#if defined(MY_PARENT_NODE_IS_STATIC) && !defined(MY_PARENT_NODE_ID)
|
||||
@@ -195,7 +208,7 @@ extern char _convBuf[MAX_PAYLOAD * 2 + 1];
|
||||
#define isValidDistance(_distance) (bool)(_distance!=DISTANCE_INVALID) //!< returns true if distance is valid
|
||||
#define isValidParent(_parent) (bool)(_parent != AUTO) //!< returns true if parent is valid
|
||||
|
||||
// RX queue
|
||||
// RX queue ==> shift to HAL
|
||||
#if defined(MY_RX_MESSAGE_BUFFER_FEATURE)
|
||||
#if defined(MY_RADIO_RFM69)
|
||||
#error Receive message buffering not supported for RFM69!
|
||||
@@ -221,6 +234,8 @@ typedef struct {
|
||||
uint8_t nodeId; //!< Current node id
|
||||
uint8_t parentNodeId; //!< Where this node sends its messages
|
||||
uint8_t distanceGW; //!< This nodes distance to sensor net gateway (number of hops)
|
||||
uint8_t passiveMode : 1; //!< Passive mode
|
||||
uint8_t reserved : 7; //!< Reserved
|
||||
} transportConfig_t;
|
||||
|
||||
/**
|
||||
@@ -232,6 +247,14 @@ typedef struct {
|
||||
void(*Transition)(void); //!< state transition function
|
||||
void(*Update)(void); //!< state update function
|
||||
} transportState_t;
|
||||
/**
|
||||
* @brief
|
||||
*/
|
||||
typedef int16_t transportRSSI_t; //!< Datatype for internal RSSI storage
|
||||
|
||||
// helper macro for conversion
|
||||
#define transportInternalToRSSI(__value) ((int16_t)__value>>4) //!< Convert internal RSSI to RSSI
|
||||
#define transportRSSItoInternal(__value) ((transportRSSI_t)__value<<4) //!< Convert RSSI to internal RSSI
|
||||
|
||||
/**
|
||||
* @brief Status variables and SM state
|
||||
@@ -240,7 +263,7 @@ typedef struct {
|
||||
*/
|
||||
typedef struct {
|
||||
// SM variables
|
||||
transportState_t* currentState; //!< pointer to current fsm state
|
||||
transportState_t* currentState; //!< pointer to current FSM state
|
||||
uint32_t stateEnter; //!< state enter timepoint
|
||||
// general transport variables
|
||||
uint32_t lastUplinkCheck; //!< last uplink check, required to prevent GW flooding
|
||||
@@ -255,8 +278,9 @@ typedef struct {
|
||||
uint8_t failedUplinkTransmissions : 4; //!< counter failed uplink transmissions (max 15)
|
||||
uint8_t failureCounter : 3; //!< counter for TSM failures (max 7)
|
||||
bool msgReceived : 1; //!< flag message received
|
||||
// 8 bits
|
||||
|
||||
uint8_t pingResponse; //!< stores I_PONG hops
|
||||
transportRSSI_t uplinkQualityRSSI; //!< Uplink quality, internal RSSI representation
|
||||
} transportSM_t;
|
||||
|
||||
/**
|
||||
@@ -463,6 +487,10 @@ void transportSetRoute(const uint8_t node, const uint8_t route);
|
||||
*/
|
||||
uint8_t transportGetRoute(const uint8_t node);
|
||||
/**
|
||||
* @brief Reports content of routing table
|
||||
*/
|
||||
void transportReportRoutingTable(void);
|
||||
/**
|
||||
* @brief Get node ID
|
||||
* @return node ID
|
||||
*/
|
||||
@@ -477,49 +505,37 @@ uint8_t transportGetParentNodeId(void);
|
||||
* @return distance (=hops) to GW
|
||||
*/
|
||||
uint8_t transportGetDistanceGW(void);
|
||||
|
||||
// interface functions for radio driver
|
||||
/**
|
||||
* @brief Toggle passive mode, i.e. transport does not wait for ACK
|
||||
* @param OnOff
|
||||
*/
|
||||
void transportTogglePassiveMode(const bool OnOff);
|
||||
/**
|
||||
* @brief Disable transport, if xxx_POWER_PIN is defined, transport is powered down, else send to sleep
|
||||
*/
|
||||
void transportDisable(void);
|
||||
/**
|
||||
* @brief Reinitialise transport. Put transport to standby - If xxx_POWER_PIN set, power up and go to standby
|
||||
*/
|
||||
void transportReInitialise(void);
|
||||
/**
|
||||
* @brief Get transport signal report
|
||||
* @param command
|
||||
* S=SNR (if available) of incoming message (this)
|
||||
* R=RSSI (if available) of incoming message (this)
|
||||
* P=TX powerlevel in %
|
||||
* T=TX powerlevel in dBm
|
||||
* U=Uplink quality
|
||||
* @return report
|
||||
*/
|
||||
int16_t transportSignalReport(const char command);
|
||||
|
||||
/**
|
||||
* @brief Initialize transport HW
|
||||
* @return true if initialization successful
|
||||
* @brief Get transport signal report
|
||||
* @param signalReport
|
||||
* @return report
|
||||
*/
|
||||
bool transportInit(void);
|
||||
/**
|
||||
* @brief Set node address
|
||||
*/
|
||||
void transportSetAddress(const uint8_t address);
|
||||
/**
|
||||
* @brief Retrieve node address
|
||||
*/
|
||||
uint8_t transportGetAddress(void);
|
||||
/**
|
||||
* @brief Send message
|
||||
* @param to recipient
|
||||
* @param data message to be sent
|
||||
* @param len length of message (header + payload)
|
||||
* @return true if message sent successfully
|
||||
*/
|
||||
bool transportSend(const uint8_t to, const void* data, const uint8_t len);
|
||||
/**
|
||||
* @brief Verify if RX FIFO has pending messages
|
||||
* @return true if message available in RX FIFO
|
||||
*/
|
||||
bool transportAvailable(void);
|
||||
/**
|
||||
* @brief Sanity check for transport: is transport still responsive?
|
||||
* @return true if transport HW is ok
|
||||
*/
|
||||
bool transportSanityCheck(void);
|
||||
/**
|
||||
* @brief Receive message from FIFO
|
||||
* @return length of recevied message (header + payload)
|
||||
*/
|
||||
uint8_t transportReceive(void* data);
|
||||
/**
|
||||
* @brief Power down transport HW
|
||||
*/
|
||||
void transportPowerDown(void);
|
||||
int16_t transportGetSignalReport(const signalReport_t signalReport);
|
||||
|
||||
#endif // MyTransport_h
|
||||
/** @}*/
|
||||
|
||||
@@ -1,155 +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-2017 Sensnology AB
|
||||
* Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
|
||||
*
|
||||
* Documentation: http://www.mysensors.org
|
||||
* Support Forum: http://forum.mysensors.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include "MyConfig.h"
|
||||
#include "MyTransport.h"
|
||||
|
||||
#include "drivers/RF24/RF24.h"
|
||||
|
||||
#include "drivers/CircularBuffer/CircularBuffer.h"
|
||||
|
||||
#if defined(MY_RF24_ENABLE_ENCRYPTION)
|
||||
#include "drivers/AES/AES.h"
|
||||
#endif
|
||||
|
||||
#if defined(MY_RX_MESSAGE_BUFFER_FEATURE)
|
||||
typedef struct _transportQueuedMessage {
|
||||
uint8_t m_len; // Length of the data
|
||||
uint8_t m_data[MAX_MESSAGE_LENGTH]; // The raw data
|
||||
} transportQueuedMessage;
|
||||
|
||||
/** Buffer to store queued messages in. */
|
||||
static transportQueuedMessage transportRxQueueStorage[MY_RX_MESSAGE_BUFFER_SIZE];
|
||||
/** Circular buffer, which uses the transportRxQueueStorage and administers stored messages. */
|
||||
static CircularBuffer<transportQueuedMessage> transportRxQueue(transportRxQueueStorage,
|
||||
MY_RX_MESSAGE_BUFFER_SIZE);
|
||||
|
||||
static volatile uint8_t transportLostMessageCount = 0;
|
||||
|
||||
static void transportRxCallback(void)
|
||||
{
|
||||
// Called for each message received by radio, from interrupt context.
|
||||
// This function _must_ call RF24_readMessage() to de-assert interrupt line!
|
||||
if (!transportRxQueue.full()) {
|
||||
transportQueuedMessage* msg = transportRxQueue.getFront();
|
||||
msg->m_len = RF24_readMessage(msg->m_data); // Read payload & clear RX_DR
|
||||
(void)transportRxQueue.pushFront(msg);
|
||||
} else {
|
||||
// Queue is full. Discard message.
|
||||
(void)RF24_readMessage(NULL); // Read payload & clear RX_DR
|
||||
// Keep track of messages lost. Max 255, prevent wrapping.
|
||||
if (transportLostMessageCount < 255) {
|
||||
++transportLostMessageCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(MY_RF24_ENABLE_ENCRYPTION)
|
||||
AES _aes;
|
||||
uint8_t _dataenc[32] = {0};
|
||||
uint8_t _psk[16];
|
||||
#endif
|
||||
|
||||
bool transportInit(void)
|
||||
{
|
||||
#if defined(MY_RF24_ENABLE_ENCRYPTION)
|
||||
hwReadConfigBlock((void*)_psk, (void*)EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, 16);
|
||||
//set up AES-key
|
||||
_aes.set_key(_psk, 16);
|
||||
// Make sure it is purged from memory when set
|
||||
memset(_psk, 0, 16);
|
||||
#endif
|
||||
|
||||
#if defined(MY_RX_MESSAGE_BUFFER_FEATURE)
|
||||
RF24_registerReceiveCallback( transportRxCallback );
|
||||
#endif
|
||||
return RF24_initialize();
|
||||
}
|
||||
|
||||
void transportSetAddress(const uint8_t address)
|
||||
{
|
||||
RF24_setNodeAddress(address);
|
||||
RF24_startListening();
|
||||
}
|
||||
|
||||
uint8_t transportGetAddress(void)
|
||||
{
|
||||
return RF24_getNodeID();
|
||||
}
|
||||
|
||||
bool transportSend(const uint8_t to, const void* data, const uint8_t len)
|
||||
{
|
||||
#if defined(MY_RF24_ENABLE_ENCRYPTION)
|
||||
// copy input data because it is read-only
|
||||
(void)memcpy(_dataenc,data,len);
|
||||
// has to be adjusted, WIP!
|
||||
_aes.set_IV(0);
|
||||
const uint8_t finalLength = len > 16 ? 32 : 16;
|
||||
//encrypt data
|
||||
_aes.cbc_encrypt(_dataenc, _dataenc, finalLength /16);
|
||||
return RF24_sendMessage(to, _dataenc, finalLength);
|
||||
#else
|
||||
return RF24_sendMessage(to, data, len);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool transportAvailable(void)
|
||||
{
|
||||
#if defined(MY_RX_MESSAGE_BUFFER_FEATURE)
|
||||
(void)RF24_isDataAvailable; // Prevent 'defined but not used' warning
|
||||
return !transportRxQueue.empty();
|
||||
#else
|
||||
return RF24_isDataAvailable();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool transportSanityCheck(void)
|
||||
{
|
||||
return RF24_sanityCheck();
|
||||
}
|
||||
|
||||
uint8_t transportReceive(void* data)
|
||||
{
|
||||
uint8_t len = 0;
|
||||
#if defined(MY_RX_MESSAGE_BUFFER_FEATURE)
|
||||
transportQueuedMessage* msg = transportRxQueue.getBack();
|
||||
if (msg) {
|
||||
len = msg->m_len;
|
||||
(void)memcpy(data, msg->m_data, len);
|
||||
(void)transportRxQueue.popBack();
|
||||
}
|
||||
#else
|
||||
len = RF24_readMessage(data);
|
||||
#endif
|
||||
#if defined(MY_RF24_ENABLE_ENCRYPTION)
|
||||
// has to be adjusted, WIP!
|
||||
_aes.set_IV(0);
|
||||
// decrypt data
|
||||
if (_aes.cbc_decrypt((uint8_t*)(data), (uint8_t*)(data), len > 16 ? 2 : 1) != AES_SUCCESS) {
|
||||
len = 0;
|
||||
}
|
||||
#endif
|
||||
return len;
|
||||
}
|
||||
|
||||
void transportPowerDown(void)
|
||||
{
|
||||
RF24_powerDown();
|
||||
}
|
||||
@@ -1,371 +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-2017 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.
|
||||
*
|
||||
* -------------------------------------------------------------------------------
|
||||
*
|
||||
* Copyright (c) 2013, Majenko Technologies and S.J.Hoeksma
|
||||
* Copyright (c) 2015, LeoDesigner
|
||||
* https://github.com/leodesigner/mysensors-serial-transport
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those
|
||||
* of the authors and should not be interpreted as representing official policies,
|
||||
* either expressed or implied, of Majenko Technologies.
|
||||
********************************************************************************/
|
||||
|
||||
// transport(Serial,0,-1); // serial port, node, dePin (-1 disabled)
|
||||
|
||||
// Serial Transport
|
||||
|
||||
|
||||
#include "MyConfig.h"
|
||||
#include "MyTransport.h"
|
||||
#include <stdint.h>
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "MyTransport.h"
|
||||
|
||||
#ifdef __linux__
|
||||
#include "SerialPort.h"
|
||||
#endif
|
||||
|
||||
#if defined(MY_RS485_DE_PIN)
|
||||
#define assertDE() hwDigitalWrite(MY_RS485_DE_PIN, HIGH); delayMicroseconds(5)
|
||||
#define deassertDE() hwDigitalWrite(MY_RS485_DE_PIN, LOW)
|
||||
|
||||
#else
|
||||
#define assertDE()
|
||||
#define deassertDE()
|
||||
#endif
|
||||
|
||||
// We only use SYS_PACK in this application
|
||||
#define ICSC_SYS_PACK 0x58
|
||||
|
||||
// Receiving header information
|
||||
char _header[6];
|
||||
|
||||
// Reception state machine control and storage variables
|
||||
unsigned char _recPhase;
|
||||
unsigned char _recPos;
|
||||
unsigned char _recCommand;
|
||||
unsigned char _recLen;
|
||||
unsigned char _recStation;
|
||||
unsigned char _recSender;
|
||||
unsigned char _recCS;
|
||||
unsigned char _recCalcCS;
|
||||
|
||||
|
||||
#if defined(__linux__)
|
||||
SerialPort _dev = SerialPort(MY_RS485_HWSERIAL);
|
||||
#elif defined(MY_RS485_HWSERIAL)
|
||||
HardwareSerial& _dev = MY_RS485_HWSERIAL;
|
||||
#else
|
||||
AltSoftSerial _dev;
|
||||
#endif
|
||||
|
||||
unsigned char _nodeId;
|
||||
char _data[MY_RS485_MAX_MESSAGE_LENGTH];
|
||||
uint8_t _packet_len;
|
||||
unsigned char _packet_from;
|
||||
bool _packet_received;
|
||||
|
||||
// Packet wrapping characters, defined in standard ASCII table
|
||||
#define SOH 1
|
||||
#define STX 2
|
||||
#define ETX 3
|
||||
#define EOT 4
|
||||
|
||||
|
||||
|
||||
//Reset the state machine and release the data pointer
|
||||
void _serialReset()
|
||||
{
|
||||
_recPhase = 0;
|
||||
_recPos = 0;
|
||||
_recLen = 0;
|
||||
_recCommand = 0;
|
||||
_recCS = 0;
|
||||
_recCalcCS = 0;
|
||||
}
|
||||
|
||||
// This is the main reception state machine. Progress through the states
|
||||
// is keyed on either special control characters, or counted number of bytes
|
||||
// received. If all the data is in the right format, and the calculated
|
||||
// checksum matches the received checksum, AND the destination station is
|
||||
// our station ID, then look for a registered command that matches the
|
||||
// command code. If all the above is true, execute the command's
|
||||
// function.
|
||||
bool _serialProcess()
|
||||
{
|
||||
char inch;
|
||||
unsigned char i;
|
||||
if (!_dev.available()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
while(_dev.available()) {
|
||||
inch = _dev.read();
|
||||
|
||||
switch(_recPhase) {
|
||||
|
||||
// Case 0 looks for the header. Bytes arrive in the serial interface and get
|
||||
// shifted through a header buffer. When the start and end characters in
|
||||
// the buffer match the SOH/STX pair, and the destination station ID matches
|
||||
// our ID, save the header information and progress to the next state.
|
||||
case 0:
|
||||
memcpy(&_header[0],&_header[1],5);
|
||||
_header[5] = inch;
|
||||
if ((_header[0] == SOH) && (_header[5] == STX) && (_header[1] != _header[2])) {
|
||||
_recCalcCS = 0;
|
||||
_recStation = _header[1];
|
||||
_recSender = _header[2];
|
||||
_recCommand = _header[3];
|
||||
_recLen = _header[4];
|
||||
|
||||
for (i=1; i<=4; i++) {
|
||||
_recCalcCS += _header[i];
|
||||
}
|
||||
_recPhase = 1;
|
||||
_recPos = 0;
|
||||
|
||||
//Check if we should process this message
|
||||
//We reject the message if we are the sender
|
||||
//We reject if we are not the receiver and message is not a broadcast
|
||||
if ((_recSender == _nodeId) ||
|
||||
(_recStation != _nodeId &&
|
||||
_recStation != BROADCAST_ADDRESS)) {
|
||||
_serialReset();
|
||||
break;
|
||||
}
|
||||
|
||||
if (_recLen == 0) {
|
||||
_recPhase = 2;
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
// Case 1 receives the data portion of the packet. Read in "_recLen" number
|
||||
// of bytes and store them in the _data array.
|
||||
case 1:
|
||||
_data[_recPos++] = inch;
|
||||
_recCalcCS += inch;
|
||||
if (_recPos == _recLen) {
|
||||
_recPhase = 2;
|
||||
}
|
||||
break;
|
||||
|
||||
// After the data comes a single ETX character. Do we have it? If not,
|
||||
// reset the state machine to default and start looking for a new header.
|
||||
case 2:
|
||||
// Packet properly terminated?
|
||||
if (inch == ETX) {
|
||||
_recPhase = 3;
|
||||
} else {
|
||||
_serialReset();
|
||||
}
|
||||
break;
|
||||
|
||||
// Next comes the checksum. We have already calculated it from the incoming
|
||||
// data, so just store the incoming checksum byte for later.
|
||||
case 3:
|
||||
_recCS = inch;
|
||||
_recPhase = 4;
|
||||
break;
|
||||
|
||||
// The final state - check the last character is EOT and that the checksum matches.
|
||||
// If that test passes, then look for a valid command callback to execute.
|
||||
// Execute it if found.
|
||||
case 4:
|
||||
if (inch == EOT) {
|
||||
if (_recCS == _recCalcCS) {
|
||||
// First, check for system level commands. It is possible
|
||||
// to register your own callback as well for system level
|
||||
// commands which will be called after the system default
|
||||
// hook.
|
||||
|
||||
switch (_recCommand) {
|
||||
case ICSC_SYS_PACK:
|
||||
_packet_from = _recSender;
|
||||
_packet_len = _recLen;
|
||||
_packet_received = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
//Clear the data
|
||||
_serialReset();
|
||||
//Return true, we have processed one command
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool transportSend(const uint8_t to, const void* data, const uint8_t len)
|
||||
{
|
||||
const char *datap = static_cast<char const *>(data);
|
||||
unsigned char i;
|
||||
unsigned char cs = 0;
|
||||
unsigned char del;
|
||||
|
||||
// This is how many times to try and transmit before failing.
|
||||
unsigned char timeout = 10;
|
||||
|
||||
// Let's start out by looking for a collision. If there has been anything seen in
|
||||
// the last millisecond, then wait for a random time and check again.
|
||||
|
||||
while (_serialProcess()) {
|
||||
del = rand() % 20;
|
||||
for (i = 0; i < del; i++) {
|
||||
delay(1);
|
||||
_serialProcess();
|
||||
}
|
||||
timeout--;
|
||||
if (timeout == 0) {
|
||||
// Failed to transmit!!!
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(MY_RS485_DE_PIN)
|
||||
hwDigitalWrite(MY_RS485_DE_PIN, HIGH);
|
||||
delayMicroseconds(5);
|
||||
#endif
|
||||
|
||||
// Start of header by writing multiple SOH
|
||||
for(byte w=0; w<1; w++) {
|
||||
_dev.write(SOH);
|
||||
}
|
||||
_dev.write(to); // Destination address
|
||||
cs += to;
|
||||
_dev.write(_nodeId); // Source address
|
||||
cs += _nodeId;
|
||||
_dev.write(ICSC_SYS_PACK); // Command code
|
||||
cs += ICSC_SYS_PACK;
|
||||
_dev.write(len); // Length of text
|
||||
cs += len;
|
||||
_dev.write(STX); // Start of text
|
||||
for(i=0; i<len; i++) {
|
||||
_dev.write(datap[i]); // Text bytes
|
||||
cs += datap[i];
|
||||
}
|
||||
_dev.write(ETX); // End of text
|
||||
_dev.write(cs);
|
||||
_dev.write(EOT);
|
||||
|
||||
#if defined(MY_RS485_DE_PIN)
|
||||
#ifdef __PIC32MX__
|
||||
// MPIDE has nothing yet for this. It uses the hardware buffer, which
|
||||
// could be up to 8 levels deep. For now, let's just delay for 8
|
||||
// characters worth.
|
||||
delayMicroseconds((F_CPU/9600)+1);
|
||||
#else
|
||||
#if defined(ARDUINO) && ARDUINO >= 100
|
||||
#if ARDUINO >= 104
|
||||
// Arduino 1.0.4 and upwards does it right
|
||||
_dev.flush();
|
||||
#else
|
||||
// Between 1.0.0 and 1.0.3 it almost does it - need to compensate
|
||||
// for the hardware buffer. Delay for 2 bytes worth of transmission.
|
||||
_dev.flush();
|
||||
delayMicroseconds((20000000UL/9600)+1);
|
||||
#endif
|
||||
#elif defined(__linux__)
|
||||
_dev.flush();
|
||||
#endif
|
||||
#endif
|
||||
hwDigitalWrite(MY_RS485_DE_PIN, LOW);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool transportInit(void)
|
||||
{
|
||||
// Reset the state machine
|
||||
_dev.begin(MY_RS485_BAUD_RATE);
|
||||
_serialReset();
|
||||
#if defined(MY_RS485_DE_PIN)
|
||||
hwPinMode(MY_RS485_DE_PIN, OUTPUT);
|
||||
hwDigitalWrite(MY_RS485_DE_PIN, LOW);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
void transportSetAddress(const uint8_t address)
|
||||
{
|
||||
_nodeId = address;
|
||||
}
|
||||
|
||||
uint8_t transportGetAddress(void)
|
||||
{
|
||||
return _nodeId;
|
||||
}
|
||||
|
||||
|
||||
bool transportAvailable(void)
|
||||
{
|
||||
_serialProcess();
|
||||
return _packet_received;
|
||||
}
|
||||
|
||||
bool transportSanityCheck(void)
|
||||
{
|
||||
// not implemented yet
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t transportReceive(void* data)
|
||||
{
|
||||
if (_packet_received) {
|
||||
memcpy(data,_data,_packet_len);
|
||||
_packet_received = false;
|
||||
return _packet_len;
|
||||
} else {
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
|
||||
void transportPowerDown(void)
|
||||
{
|
||||
// Nothing to shut down here
|
||||
}
|
||||
Reference in New Issue
Block a user