From 7856dfb20943aa66fa9159eab5a6b073281dae78 Mon Sep 17 00:00:00 2001 From: Henrik Ekblad Date: Mon, 12 May 2014 23:50:44 +0200 Subject: [PATCH] Compiling. No tests what so ever. --- EthernetGateway/EthernetGateway.ino | 6 +- libraries/MySensors/{Config.h => MyConfig.h} | 6 +- .../MySensors/{Gateway.cpp => MyGateway.cpp} | 77 +-- .../MySensors/{Gateway.h => MyGateway.h} | 0 libraries/MySensors/MyMessage.cpp | 259 +++++++ libraries/MySensors/MyMessage.h | 184 +++++ libraries/MySensors/MySensor.cpp | 650 ++++++++++++++++++ libraries/MySensors/MySensor.h | 237 +++++++ libraries/MySensors/Relay.cpp | 271 -------- libraries/MySensors/Relay.h | 62 -- libraries/MySensors/Sensor.cpp | 555 --------------- libraries/MySensors/Sensor.h | 298 -------- .../examples/RelayActuator/RelayActuator.ino | 45 +- libraries/RF24/RF24.cpp | 3 - 14 files changed, 1396 insertions(+), 1257 deletions(-) rename libraries/MySensors/{Config.h => MyConfig.h} (91%) rename libraries/MySensors/{Gateway.cpp => MyGateway.cpp} (70%) rename libraries/MySensors/{Gateway.h => MyGateway.h} (100%) create mode 100644 libraries/MySensors/MyMessage.cpp create mode 100644 libraries/MySensors/MyMessage.h create mode 100644 libraries/MySensors/MySensor.cpp create mode 100644 libraries/MySensors/MySensor.h delete mode 100644 libraries/MySensors/Relay.cpp delete mode 100644 libraries/MySensors/Relay.h delete mode 100644 libraries/MySensors/Sensor.cpp delete mode 100644 libraries/MySensors/Sensor.h diff --git a/EthernetGateway/EthernetGateway.ino b/EthernetGateway/EthernetGateway.ino index 75fa991..7a4b363 100644 --- a/EthernetGateway/EthernetGateway.ino +++ b/EthernetGateway/EthernetGateway.ino @@ -52,9 +52,9 @@ #include #include #include -#include +#include #include -#include +//#include #include // Use this if you have attached a Ethernet ENC28J60 shields //#include // Use this fo WizNET module and Arduino Ethernet Shield @@ -81,7 +81,7 @@ byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; // DEAD BEEF FEED EthernetServer server = EthernetServer(IP_PORT); // No blink or button functionality. Use the vanilla constructor. -Gateway gw(RADIO_CE_PIN, RADIO_SPI_SS_PIN, INCLUSION_MODE_TIME); +MyGateway gw(RADIO_CE_PIN, RADIO_SPI_SS_PIN, INCLUSION_MODE_TIME); // Uncomment this constructor if you have leds and include button attached to your gateway //Gateway gw(RADIO_CE_PIN, RADIO_SPI_SS_PIN, INCLUSION_MODE_TIME, INCLUSION_MODE_PIN, RADIO_RX_LED_PIN, RADIO_TX_LED_PIN, RADIO_ERROR_LED_PIN); diff --git a/libraries/MySensors/Config.h b/libraries/MySensors/MyConfig.h similarity index 91% rename from libraries/MySensors/Config.h rename to libraries/MySensors/MyConfig.h index 3b3b6e4..5d7956f 100644 --- a/libraries/MySensors/Config.h +++ b/libraries/MySensors/MyConfig.h @@ -1,5 +1,5 @@ -#ifndef Config_h -#define Config_h +#ifndef MyConfig_h +#define MyConfig_h /*** * Configure Sensor Network @@ -12,6 +12,6 @@ /*** * Enable/Disable debug logging */ -#define DEBUG +//#define DEBUG #endif diff --git a/libraries/MySensors/Gateway.cpp b/libraries/MySensors/MyGateway.cpp similarity index 70% rename from libraries/MySensors/Gateway.cpp rename to libraries/MySensors/MyGateway.cpp index 1b59d04..6d8fa89 100644 --- a/libraries/MySensors/Gateway.cpp +++ b/libraries/MySensors/MyGateway.cpp @@ -9,17 +9,15 @@ version 2 as published by the Free Software Foundation. */ -#include "Gateway.h" +#include "MyGateway.h" -Gateway::Gateway(uint8_t _cepin, uint8_t _cspin, uint8_t _inclusion_time) : Relay(_cepin, _cspin) { +MyGateway::MyGateway(uint8_t _cepin, uint8_t _cspin, uint8_t _inclusion_time) : MySensor(_cepin, _cspin) { ledMode = false; - isRelay = true; inclusionTime = _inclusion_time; } -Gateway::Gateway(uint8_t _cepin, uint8_t _cspin, uint8_t _inclusion_time, uint8_t _inclusion_pin, uint8_t _rx, uint8_t _tx, uint8_t _er) : Relay(_cepin, _cspin) { +MyGateway::MyGateway(uint8_t _cepin, uint8_t _cspin, uint8_t _inclusion_time, uint8_t _inclusion_pin, uint8_t _rx, uint8_t _tx, uint8_t _er) : MySensor(_cepin, _cspin) { ledMode = true; - isRelay = true; pinInclusion = _inclusion_pin; inclusionTime = _inclusion_time; pinRx = _rx; @@ -27,8 +25,10 @@ Gateway::Gateway(uint8_t _cepin, uint8_t _cspin, uint8_t _inclusion_time, uint8_ pinEr = _er; } -void Gateway::begin(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e dataRate, void (*inDataCallback)(char *)) { +void MyGateway::begin(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e dataRate, void (*inDataCallback)(char *)) { Serial.begin(BAUD_RATE); + relayMode = true; + setupRelayMode(); if (inDataCallback != NULL) { useWriteCallback = true; @@ -72,25 +72,25 @@ void Gateway::begin(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e data RF24::startListening(); // Send startup log message on serial - serial(PSTR("0;0;%d;%d;Arduino startup complete.\n"), M_INTERNAL, I_LOG_MESSAGE); + serial(PSTR("0;0;%d;%d;Arduino startup complete.\n"), C_INTERNAL, I_LOG_MESSAGE); } -boolean Gateway::isLedMode() { +boolean MyGateway::isLedMode() { return ledMode; } -void Gateway::startInclusionInterrupt() { +void MyGateway::startInclusionInterrupt() { buttonTriggeredInclusion = true; } -void Gateway::checkButtonTriggeredInclusion() { +void MyGateway::checkButtonTriggeredInclusion() { if (buttonTriggeredInclusion) { // Ok, someone pressed the inclusion button on the gateway // start inclusion mode for 1 munute. - serial(PSTR("0;0;%d;%d;Inclusion started by button.\n"), M_INTERNAL, I_LOG_MESSAGE); + serial(PSTR("0;0;%d;%d;Inclusion started by button.\n"), C_INTERNAL, I_LOG_MESSAGE); buttonTriggeredInclusion = false; setInclusionMode(true); @@ -101,7 +101,7 @@ void Gateway::checkButtonTriggeredInclusion() { } } -void Gateway::checkInclusionFinished() { +void MyGateway::checkInclusionFinished() { if (inclusionMode && millis()-inclusionStartTime>60000UL*inclusionTime) { // inclusionTimeInMinutes minute(s) has passed.. stop inclusion mode setInclusionMode(false); @@ -109,13 +109,13 @@ void Gateway::checkInclusionFinished() { } -void Gateway::parseAndSend(char *commandBuffer) { +void MyGateway::parseAndSend(char *commandBuffer) { boolean ok = false; char *str, *p, *value=NULL; int i = 0; - uint16_t sensorRadioId = 0; - uint8_t childId = 0; - uint8_t messageType = 0; + uint16_t destination = 0; + uint8_t sensor = 0; + uint8_t command = 0; uint8_t type = 0; // Extract command data coming on serial line @@ -125,13 +125,13 @@ void Gateway::parseAndSend(char *commandBuffer) { ) { switch (i) { case 0: // Radioid (destination) - sensorRadioId = atoi(str); + destination = atoi(str); break; case 1: // Childid - childId = atoi(str); + sensor = atoi(str); break; case 2: // Message type - messageType = atoi(str); + command = atoi(str); break; case 3: // Data type type = atoi(str); @@ -143,19 +143,18 @@ void Gateway::parseAndSend(char *commandBuffer) { i++; } - if (sensorRadioId==GATEWAY_ADDRESS && messageType==M_INTERNAL) { + if (destination==GATEWAY_ADDRESS && command==C_INTERNAL) { // Handle messages directed to gateway if (type == I_VERSION) { // Request for version - serial(PSTR("0;0;%d;%d;%s\n"),M_INTERNAL, I_VERSION, LIBRARY_VERSION); + serial(PSTR("0;0;%d;%d;%s\n"),C_INTERNAL, I_VERSION, LIBRARY_VERSION); } else if (type == I_INCLUSION_MODE) { // Request to change inclusion mode setInclusionMode(atoi(value) == 1); } } else { txBlink(1); - - ok = sendData(GATEWAY_ADDRESS, sensorRadioId, childId, messageType, type, value, strlen(value), false); + ok = sendRoute(msg.build(GATEWAY_ADDRESS, destination, sensor, command, type).set(value)); if (!ok) { errBlink(1); } @@ -163,11 +162,11 @@ void Gateway::parseAndSend(char *commandBuffer) { } -void Gateway::setInclusionMode(boolean newMode) { +void MyGateway::setInclusionMode(boolean newMode) { if (newMode != inclusionMode) inclusionMode = newMode; // Send back mode change on serial line to ack command - serial(PSTR("0;0;%d;%d;%d\n"), M_INTERNAL, I_INCLUSION_MODE, inclusionMode?1:0); + serial(PSTR("0;0;%d;%d;%d\n"), C_INTERNAL, I_INCLUSION_MODE, inclusionMode?1:0); if (inclusionMode) { inclusionStartTime = millis(); @@ -176,32 +175,32 @@ void Gateway::setInclusionMode(boolean newMode) { // Override normal validate to add error blink if crc check fails -uint8_t Gateway::validate(uint8_t length) { - uint8_t res = Sensor::validate(length); +uint8_t MyGateway::validate(MyMessage message) { + uint8_t res = MySensor::validate(message); if (res == VALIDATE_BAD_CRC) { errBlink(1); } return res; } -void Gateway::processRadioMessage() { - if (messageAvailable()) { +void MyGateway::processRadioMessage() { + if (process()) { // A new message was received from one of the sensors - message_s msg = getMessage(); - if (msg.header.messageType == M_PRESENTATION && inclusionMode) { + MyMessage message = getLastMessage(); + if (message.getCommand() == C_PRESENTATION && inclusionMode) { rxBlink(3); } else { rxBlink(1); } // Pass along the message from sensors to serial line - serial(msg); + serial(message); } checkButtonTriggeredInclusion(); checkInclusionFinished(); } -void Gateway::serial(const char *fmt, ... ) { +void MyGateway::serial(const char *fmt, ... ) { va_list args; va_start (args, fmt ); vsnprintf_P(serialBuffer, MAX_SEND_LENGTH, fmt, args); @@ -213,12 +212,12 @@ void Gateway::serial(const char *fmt, ... ) { } } -void Gateway::serial(message_s msg) { - serial(PSTR("%d;%d;%d;%d;%s\n"),msg.header.from, msg.header.childId, msg.header.messageType, msg.header.type, msg.data); +void MyGateway::serial(MyMessage msg) { + serial(PSTR("%d;%d;%d;%d;%s\n"),msg.getSender(), msg.getSensor(), msg.getCommand(), msg.getType(), msg.getString()); } -void Gateway::ledTimersInterrupt() { +void MyGateway::ledTimersInterrupt() { if(countRx && countRx != 255) { // switch led on digitalWrite(pinRx, LOW); @@ -249,13 +248,13 @@ void Gateway::ledTimersInterrupt() { } -void Gateway::rxBlink(uint8_t cnt) { +void MyGateway::rxBlink(uint8_t cnt) { if(countRx == 255) { countRx = cnt; } } -void Gateway::txBlink(uint8_t cnt) { +void MyGateway::txBlink(uint8_t cnt) { if(countTx == 255 && !inclusionMode) { countTx = cnt; } } -void Gateway::errBlink(uint8_t cnt) { +void MyGateway::errBlink(uint8_t cnt) { if(countErr == 255) { countErr = cnt; } } diff --git a/libraries/MySensors/Gateway.h b/libraries/MySensors/MyGateway.h similarity index 100% rename from libraries/MySensors/Gateway.h rename to libraries/MySensors/MyGateway.h diff --git a/libraries/MySensors/MyMessage.cpp b/libraries/MySensors/MyMessage.cpp new file mode 100644 index 0000000..3b31083 --- /dev/null +++ b/libraries/MySensors/MyMessage.cpp @@ -0,0 +1,259 @@ +#include "MyMessage.h" +#include +#include + + +MyMessage::MyMessage() { + setVersion(PROTOCOL_VERSION); +} + +MyMessage::MyMessage(uint8_t _sensor, uint8_t _type, uint8_t _destination) { + sensor = _sensor; + type = _type; + destination = _destination; + setVersion(PROTOCOL_VERSION); + setCommand(C_SET_VARIABLE); +} + +MyMessage MyMessage::build(uint8_t _sender, uint8_t _destination, uint8_t _sensor, uint8_t _command, uint8_t _type) { + sender = _sender; + destination = _destination; + sensor = _sensor; + type = _type; + setCommand(_command); + setLength(0); + setPayloadType(P_STRING); + return *this; +} + +/*MyMessage MyMessage::failed() { + return getCommand() == C_FAILED +}*/ + +// Header getters + +uint8_t MyMessage::getCRC() { + return crc; +} + +uint8_t MyMessage::getVersion() { + return BF_GET(version_length, 0, 3); +} + +uint8_t MyMessage::getLength() { + return BF_GET(version_length, 3, 5); +} + +uint8_t MyMessage::getCommand() { + return BF_GET(command_payload, 0, 4); +} + +uint8_t MyMessage::getPayloadType() { + return BF_GET(command_payload, 4, 4); +} + +uint8_t MyMessage::getType() { + return type; +} + +uint8_t MyMessage::getSensor() { + return sensor; +} + +uint8_t MyMessage::getSender() { + return sender; +} + +uint8_t MyMessage::getLast() { + return last; +} + +uint8_t MyMessage::getDestination() { + return destination; +} + + +/* Getters for payload converted to desired form */ +void* MyMessage::getCustom() { + return (void *)data; +} + +/* NOTE: Calling this method might affect the payload by converting it to its string representation */ +char* MyMessage::getString(char *buffer) { + uint8_t payloadType = getPayloadType(); + + if (payloadType == P_STRING) { + data[getLength()] = '\0'; + return data; + } else if (buffer != NULL) { + switch(payloadType) { + case P_BYTE: + itoa(bValue, buffer, MAX_PAYLOAD); + break; + case P_INT16: + itoa(iValue, data, MAX_PAYLOAD); + break; + case P_UINT16: + ltoa(uiValue, data, MAX_PAYLOAD); + break; + case P_LONG32: + ltoa(lValue, data, MAX_PAYLOAD); + break; + case P_ULONG32: + ltoa(ulValue, data, MAX_PAYLOAD); + break; + case P_CUSTOM: + // TODO: Ok, what do we do here? We should probably convert this to hex + // Mostly gateway interested in this so we do the special handling of this + // over there. + + break; + } + return buffer; + } else { + return NULL; + } +} + +uint8_t MyMessage::getByte() { + return data[0]; +} +bool MyMessage::getBool() { + return !(data[0]-'0'==0); +} + +double MyMessage::getDouble() { + return strtod(data, NULL); +} +long MyMessage::getLong() { + return lValue; +} +unsigned long MyMessage::getULong() { + return ulValue; +} +int MyMessage::getInt() { + return iValue; +} +unsigned int MyMessage::getUInt() { + return uiValue; +} + +// Header setters +MyMessage MyMessage::setCRC(uint8_t _crc) { + crc = _crc; + return *this; +} + +MyMessage MyMessage::setVersion(uint8_t version) { + BF_SET(version_length, version, 0, 3); + return *this; +} + +MyMessage MyMessage::setLength(uint8_t length) { + BF_SET(version_length, length, 3, 5); + return *this; +} + +MyMessage MyMessage::setCommand(uint8_t command) { + BF_SET(command_payload, command, 0, 4); + return *this; +} + +MyMessage MyMessage::setPayloadType(uint8_t pt) { + BF_SET(command_payload, pt, 4, 4); + return *this; +} + +MyMessage MyMessage::setType(uint8_t _type) { + type = _type; + return *this; +} + +MyMessage MyMessage::setSensor(uint8_t _sensor) { + sensor = _sensor; + return *this; +} + +MyMessage MyMessage::setSender(uint8_t _sender) { + sender = _sender; + return *this; +} + +MyMessage MyMessage::setLast(uint8_t _last) { + last=_last; + return *this; +} + +MyMessage MyMessage::setDestination(uint8_t _destination) { + destination = _destination; + return *this; +} + + +// Set payload +MyMessage MyMessage::set(void* value, uint8_t length) { + setPayloadType(P_CUSTOM); + setLength(length); + memcpy(data, value, min(length, MAX_PAYLOAD)); + return *this; +} + + +MyMessage MyMessage::set(const char* value) { + uint8_t length = strlen(value); + setLength(length); + setPayloadType(P_STRING); + strncpy(data, value, min(length, MAX_PAYLOAD)); + return *this; +} + +MyMessage MyMessage::set(uint8_t value) { + setLength(1); + setPayloadType(P_BYTE); + data[0] = value; + return *this; +} + +MyMessage MyMessage::set(bool value) { + setLength(1); + setPayloadType(P_STRING); + data[0] = value?'1':'0'; + return *this; +} + +MyMessage MyMessage::set(double value, uint8_t decimals) { + dtostrf(value,2,decimals,data); + setLength(strlen(data)); + setPayloadType(P_STRING); + return *this; +} + +MyMessage MyMessage::set(unsigned long value) { + setPayloadType(P_ULONG32); + setLength(4); + ulValue = value; + return *this; +} + +MyMessage MyMessage::set(long value) { + setPayloadType(P_LONG32); + setLength(4); + lValue = value; + return *this; +} + +MyMessage MyMessage::set(unsigned int value) { + setPayloadType(P_INT16); + setLength(2); + uiValue = value; + return *this; +} + +MyMessage MyMessage::set(int value) { + setPayloadType(P_UINT16); + setLength(2); + iValue = value; + return *this; +} + + diff --git a/libraries/MySensors/MyMessage.h b/libraries/MySensors/MyMessage.h new file mode 100644 index 0000000..bd80b66 --- /dev/null +++ b/libraries/MySensors/MyMessage.h @@ -0,0 +1,184 @@ +/* + The MySensors library adds a new layer on top of the RF24 library. + It handles radio network routing, relaying and ids. + + Created by Henrik Ekblad + + 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 MyMessage_h +#define MyMessage_h + +#include + +#define PROTOCOL_VERSION 2 +#define MAX_MESSAGE_LENGTH 32 +#define HEADER_SIZE 8 +#define MAX_PAYLOAD (MAX_MESSAGE_LENGTH - HEADER_SIZE) +#define GATEWAY_ADDRESS ((uint8_t)0) + +// Message types +typedef enum { + C_PRESENTATION = 0, + C_SET_VARIABLE = 1, + C_REQ_VARIABLE = 2, + C_SET_WITH_ACK = 3, + C_INTERNAL = 4, + C_STREAM = 5, // For Firmaware and other larger chunks of data that need to be divided into pieces. + C_FAILED = 6 +} command; + +// Type of sensor data (for set/req/ack messages) +typedef enum { + V_TEMP,V_HUM, V_LIGHT, V_DIMMER, V_PRESSURE, V_FORECAST, V_RAIN, + V_RAINRATE, V_WIND, V_GUST, V_DIRECTION, V_UV, V_WEIGHT, V_DISTANCE, + V_IMPEDANCE, V_ARMED, V_TRIPPED, V_WATT, V_KWH, V_SCENE_ON, V_SCENE_OFF, + V_HEATER, V_HEATER_SW, V_LIGHT_LEVEL, V_VAR1, V_VAR2, V_VAR3, V_VAR4, V_VAR5, + V_UP, V_DOWN, V_STOP, V_IR_SEND, V_IR_RECEIVE, V_FLOW, V_VOLUME, V_LOCK_STATUS +} data; + +// Type of internal messages (for internal messages) +typedef enum { + I_BATTERY_LEVEL, I_TIME, I_VERSION, I_REQUEST_ID, + I_INCLUSION_MODE, I_RELAY_NODE, I_PING, I_PING_ACK, + I_LOG_MESSAGE, I_CHILDREN, I_UNIT, I_SKETCH_NAME, I_SKETCH_VERSION +} internal; + +// Type of sensor (for presentation message) +typedef enum { + S_DOOR, S_MOTION, S_SMOKE, S_LIGHT, S_DIMMER, S_COVER, S_TEMP, S_HUM, S_BARO, S_WIND, + S_RAIN, S_UV, S_WEIGHT, S_POWER, S_HEATER, S_DISTANCE, S_LIGHT_LEVEL, S_ARDUINO_NODE, + S_ARDUINO_RELAY, S_LOCK, S_IR, S_WATER +} sensor; + +// Type of data stream (for streamed message) +typedef enum { + ST_FIRMWARE, ST_SOUND, ST_IMAGE +} stream; + +typedef enum { + P_STRING, P_BYTE, P_INT16, P_UINT16, P_LONG32, P_ULONG32, P_CUSTOM +} payload; + +// Possible return values by validate() when doing crc check of received message. +typedef enum { + VALIDATE_OK, VALIDATE_BAD_CRC, VALIDATE_BAD_VERSION +} validationResult; + + + +#define BIT(n) ( 1<<(n) ) +// Create a bitmask of length len. +#define BIT_MASK(len) ( BIT(len)-1 ) +// Create a bitfield mask of length starting at bit 'start'. +#define BF_MASK(start, len) ( BIT_MASK(len)<<(start) ) +// Prepare a bitmask for insertion or combining. +#define BF_PREP(x, start, len) ( ((x)&BIT_MASK(len)) << (start) ) +// Extract a bitfield of length len starting at bit 'start' from y. +#define BF_GET(y, start, len) ( ((y)>>(start)) & BIT_MASK(len) ) +// Insert a new bitfield value x into y. +#define BF_SET(y, x, start, len) ( y= ((y) &~ BF_MASK(start, len)) | BF_PREP(x, start, len) ) + + + +class MyMessage +{ +public: + // Constructors + MyMessage(uint8_t sensor, uint8_t type, uint8_t destination=GATEWAY_ADDRESS); + MyMessage(); + + /* Check this after callback to see if request failed */ +// bool failed(); + + + // Getters for header + uint8_t getCRC(); + uint8_t getVersion(); + uint8_t getLength(); + uint8_t getCommand(); + uint8_t getPayloadType(); + uint8_t getType(); + uint8_t getSensor(); + uint8_t getSender(); + uint8_t getLast(); + uint8_t getDestination(); + + // Getters for payload + + + /** + * If payload is something else than P_STRING you can have payload the value converted + * to string representation by supplying a buffer with the minimum size of + * 2*MAX_PAYLOAD+1. This is to be able to fit hex-conversion of a full binary payload. + */ + char* getString(char *buffer = NULL); + + /** + * Returns payload untouched + */ + void* getCustom(); + + uint8_t getByte(); + bool getBool(); + double getDouble(); + long getLong(); + unsigned long getULong(); + int getInt(); + unsigned int getUInt(); + + // Builder helper + MyMessage build(uint8_t sender, uint8_t destination, uint8_t sensor, uint8_t command, uint8_t type); + + // Setters for header + MyMessage setCRC(uint8_t crc); + MyMessage setVersion(uint8_t version); + MyMessage setLength(uint8_t length); + MyMessage setCommand(uint8_t command); + MyMessage setPayloadType(uint8_t pt); + MyMessage setType(uint8_t type); + MyMessage setSensor(uint8_t sensor); + MyMessage setSender(uint8_t sender); + MyMessage setLast(uint8_t last); + MyMessage setDestination(uint8_t destination); + + + // Setters for payload + MyMessage set(void* payload, uint8_t length); + MyMessage set(const char* value); + MyMessage set(uint8_t value); + MyMessage set(bool value); + MyMessage set(double value, uint8_t decimals); + MyMessage set(unsigned long value); + MyMessage set(long value); + MyMessage set(unsigned int value); + MyMessage set(int value); + + + uint8_t version_length; // 3 bit - Protocol version + 5 bit - Length of payload + uint8_t command_payload; // 4 bit - Command type + 4 bit - Payload data type + uint8_t type; // 8 bit - Type varies depending on command + uint8_t sensor; // 8 bit - Id of sensor that this message concerns. + uint8_t sender; // 8 bit - Id of sender node + uint8_t last; // 8 bit - Id of last node this message passed + uint8_t destination; // 8 bit - Id of destination node + uint8_t crc; // 8 bit - Message checksum + // Each message can transfer a payload. We add one extra byte for string + // terminator \0 to be "printable" this is not transferred OTA + // This union is used to simplify the construction of the binary transferred int/long values. + union { + uint8_t bValue; + unsigned long ulValue; + long lValue; + unsigned int uiValue; + int iValue; + char data[MAX_PAYLOAD + 1]; + }; + + +}; + +#endif diff --git a/libraries/MySensors/MySensor.cpp b/libraries/MySensors/MySensor.cpp new file mode 100644 index 0000000..e4a3638 --- /dev/null +++ b/libraries/MySensors/MySensor.cpp @@ -0,0 +1,650 @@ + /* + The MySensors library adds a new layer on top of the RF24 library. + It handles radio network routing, relaying and ids. + + Created by Henrik Ekblad + + 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" + +MySensor::MySensor(uint8_t _cepin, uint8_t _cspin) : RF24(_cepin, _cspin) { +} + + +void MySensor::setupRadio(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e dataRate) { + failedTransmissions = 0; + + // Start up the radio library + RF24::begin(); + RF24::setAutoAck(1); + RF24::enableAckPayload(); + if (!RF24::isPVariant()) { + debug(PSTR("Radio wiring NOT ok or not using P version of NRF24L01.\n")); + while(1); + } + + RF24::setChannel(channel); + RF24::setPALevel(paLevel); + RF24::setDataRate(dataRate); + RF24::setRetries(5,15); + RF24::setCRCLength(RF24_CRC_16); + RF24::enableDynamicPayloads(); + + + + // All repeater nodes and gateway listen to broadcast pipe (for PING messages) + if (relayMode) { + RF24::openReadingPipe(BROADCAST_PIPE, TO_ADDR(BROADCAST_ADDRESS)); + } +} + +void MySensor::setupRelayMode(){ + childNodeTable = (uint8_t *) malloc(256); + // Read routing table from EEPROM + for (unsigned int i=0;i0 && route<255 && dest != GATEWAY_ADDRESS) { + debug(PSTR("Routing message to %d.\n"), route); + // Message destination is not gateway and is in routing table for this node. + // Send it downstream + return sendWrite(route, message); + } + } + + if (radioId != GATEWAY_ADDRESS) { + debug(PSTR("Relaying message back to gateway.\n")); + // Should be routed back to gateway. + bool ok = sendWrite(relayId, message); + + if (!ok) { + // Failure when sending to relay node. The relay node might be down and we + // need to find another route to gateway. Max 20 retries before giving up. + if (failedTransmissions > FIND_RELAY_AFTER_FAILED_TRANSMISSIONS) { + findRelay(); + } + failedTransmissions++; + } else { + failedTransmissions = 0; + } + return ok; + } + return false; +} + + + + + +boolean MySensor::sendWrite(uint8_t next, MyMessage message) { + uint8_t length = message.getLength(); + message.setLast(radioId); + message.setCRC(crc8Message(message)); + // Only print payload if string type + debug(PSTR("Tx: fr=%d,to=%d,la=%d,ne=%d,ci=%d,mt=%d,ty=%d,cr=%d:%s\n"), + message.getSender(),message.getDestination(), message.getLast(), next, message.getSensor(), message.getCommand(), message.getType(), message.getCRC(), message.getString()); + + bool broadcast = message.getCommand() == C_INTERNAL && message.getType() == I_PING; + + // Make sure radio has powered up + RF24::powerUp(); + RF24::stopListening(); + RF24::openWritingPipe(TO_ADDR(next)); + bool ok = RF24::write(&message, min(MAX_MESSAGE_LENGTH, HEADER_SIZE + length), broadcast); + RF24::startListening(); + + if (ok) + debug(PSTR("Sent successfully\n")); + else + debug(PSTR("Send failed\n")); + + + return ok; +} + +void MySensor::sendInternal(uint8_t variableType, const char *value) { + sendRoute(msg.build(radioId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, variableType).set(value)); +} + + +bool MySensor::send(MyMessage message, bool enableAck, uint8_t retry) { + message.setSender(radioId); + if (enableAck) { + message.setCommand(C_SET_WITH_ACK); + return wait(message, C_SET_VARIABLE, retry).getCommand() != C_FAILED; + } else { + return sendRoute(message);; + } +} + + +void MySensor::present(uint8_t childSensorId, uint8_t sensorType) { + sendRoute(msg.build(radioId, GATEWAY_ADDRESS, childSensorId, C_PRESENTATION, sensorType).set(LIBRARY_VERSION)); +} + +void MySensor::sketchInfo(const char *name, const char *version) { + if (name != NULL) + sendInternal(I_SKETCH_NAME, name); + if (version != NULL) + sendInternal(I_SKETCH_VERSION, version); +} + +MyMessage MySensor::get( uint8_t childSensorId, uint8_t variableType, uint8_t destination, uint8_t retry) { + msg.build(radioId, destination, childSensorId, C_REQ_VARIABLE, variableType); + return wait(msg, C_SET_VARIABLE, retry); +} + +void MySensor::request(uint8_t childSensorId, uint8_t variableType, uint8_t destination) { + process(); // Make sure to process any queued messages before sending new request + sendRoute(msg.build(radioId, destination, childSensorId, C_REQ_VARIABLE, variableType)); +} + + + +void MySensor::sendBatteryLevel(int value) { + sendInternal(I_BATTERY_LEVEL, ltoa(value, convBuffer, 10)); +} + +MyMessage MySensor::wait(MyMessage message, uint8_t expectedResponseCommand, uint8_t retry) { + while (retry>0) { + + uint8_t sensor = message.getSensor(); + uint8_t type = message.getType(); + sendRoute(message); + uint8_t i = 0; + while (i < 100) { // 1 seconds timeout before re-sending status request + while (process()) { + // Check that it is right type of message and not a routing message + if (msg.getCommand() == expectedResponseCommand && + msg.getType() == type && + msg.getSensor() == sensor) { + return msg; + } + } + delay(10); + i++; + } + retry--; + } + msg.setCommand(C_FAILED); + return msg; +} + + + + +MyMessage MySensor::getInternal(uint8_t variableType, uint8_t retry) { + msg.build(radioId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, variableType); + return wait(msg, C_INTERNAL, retry); +} + +/*unsigned long MySensor::getTime(uint8_t retry) { + MyMessage message = getInternal(I_TIME, retry); + return message == NULL ? 0UL : message.getULong(); +}*/ + +void MySensor::requestTime() { + sendRoute(msg.build(radioId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, I_TIME)); +} + + +/*bool MySensor::isMetricSystem(uint8_t retry) { + MyMessage message = getInternal(I_UNIT, retry); + return (message == NULL) + return true; + else + return message.getByte() == 'M'; +}*/ + +void MySensor::requestIsMetricSystem() { + sendRoute(msg.build(radioId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, I_UNIT)); +} + + +boolean MySensor::process() { + uint8_t pipe; + boolean available = RF24::available(&pipe); + + if (available) { + debug(PSTR("Message available on pipe %d\n"),pipe); + } + + if (available && pipe<7) { + uint8_t len = RF24::getDynamicPayloadSize(); + RF24::read(&msg, len); + RF24::writeAckPayload(pipe,&pipe, 1 ); + + uint8_t valid = validate(msg); + boolean ok = valid == VALIDATE_OK; + + // Make sure string gets terminated ok for full string messages. + msg.data[len - HEADER_SIZE ] = '\0'; + debug(PSTR("Rx: fr=%d,to=%d,la=%d,ci=%d,mt=%d,t=%d,cr=%d(%s): %s\n"), + msg.getSender(),msg.getDestination(), msg.getLast(), msg.getSensor(), msg.getCommand(), msg.getType(), msg.getCRC(), valid==0?"ok":valid==1?"ec":"ev", msg.getString()); + + if (ok) { + uint8_t command = msg.getCommand(); + uint8_t type = msg.getType(); + uint8_t sender = msg.getSender(); + uint8_t last = msg.getLast(); + uint8_t destination = msg.getDestination(); + + if (relayMode && command == C_INTERNAL && type == I_PING) { + // Relays should always answer ping messages + // Wait a random delay of 0-2 seconds to minimize collision + // between ping ack messages from other relaying nodes + randomSeed(millis()); + delay(random(2000)); + uint8_t replyTo = msg.getSender(); + msg.build(radioId, replyTo, NODE_CHILD_ID, C_INTERNAL, I_PING_ACK).set(distance); + sendWrite(replyTo, msg); + } else if (sender == radioId) { + // This message is addressed to this node + if (relayMode && command == C_INTERNAL) { + // Handle all internal messages to this node + if (type == I_RELAY_NODE && destination != GATEWAY_ADDRESS) { + // Someone at sensor net gateway side wants this node to refresh its relay node + findRelay(); + // Send relay information back to sensor net gateway node device. + sendInternal(I_RELAY_NODE, ltoa(relayId, convBuffer, 10)); + return false; + } else if (type == I_CHILDREN && destination != GATEWAY_ADDRESS) { + debug(PSTR("Route command received")); + if (msg.getByte() == 'F') { + // Send in as many children we can fit in a message (binary) + sendChildren(); + } else if (msg.getByte() == 'C') { + // Clears child relay data for this node + clearChildRoutes(); + } + return false; + } + // Return rest of internal messages back to sketch... + if (last != GATEWAY_ADDRESS) + addChildRoute(sender, last); + + return true; + } else { + // If this is variable message from sensor net gateway. Send ack back. + debug(PSTR("Message addressed for this node.\n")); + + // Send set-message back to sender if sender wants this + if (command == C_SET_WITH_ACK) { + sendAck(msg); + // The library user should not need to care about this ack request. Just treat it as a normal SET. + msg.setCommand(C_SET_VARIABLE); + } + // Call incoming message callback if available + if (msgCallback != NULL) { + msgCallback(msg); + } + + if (last != GATEWAY_ADDRESS) + addChildRoute(sender, last); + + // Return true if message was addressed for this node... + return true; + } + } else if (relayMode) { + // We should try to relay this message to another node + + uint8_t route = getChildRoute(msg.getDestination()); + if (route>0 && route<255) { + debug(PSTR("Routing message to child node.\n")); + // 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 + // + // lookup route in table and send message there + sendWrite(route, msg); + } else if (pipe == CURRENT_NODE_PIPE) { + // 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) + + debug(PSTR("Routing message to relay.\n")); + // This message should be routed back towards sensor net gateway + sendWrite(relayId, msg); + // Add this child to our "routing table" if it not already exist + addChildRoute(sender, last); + } else { + // We're snooped a message directed to gateway from another branch + // Make sure to remove the sender node from our routing table. + // + // +-----B <-- Message comes from here + // -A + // +-----C <-- We're here + // + // So the sender of message should never be in our + // routing table. + // + // We could also end up in this else-statement if message + // destination has no route. + // This can happen if a node never has sent any message to + // sensor net gateway (which will build up routing information in all nodes + // that gets passed). + if (radioId == GATEWAY_ADDRESS) { + // We should perhaps inform gateway about the no-route situation?! + debug(PSTR("Unknown route from GW\n")); + + } else { + debug(PSTR("Remove child node from routing table.\n")); + removeChildRoute(sender); + } + } + + } + } + } + return false; +} + + + + + +MyMessage MySensor::getLastMessage() { + return msg; +} + + +/* + * calculate CRC8 on MyMessage data taking care of data structure and protocol version + */ +uint8_t MySensor::crc8Message(MyMessage message) { + uint8_t len = message.getLength(); + uint8_t crc = 0x00; + uint8_t loop_count; + uint8_t bit_counter; + uint8_t data; + uint8_t feedback_bit; + uint8_t number_of_bytes_to_read = (uint8_t)sizeof(message); + + // Must set crc to a constant value. + message.setCRC(0); + + // fill unused space by zeroes for string data only + if(len>=0 && len < sizeof(message.data)-1) { + memset(&message.data[len], 0, sizeof(message.data) - 1 - len); + } + + + for (loop_count = 0; loop_count != number_of_bytes_to_read; loop_count++) + { + data = ((uint8_t*)&message)[loop_count]; + + bit_counter = 8; + do { + feedback_bit = (crc ^ data) & 0x01; + + if ( feedback_bit == 0x01 ) { + crc = crc ^ 0x18; //0X18 = X^8+X^5+X^4+X^0 + } + crc = (crc >> 1) & 0x7F; + if ( feedback_bit == 0x01 ) { + crc = crc | 0x80; + } + + data = data >> 1; + bit_counter--; + + } while (bit_counter > 0); + } + return crc; +} + + +uint8_t MySensor::validate(MyMessage message) { + uint8_t oldCrc = message.getCRC(); + uint8_t newCrc = crc8Message(message); + + if(!(message.getVersion() == PROTOCOL_VERSION)) return VALIDATE_BAD_VERSION; + + if(!(oldCrc == newCrc)) return VALIDATE_BAD_CRC; + return VALIDATE_OK; +} + + +void MySensor::addChildRoute(uint8_t childId, uint8_t route) { + if (childNodeTable[childId] != route) { + childNodeTable[childId] = route; + EEPROM.write(EEPROM_ROUTES_ADDRESS+childId, route); + } +} + +void MySensor::removeChildRoute(uint8_t childId) { + if (childNodeTable[childId] != 0xff) { + childNodeTable[childId] = 0xff; + EEPROM.write(EEPROM_ROUTES_ADDRESS+childId, 0xff); + } +} + +uint8_t MySensor::getChildRoute(uint8_t childId) { + return childNodeTable[childId]; +} + +void MySensor::clearChildRoutes() { + debug(PSTR("Clear child routing data\n")); + for (unsigned int i=0;i< sizeof(childNodeTable); i++) { + removeChildRoute(i); + } + sendInternal(I_CHILDREN, ""); +} + +void MySensor::sendChildren() { + // Send in which children that is using this node as an relay. + // TODO: Fix this + debug(PSTR("Dump beginning of routing table\n")); + + for (int i=0;i< 50; i++) { +// Serial.println(childNodeTable[i]); + debug(PSTR("rt:%d, %d\n"), i, getChildRoute(i) ); + } + + sendInternal(I_CHILDREN, "not implemented"); +} + + + + + +#ifdef DEBUG +void MySensor::debugPrint(const char *fmt, ... ) { + char fmtBuffer[300]; + if (radioId == GATEWAY_ADDRESS) { + // prepend debug message to be handled correctly by gw + Serial.print("0;0;4;11;"); + } + va_list args; + va_start (args, fmt ); + vsnprintf_P(fmtBuffer, 300, fmt, args); + va_end (args); + if (radioId == GATEWAY_ADDRESS) { + // Truncate message if this is gateway node + vsnprintf_P(fmtBuffer, 60, fmt, args); + fmtBuffer[59] = '\n'; + fmtBuffer[60] = '\0'; + } else { + vsnprintf_P(fmtBuffer, 299, fmt, args); + } + va_end (args); + Serial.print(fmtBuffer); + + + //Serial.print("0;0;4;11;Mem free:"); + //Serial.println(freeRam()); +} +#endif + + +#ifdef DEBUG +int MySensor::freeRam (void) { + extern int __heap_start, *__brkval; + int v; + return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); +} +#endif diff --git a/libraries/MySensors/MySensor.h b/libraries/MySensors/MySensor.h new file mode 100644 index 0000000..1cc3537 --- /dev/null +++ b/libraries/MySensors/MySensor.h @@ -0,0 +1,237 @@ +/* + The MySensors library adds a new layer on top of the RF24 library. + It handles radio network routing, relaying and ids. + + Created by Henrik Ekblad + + 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 MySensor_h +#define MySensor_h + +#include "Version.h" // Auto generated by bot +#include "MyConfig.h" +#include "MyMessage.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef DEBUG +#define debug(x,...) debugPrint(x, ##__VA_ARGS__) +#else +#define debug(x,...) +#endif + +#define BAUD_RATE 115200 + +#define AUTO 0xFF // 0-254. Id 255 is reserved for auto initialization of radioId. +#define NODE_CHILD_ID 0xFF // Node child id is always created for when a new sensor is detected +#define EEPROM_RADIO_ID_ADDRESS 0 // Where to store radio id in EEPROM +#define EEPROM_RELAY_ID_ADDRESS 1 // Where to store relay id in EEPROM +#define EEPROM_DISTANCE_ADDRESS 2 // Where to store distance to gateway in EEPROM +#define EEPROM_ROUTES_ADDRESS ((uint8_t)3) // Where to start storing routing information in EEPROM. Will allocate 256 bytes. + + +// This is the radioId for sensor net gateway receiver sketch (where all sensors should send their data). +// This is also act as base value for sensor radioId +#define BASE_RADIO_ID ((uint64_t)0xA8A8E1FC00LL) +#define GATEWAY_ADDRESS ((uint8_t)0) +#define BROADCAST_ADDRESS ((uint8_t)0xFF) +#define TO_ADDR(x) (BASE_RADIO_ID + x) + +#define WRITE_PIPE ((uint8_t)0) +#define CURRENT_NODE_PIPE ((uint8_t)1) +#define BROADCAST_PIPE ((uint8_t)2) + +#define SEND_RETRIES 3 +#define FIND_RELAY_RETRIES 2 +#define FIND_RELAY_AFTER_FAILED_TRANSMISSIONS 20 + + +class MySensor : public RF24 +{ + public: + /** + * Constructor + * + * Creates a new instance of Sensor class. + * + * @param _cepin The pin attached to RF24 Chip Enable on the RF module (defualt 9) + * @param _cspin The pin attached to RF24 Chip Select (default 10) + */ + MySensor(uint8_t _cepin=9, uint8_t _cspin=10); + + /** + * 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. Default is NULL + * @param relayMode Activate relay mode. This node will forward messages to other nodes in the radio network. Make sure to call process() regularly. Default in false + * @param radioId The unique id (1-254) for this sensor. Default is AUTO(255) which means sensor tries to fetch an id from controller. + * @param paLevel Radio PA Level for this sensor. Default RF24_PA_MAX + * @param channel Radio channel. Default is channel 76 + * @param dataRate Radio transmission speed. Default RF24_1MBPS + */ + + void begin(void (* msgCallback)(MyMessage)=NULL, boolean relayMode=false, uint8_t radioId=AUTO, rf24_pa_dbm_e paLevel=RF24_PA_LEVEL, uint8_t channel=RF24_CHANNEL, rf24_datarate_e dataRate=RF24_DATARATE); + + /** + * Return the nodes radioId. + */ + uint8_t getRadioId(); + + /** + * 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 sensorChildId Sensor child id for the presented. Choose anything between 0-254. + * @param sensorType The sensor type. See sensor typedef in Message.h. + */ + void present(uint8_t sensorChildId, uint8_t sensorType); + + /** + * 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 + */ + void sketchInfo(const char *name, const char *version); + + /** + * 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. + * @param retry Number of attempts before giving up. Default is SEND_RETRIES + * @return true Retuns false if ack is enabled and no ack came back + */ + bool send(MyMessage msg, bool ack=false, uint8_t retry=SEND_RETRIES); + + /** + * Send this nodes battery level to gateway. + * @param level Level between 0-100(%) + */ + void sendBatteryLevel(int level); + + /** + * Fetches a value from gateway or some other sensor in the radio network. Resends request and waits for response. + * Note that if you have registered a callback in begin() it will also be called before message is returned + * by get() + * + * @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 radioId of other node in radio network. Default is gateway + * @param number of attempts to fetch + * @return Message response + */ + MyMessage get(uint8_t childSensorId, uint8_t variableType, uint8_t destination=GATEWAY_ADDRESS, uint8_t retry=SEND_RETRIES); + + /** + * Requests a value from gateway or some other sensor in the radio network. + * Make sure to add callback in begin() to handle request response. + * + * @param childSensorId The unique child id for the different sensors connected to this arduino. 0-254. + * @param variableType The variableType to fetch + * @param callback for result. This will be called when a message reply is received or request fails (see status of second argument). + * @param destination The radioId of other node in radio network. Default is gateway + */ + /*void (* msgCallback)(MyMessage, bool)=NULL, */ + void request(uint8_t childSensorId, uint8_t variableType, uint8_t destination=GATEWAY_ADDRESS); + + + /** + * Fetches time from sensor net gateway. + * + * @return Seconds since 1970, 0 if failed. + */ +// unsigned long getTime(uint8_t retry=SEND_RETRIES); + void requestTime(); + + /** + * Fetches unit setting from sensor net gateway. Returns true if metric system has been selected which means + * that sensor should report it's information in: celsius, meter, cm, gram, km/h, m/s etc.. + * If false is returned the sensor should report data in imperial system which means + * fahrenheit, feet, gallon, mph etc... + */ + void requestIsMetricSystem(); +// bool isMetricSystem(uint8_t retry=SEND_RETRIES); + + + /** + * 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(void); + + /** + * Returns the last received message + */ + MyMessage getLastMessage(void); + + + + + + +#ifdef DEBUG + void debugPrint(const char *fmt, ... ); + int freeRam(); +#endif + + + protected: + MyMessage msg; // Buffer for incoming messages. + uint8_t failedTransmissions; + boolean relayMode; + uint8_t radioId; + uint8_t distance; // This nodes distance to sensor net gateway (number of hops) + uint8_t relayId; + char convBuffer[20]; + + void setupRelayMode(); + void setupRadio(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e dataRate); + void findRelay(); + boolean sendRoute(MyMessage message); + boolean sendWrite(uint8_t dest, MyMessage message); + void sendInternal(uint8_t variableType, const char *value); + boolean sendAck(MyMessage message); + boolean sendData(uint8_t from, uint8_t to, uint8_t childId, uint8_t messageType, uint8_t type, const char *data, uint8_t length, boolean binaryMessage); + uint8_t validate(MyMessage message); + void sendChildren(); + + + private: + // Callbacks for reqeusts +/* struct callback { + bool active; // Deactivates callback after each reply + uint8_t command; // Command type + uint8_t sender; + uint8_t sensor; + uint8_t type; + void (*msgCallback)(MyMessage, bool); + };*/ + MyMessage ack; // Buffer for ack messages. + uint8_t *childNodeTable; // In memory buffer for routing to other nodes. Also stored in EEPROM + void (*msgCallback)(MyMessage); // Callback for undefined incoming messages + + void initializeRadioId(); + uint8_t crc8Message(MyMessage); + MyMessage wait(MyMessage msg, uint8_t expectedReceiveType, uint8_t retry); + MyMessage getInternal(uint8_t variableType, uint8_t retry=SEND_RETRIES); + + uint8_t getChildRoute(uint8_t childId); + void addChildRoute(uint8_t childId, uint8_t route); + void removeChildRoute(uint8_t childId); + void clearChildRoutes(); +}; + +#endif diff --git a/libraries/MySensors/Relay.cpp b/libraries/MySensors/Relay.cpp deleted file mode 100644 index 19f5b0d..0000000 --- a/libraries/MySensors/Relay.cpp +++ /dev/null @@ -1,271 +0,0 @@ -/* - The MySensors library adds a new layer on top of the RF24 library. - It handles radio network routing, relaying and ids. - - Created by Henrik Ekblad - - 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 "Relay.h" - -Relay::Relay(uint8_t _cepin, uint8_t _cspin) : Sensor(_cepin, _cspin) { - isRelay = true; -} - - -void Relay::begin(uint8_t radioId, rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e dataRate) { - Sensor::begin(radioId, paLevel, channel, dataRate); - // Read routing table from EEPROM - for (unsigned int i=0;i0) { - debug(PSTR("Found child in routing table. Sending to %d\n"),route); - buildMsg(from, to, childId, messageType, type, data, length, binary); - // Found node in route table - ok = sendWrite(route, msg, length); - } else if (radioId == GATEWAY_ADDRESS) { - // If we're GW (no parent...). As a last resort try sending message directly to node. - debug(PSTR("No route... try sending direct.\n")); - buildMsg(from, to, childId, messageType, type, data, length, binary); - // Found node in route table - ok = sendWrite(to, msg, length); - } else { - // We are probably a repeater node which should send message back to relay - ok = Sensor::sendData(from, to, childId, messageType, type, data, length, binary); - } - } else { - debug(PSTR("Message too large\n")); - } - return ok; -} - - - -boolean Relay::send(message_s message, int length) { - bool ok = true; - uint8_t route = getChildRoute(msg.header.to); - - if (route>0 && route<255 && message.header.to != GATEWAY_ADDRESS) { - debug(PSTR("Routing message to %d.\n"), route); - // Message destination is not gateway and is in routing table for this node. - // Send it downstream - ok = sendWrite(route, message, length); - - - } else if (radioId != GATEWAY_ADDRESS) { - debug(PSTR("Sending message back towards gw.\n")); - // Should be routed back to gateway. Sensor code knows how to do this. - ok = Sensor::send(message, length); - } - - if (!ok) { - debug(PSTR("No ack received.\n")); - } - return ok; -} - - - -boolean Relay::messageAvailable() { - uint8_t pipe; - boolean available = RF24::available(&pipe); - - if (available) { - debug(PSTR("Message available on pipe %d\n"),pipe); - } - - if (available && pipe<7) { - uint8_t len = RF24::getDynamicPayloadSize(); - RF24::read(&msg, len); - RF24::writeAckPayload(pipe,&pipe, 1 ); - - uint8_t valid = validate(len-sizeof(header_s)); - boolean ok = valid == VALIDATE_OK; - - // Make sure string gets terminated ok for full sized messages. - msg.data[len - sizeof(header_s) ] = '\0'; - debug(PSTR("Rx: fr=%d,to=%d,la=%d,ci=%d,mt=%d,t=%d,cr=%d(%s): %s\n"), - msg.header.from,msg.header.to, msg.header.last, msg.header.childId, msg.header.messageType, msg.header.type, msg.header.crc, valid==0?"ok":valid==1?"ec":"ev", msg.data); - - - //boolean ok = readMessage(); - if (ok) { - if (msg.header.messageType == M_INTERNAL && - msg.header.type == I_PING) { - // We always answer ping messages - // Wait a random delay of 0-2 seconds to minimize collision - // between ping ack messages from other relaying nodes - randomSeed(millis()); - delay(random(2000)); - ltoa(distance, convBuffer, 10); - uint8_t to = msg.header.from; - debug(PSTR("Answer ping message. %d\n"), strlen(convBuffer)); - buildMsg(radioId, to, NODE_CHILD_ID, M_INTERNAL, I_PING_ACK, convBuffer,strlen(convBuffer), false); - sendWrite(to, msg, strlen(convBuffer)); - } else if (msg.header.to == radioId) { - // This message is addressed to this node - if (msg.header.messageType == M_INTERNAL) { - // Handle all internal messages to this node - if (msg.header.type == I_RELAY_NODE && msg.header.to != GATEWAY_ADDRESS) { - // Someone at sensor net gateway side wants this node to refresh its relay node - findRelay(); - // Send relay information back to sensor net gateway node device. - sendInternal(I_RELAY_NODE, ltoa(relayId, convBuffer, 10)); - return false; - } else if (msg.header.type == I_CHILDREN && msg.header.to != GATEWAY_ADDRESS) { - debug(PSTR("Route command received")); - if (strncmp(msg.data,"F", 1) == 0) { - // Send in as many children we can fit in a message (binary) - sendChildren(); - } else if (strncmp(msg.data,"C", 1) == 0) { - // Clears child relay data for this node - clearChildRoutes(); - } - return false; - } - // Return rest of internal messages back to sketch... - if (msg.header.last != GATEWAY_ADDRESS) - addChildRoute(msg.header.from, msg.header.last); - - return true; - } else { - // If this is variable message from sensor net gateway. Send ack back. - debug(PSTR("Message addressed for this node.\n")); - - // Send set-message back to sender if sender wants this - if (msg.header.messageType == M_SET_WITH_ACK) { - sendVariableAck(); - // The library user should not need to care about this ack request. Just treat it as a normal SET. - msg.header.messageType = M_SET_VARIABLE; - } - - // Return message to waiting sketch... - if (msg.header.last != GATEWAY_ADDRESS) - addChildRoute(msg.header.from, msg.header.last); - - return true; - } - } else { - // We should probably try to relay this message - relayMessage(len-sizeof(msg.header), pipe); - } - } - } - return false; -} - - - - -void Relay::relayMessage(uint8_t length, uint8_t pipe) { - uint8_t route = getChildRoute(msg.header.to); - if (route>0 && route<255) { - debug(PSTR("Routing message to child node.\n")); - // 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 - // - // lookup route in table and send message there - sendWrite(route, msg, length); - } else if (pipe == CURRENT_NODE_PIPE) { - // 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) - - debug(PSTR("Routing message to relay.\n")); - // This message should be routed back towards sensor net gateway - sendWrite(relayId, msg, length); - // Add this child to our "routing table" if it not already exist - addChildRoute(msg.header.from, msg.header.last); - } else { - // We're snooped a message directed to gateway from another branch - // Make sure to remove the sender node from our routing table. - // - // +-----B <-- Message comes from here - // -A - // +-----C <-- We're here - // - // So the sender of message should never be in our - // routing table. - // - // We could also end up in this else-statement if message - // destination has no route. - // This can happen if a node never has sent any message to - // sensor net gateway (which will build up routing information in all nodes - // that gets passed). - if (radioId == GATEWAY_ADDRESS) { - // We should perhaps inform gateway about the no-route situation?! - debug(PSTR("Unknown route from GW\n")); - - } else { - debug(PSTR("Remove child node from routing table.\n")); - removeChildRoute(msg.header.from); - } - } -} - - -void Relay::addChildRoute(uint8_t childId, uint8_t route) { - if (childNodeTable[childId] != route) { - childNodeTable[childId] = route; - EEPROM.write(EEPROM_ROUTES_ADDRESS+childId, route); - } -} - -void Relay::removeChildRoute(uint8_t childId) { - if (childNodeTable[childId] != 0xff) { - childNodeTable[childId] = 0xff; - EEPROM.write(EEPROM_ROUTES_ADDRESS+childId, 0xff); - } -} - -uint8_t Relay::getChildRoute(uint8_t childId) { - return childNodeTable[childId]; -} - -void Relay::clearChildRoutes() { - debug(PSTR("Clear child routing data\n")); - for (unsigned int i=0;i< sizeof(childNodeTable); i++) { - removeChildRoute(i); - } - sendInternal(I_CHILDREN, ""); -} - -void Relay::sendChildren() { - // Send in which children that is using this node as an relay. - // TODO: Fix this - debug(PSTR("Dump beginning of routing table\n")); - - for (int i=0;i< 50; i++) { -// Serial.println(childNodeTable[i]); - debug(PSTR("rt:%d, %d\n"), i, getChildRoute(i) ); - } - - sendInternal(I_CHILDREN, "not implemented"); -} - - diff --git a/libraries/MySensors/Relay.h b/libraries/MySensors/Relay.h deleted file mode 100644 index c5f3272..0000000 --- a/libraries/MySensors/Relay.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - The MySensors library adds a new layer on top of the RF24 library. - It handles radio network routing, relaying and ids. - - Created by Henrik Ekblad - - 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 Relay_h -#define Relay_h - -#include "Sensor.h" - -#ifdef DEBUG -#define debug(x,...) debugPrint(x, ##__VA_ARGS__) -#else -#define debug(x,...) -#endif - - - -#define EEPROM_ROUTES_ADDRESS ((uint8_t)3) // Where to start storing routing information in EEPROM. Will allocate 256 bytes. - - -class Relay : public Sensor -{ - - public: - /** - * Constructor - * - * Creates a new instance of Relay class. - * - * @param _cepin The pin attached to RF24 Chip Enable on the RF module (default 9) - * @param _cspin The pin attached to RF24 Chip Select (default 10) - */ - Relay(uint8_t _cepin = 9, uint8_t _cspin = 10); - - void begin(uint8_t radioId=AUTO, rf24_pa_dbm_e paLevel=RF24_PA_LEVEL, uint8_t channel=RF24_CHANNEL, rf24_datarate_e dataRate=RF24_DATARATE); - boolean messageAvailable(); - boolean send(message_s message, int length); - - boolean sendData(uint8_t from, uint8_t to, uint8_t childId, uint8_t messageType, uint8_t type, const char *data, uint8_t length, boolean binary); - - protected: - void sendChildren(); - - private: - uint8_t childNodeTable[256]; // Buffer to store child node information. Store this in EEPROM - - uint8_t getChildRoute(uint8_t childId); - void addChildRoute(uint8_t childId, uint8_t route); - void removeChildRoute(uint8_t childId); - void clearChildRoutes(); - void relayMessage(uint8_t length, uint8_t pipe); - -}; - -#endif diff --git a/libraries/MySensors/Sensor.cpp b/libraries/MySensors/Sensor.cpp deleted file mode 100644 index 72adc9f..0000000 --- a/libraries/MySensors/Sensor.cpp +++ /dev/null @@ -1,555 +0,0 @@ - /* - The MySensors library adds a new layer on top of the RF24 library. - It handles radio network routing, relaying and ids. - - Created by Henrik Ekblad - - 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 "Sensor.h" - - -Sensor::Sensor(uint8_t _cepin, uint8_t _cspin) : RF24(_cepin, _cspin) { - isRelay = false; -} - - -void Sensor::setupRadio(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e dataRate) { - failedTransmissions = 0; - - // Start up the radio library - RF24::begin(); - RF24::setAutoAck(1); - RF24::enableAckPayload(); - if (!RF24::isPVariant()) { - debug(PSTR("Sorry, you'll need to use the P version of NRF24L01.\n")); - while(1); - } - - RF24::setChannel(channel); - RF24::setPALevel(paLevel); - RF24::setDataRate(dataRate); - RF24::setRetries(5,15); - RF24::setCRCLength(RF24_CRC_16); - RF24::enableDynamicPayloads(); - - - - // All repeater nodes and gateway listen to broadcast pipe (for PING messages) - if (isRelay) { - RF24::openReadingPipe(BROADCAST_PIPE, TO_ADDR(BROADCAST_ADDRESS)); - } -} - -void Sensor::begin(uint8_t _radioId, rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e dataRate) { - Serial.begin(BAUD_RATE); - - radioId = _radioId; - - debug(PSTR("Started %s.\n"), isRelay?"relay":"sensor"); - - - setupRadio(paLevel, channel, dataRate); - - // Fetch relay from EEPROM - relayId = EEPROM.read(EEPROM_RELAY_ID_ADDRESS); - // Fetch distance from eeprom - distance = EEPROM.read(EEPROM_DISTANCE_ADDRESS); - if (relayId == 0xff) { - // No relay previously fetched and stored in eeprom. Try to find one. - findRelay(); - } - debug(PSTR("Relay=%d, distance=%d\n"), relayId, distance); - - initializeRadioId(); - - // Open reading pipe for messages directed to this node (set write pipe to same) - RF24::openReadingPipe(WRITE_PIPE, TO_ADDR(radioId)); - RF24::openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(radioId)); - - // Send presentation for this radio node - sendSensorPresentation(NODE_CHILD_ID, isRelay? S_ARDUINO_RELAY : S_ARDUINO_NODE); - - // Send relay information back to sensor net gateway. - sendInternal(I_RELAY_NODE, itoa(relayId, convBuffer, 10)); - - // Initialize ack message header - ack.header.version = PROTOCOL_VERSION; - ack.header.binary = 0; - ack.header.from = radioId; - ack.header.to = GATEWAY_ADDRESS; - ack.header.messageType = M_SET_VARIABLE; -} - - -uint8_t Sensor::getRadioId() { - return radioId; -} - -void Sensor::initializeRadioId() { - if (radioId == AUTO) { - radioId = EEPROM.read(EEPROM_RADIO_ID_ADDRESS); - if (radioId == 0xFF || radioId == 0) { - radioId = AUTO; - debug(PSTR("No radio id found in EEPROM fetching one from sensor net gateway\n")); - // No radio id has been fetched yet ant EEPROM is unwritten. - // Request new id from sensor net gateway. Use radioId 4095 temporarily - // to be able to receive my correct nodeId. - RF24::openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(radioId)); - radioId = atoi(getInternal(I_REQUEST_ID)); - // Write id to EEPROM - if (radioId == AUTO) { // sensor net gateway will return max id if all sensor id are taken - debug(PSTR("Sensor network is full! You already have the maximum of sensors!\n")); - while (1) {} // Wait here. Nothing else we can do... - } else { - debug(PSTR("Radio id received: %d\n"), radioId); - EEPROM.write(EEPROM_RADIO_ID_ADDRESS, radioId); - } - } - debug(PSTR("Radio id stored in EEPROM was: %d\n"), radioId); - } -} - - -void Sensor::findRelay() { - // This can be improved by using status = read_register(OBSERVE_TX,&observe_tx,1) on the - // sending side.. the problem is getting this information here without too much fuss. - // Try to find relay 3 times before giving up - if (radioId == GATEWAY_ADDRESS) - return; // Gateway has no business here! - - failedTransmissions = 0; - - // Open a reading pipe for current radioId (if it differs from broadcast) - if (radioId != BROADCAST_PIPE) { - RF24::openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(radioId)); - debug(PSTR("Open ping reading pipe: %d\n"), radioId); - } - distance = 255; - uint8_t oldRelayId = relayId; - uint8_t retries=0; - while (distance == 255 && retries FIND_RELAY_AFTER_FAILED_TRANSMISSIONS) { - findRelay(); - } - failedTransmissions++; - } else { - failedTransmissions = 0; - } - return ok; -} - - -boolean Sensor::sendWrite(uint8_t dest, message_s message, int length) { - message.header.last = radioId; - message.header.crc = crc8Message(message, length); - debug(PSTR("Tx: fr=%d,to=%d,la=%d,ne=%d,ci=%d,mt=%d,ty=%d,cr=%d: %s\n"), - message.header.from,message.header.to, message.header.last, dest, message.header.childId, message.header.messageType, message.header.type, message.header.crc, message.data); - - bool broadcast = message.header.messageType == M_INTERNAL && message.header.type == I_PING; - - RF24::stopListening(); - RF24::openWritingPipe(TO_ADDR(dest)); - bool ok = RF24::write(&message, min(MAX_MESSAGE_LENGTH, sizeof(message.header) + length), broadcast); - RF24::startListening(); - - - //RF24::closeReadingPipe(WRITE_PIPE); // Stop listening to write-pipe after transmit - //delay(50); - - if (ok) - debug(PSTR("Sent successfully\n")); - else - debug(PSTR("Send failed\n")); - - - return ok; -} - -void Sensor::sendInternal(uint8_t variableType, const char *value) { - sendData(radioId, GATEWAY_ADDRESS, NODE_CHILD_ID, M_INTERNAL, variableType, value, strlen(value), false); -} - -// This will be added in 1.4 -/*void Sensor::sendVariable(uint8_t childId, uint8_t variableType, - const char *value, length) { - sendData(radioId, GATEWAY_ADDRESS, childId, M_SET_VARIABLE, variableType, value, length, true); -}*/ - - -void Sensor::sendVariable(uint8_t childId, uint8_t variableType, - const char *value) { - sendData(radioId, GATEWAY_ADDRESS, childId, M_SET_VARIABLE, variableType, value, strlen(value), false); -} - -void Sensor::sendVariable(uint8_t childId, uint8_t variableType, double value, int decimals) { - sendVariable(childId, variableType, dtostrf(value,2,decimals,convBuffer)); -} - -void Sensor::sendVariable(uint8_t childId, uint8_t variableType, int value) { - sendVariable(childId, variableType, itoa(value, convBuffer, 10)); -} - -void Sensor::sendVariable(uint8_t childId, uint8_t variableType, long value) { - sendVariable(childId, variableType, ltoa(value, convBuffer, 10)); -} - -void Sensor::sendVariable(uint8_t childId, uint8_t variableType, unsigned long value) { - sendVariable(childId, variableType, ltoa(value, convBuffer, 10)); -} - -void Sensor::sendVariable(uint8_t childId, uint8_t variableType, unsigned int value) { - sendVariable(childId, variableType, ltoa(value, convBuffer, 10)); -} - -void Sensor::sendVariable(uint8_t nodeId, uint8_t childId, uint8_t variableType, - const char *value) { - sendData(radioId, nodeId, childId, M_SET_VARIABLE, variableType, value, strlen(value), false); -} - -void Sensor::sendVariable(uint8_t nodeId, uint8_t childId, uint8_t variableType, double value, int decimals) { - sendVariable(nodeId, childId, variableType, dtostrf(value,2,decimals,convBuffer)); -} - -void Sensor::sendVariable(uint8_t nodeId, uint8_t childId, uint8_t variableType, int value) { - sendVariable(nodeId, childId, variableType, itoa(value, convBuffer, 10)); -} - -void Sensor::sendVariable(uint8_t nodeId, uint8_t childId, uint8_t variableType, long value) { - sendVariable(nodeId, childId, variableType, ltoa(value, convBuffer, 10)); -} - -void Sensor::sendVariable(uint8_t nodeId, uint8_t childId, uint8_t variableType, unsigned long value) { - sendVariable(nodeId, childId, variableType, ltoa(value, convBuffer, 10)); -} - -void Sensor::sendVariable(uint8_t nodeId, uint8_t childId, uint8_t variableType, unsigned int value) { - sendVariable(nodeId, childId, variableType, ltoa(value, convBuffer, 10)); -} - -void Sensor::sendSensorPresentation(uint8_t childId, uint8_t sensorType) { - sendData(radioId, GATEWAY_ADDRESS, childId, M_PRESENTATION, sensorType, LIBRARY_VERSION, strlen(LIBRARY_VERSION), false); -} - -void Sensor::requestStatus(uint8_t childId, uint8_t variableType) { - sendData(radioId, GATEWAY_ADDRESS, childId, M_REQ_VARIABLE, variableType, "", 0, false); -} - -void Sensor::requestStatus(uint8_t nodeId, int8_t childId, uint8_t variableType) { - sendData(radioId, nodeId, childId, M_REQ_VARIABLE, variableType, "", 0, false); -} - -void Sensor::sendSketchInfo(const char *name, const char *version) { - sendSketchName(name); - sendSketchVersion(version); -} - -void Sensor::sendSketchName(const char *name) { - if (name != NULL) - sendInternal(I_SKETCH_NAME, name); -} - -void Sensor::sendSketchVersion(const char *version) { - if (version != NULL) - sendInternal(I_SKETCH_VERSION, version); -} - -void Sensor::sendBatteryLevel(int value) { - sendInternal(I_BATTERY_LEVEL, ltoa(value, convBuffer, 10)); -} - -char* Sensor::get(uint8_t nodeId, uint8_t childId, uint8_t sendType, uint8_t receiveType, uint8_t variableType) { - while (1) { - sendData(radioId, nodeId, childId, sendType, variableType, "", 0, false); - uint8_t i = 0; - while (i < 100) { // 5 seconds timeout before re-sending status request - while (messageAvailable()) { - // Check that it is right type of message and not a routing message - if (msg.header.messageType == receiveType && - msg.header.type == variableType && - msg.header.childId == childId) { - return msg.data; - } - } - delay(50); - i++; - } - } - return NULL; -} - -char* Sensor::getStatus(uint8_t childId, uint8_t variableType) { - return get(GATEWAY_ADDRESS, childId, M_REQ_VARIABLE, M_SET_VARIABLE, variableType); -} - -char * Sensor::getStatus(uint8_t nodeId, int8_t childId, uint8_t variableType) { - return get(nodeId, childId, M_REQ_VARIABLE, M_SET_VARIABLE, variableType); -} - - - -char* Sensor::getInternal(uint8_t variableType) { - return get(GATEWAY_ADDRESS, NODE_CHILD_ID, M_INTERNAL, M_INTERNAL, variableType); -} - -unsigned long Sensor::getTime() { - return atol(getInternal(I_TIME)); -} - -void Sensor::requestTime() { - sendData(radioId, GATEWAY_ADDRESS, NODE_CHILD_ID, M_INTERNAL, I_TIME, "", 0, false); -} - - -bool Sensor::isMetricSystem() { - return getInternal(I_UNIT)[0] == 'M'; -} - -void Sensor::requestIsMetricSystem() { - sendData(radioId, GATEWAY_ADDRESS, NODE_CHILD_ID, M_INTERNAL, I_UNIT, "", 0, false); -} - - -boolean Sensor::messageAvailable() { - uint8_t pipe; - boolean available = RF24::available(&pipe); - if (available && pipe<7) { - - uint8_t len = RF24::getDynamicPayloadSize(); - RF24::read(&msg, len); - RF24::writeAckPayload(pipe,&pipe, 1 ); - - if (available) { - debug(PSTR("Message available on pipe %d\n"),pipe); - } - - uint8_t valid = validate(len-sizeof(header_s)); - boolean ok = valid == VALIDATE_OK; - - // Make sure string gets terminated ok for full sized messages. - msg.data[len - sizeof(header_s) ] = '\0'; - debug(PSTR("Rx: fr=%d,to=%d,la=%d,ci=%d,mt=%d,t=%d,cr=%d(%s): %s\n"), - msg.header.from,msg.header.to, msg.header.last, msg.header.childId, msg.header.messageType, msg.header.type, msg.header.crc, valid==0?"ok":valid==1?"ec":"ev", msg.data); - - // boolean ok = readMessage(); - if (ok && msg.header.to == radioId) { - // This message is addressed to this node - debug(PSTR("Message addressed for this node.\n")); - // Send set-message back to sender if sender wants this - if (msg.header.messageType == M_SET_WITH_ACK) { - sendVariableAck(); - // The library user should not need to care about this ack request. Just treat it as a normal SET. - msg.header.messageType = M_SET_VARIABLE; - } - // Return message to waiting sketch... - return true; - } - } - return false; -} - - -message_s Sensor::waitForMessage() { - while (1) { - if (messageAvailable()) { - return msg; - } - } -} - -message_s Sensor::getMessage() { - return msg; -} - - -/*boolean Sensor::readMessage() { - uint8_t len = RF24::getDynamicPayloadSize(); - RF24::read(&msg, len); - - uint8_t valid = validate(len-sizeof(header_s)); - boolean ok = valid == VALIDATE_OK; - - // Make sure string gets terminated ok for full sized messages. - msg.data[len - sizeof(header_s) ] = '\0'; - debug(PSTR("Rx: fr=%d,to=%d,la=%d,ci=%d,mt=%d,t=%d,cr=%d(%s): %s\n"), - msg.header.from,msg.header.to, msg.header.last, msg.header.childId, msg.header.messageType, msg.header.type, msg.header.crc, valid==0?"ok":valid==1?"ec":"ev", msg.data); - return ok; -}*/ - -/* - * calculate CRC8 on message_s data taking care of data structure and protocol version - */ -uint8_t Sensor::crc8Message(message_s var_msg, uint8_t len) { - uint8_t crc = 0x00; - uint8_t loop_count; - uint8_t bit_counter; - uint8_t data; - uint8_t feedback_bit; - uint8_t number_of_bytes_to_read = (uint8_t)sizeof(var_msg); - - // Must set crc to a constant value. - var_msg.header.crc = 0; - - // fill unused space by zeroes for string data only - if(len>=0 && len < sizeof(var_msg.data)-1) { - memset(&var_msg.data[len], 0, sizeof(var_msg.data) - 1 - len); - } - - - for (loop_count = 0; loop_count != number_of_bytes_to_read; loop_count++) - { - data = ((uint8_t*)&var_msg)[loop_count]; - - bit_counter = 8; - do { - feedback_bit = (crc ^ data) & 0x01; - - if ( feedback_bit == 0x01 ) { - crc = crc ^ 0x18; //0X18 = X^8+X^5+X^4+X^0 - } - crc = (crc >> 1) & 0x7F; - if ( feedback_bit == 0x01 ) { - crc = crc | 0x80; - } - - data = data >> 1; - bit_counter--; - - } while (bit_counter > 0); - } - return crc; -} - - -uint8_t Sensor::validate(uint8_t length) { - uint8_t oldCrc = msg.header.crc; - uint8_t newCrc = crc8Message(msg, length); - - if(!(msg.header.version == PROTOCOL_VERSION)) return VALIDATE_BAD_VERSION; - - if(!(oldCrc == newCrc)) return VALIDATE_BAD_CRC; - return VALIDATE_OK; -} - -#ifdef DEBUG -void Sensor::debugPrint(const char *fmt, ... ) { - char fmtBuffer[300]; - if (radioId == GATEWAY_ADDRESS) { - // prepend debug message to be handled correctly by gw - Serial.print("0;0;4;11;"); - } - va_list args; - va_start (args, fmt ); - vsnprintf_P(fmtBuffer, 300, fmt, args); - va_end (args); - if (radioId == GATEWAY_ADDRESS) { - // Truncate message if this is gateway node - vsnprintf_P(fmtBuffer, 60, fmt, args); - fmtBuffer[59] = '\n'; - fmtBuffer[60] = '\0'; - } else { - vsnprintf_P(fmtBuffer, 299, fmt, args); - } - va_end (args); - Serial.print(fmtBuffer); - - - //Serial.print("0;0;4;11;Mem free:"); - //Serial.println(freeRam()); -} -#endif - - -#ifdef DEBUG -int Sensor::freeRam (void) { - extern int __heap_start, *__brkval; - int v; - return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); -} -#endif diff --git a/libraries/MySensors/Sensor.h b/libraries/MySensors/Sensor.h deleted file mode 100644 index d661bc5..0000000 --- a/libraries/MySensors/Sensor.h +++ /dev/null @@ -1,298 +0,0 @@ -/* - The MySensors library adds a new layer on top of the RF24 library. - It handles radio network routing, relaying and ids. - - Created by Henrik Ekblad - - 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 Sensor_h -#define Sensor_h - -#include "Config.h" -#include "Version.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef DEBUG -#define debug(x,...) debugPrint(x, ##__VA_ARGS__) -#else -#define debug(x,...) -#endif - -#define PROTOCOL_VERSION 1 -#define BAUD_RATE 115200 - -#define AUTO 0xFF // 0-254. Id 255 is reserved for auto initialization of radioId. -#define NODE_CHILD_ID 0xFF // Node child id is always created for when a new sensor is detected -#define EEPROM_RADIO_ID_ADDRESS 0 // Where to store radio id in EEPROM -#define EEPROM_RELAY_ID_ADDRESS 1 // Where to store relay id in EEPROM -#define EEPROM_DISTANCE_ADDRESS 2 // Where to store distance to gateway in EEPROM - -// This is the radioId for sensor net gateway receiver sketch (where all sensors should send their data). -// This is also act as base value for sensor radioId -#define BASE_RADIO_ID ((uint64_t)0xA8A8E1FC00LL) -#define GATEWAY_ADDRESS ((uint8_t)0) -#define BROADCAST_ADDRESS ((uint8_t)0xFF) -#define TO_ADDR(x) (BASE_RADIO_ID + x) - -#define WRITE_PIPE ((uint8_t)0) -#define CURRENT_NODE_PIPE ((uint8_t)1) -#define BROADCAST_PIPE ((uint8_t)2) - -#define FIND_RELAY_RETRIES 2 -#define FIND_RELAY_AFTER_FAILED_TRANSMISSIONS 20 - - -#define MAX_MESSAGE_LENGTH 32 - -// Message types -typedef enum { - M_PRESENTATION = 0, - M_SET_VARIABLE = 1, - M_REQ_VARIABLE = 2, - M_SET_WITH_ACK = 3, - M_INTERNAL = 4 -} messageType; - -// Sensor variables that can be used in sketches -typedef enum { - V_TEMP,V_HUM, V_LIGHT, V_DIMMER, V_PRESSURE, V_FORECAST, V_RAIN, - V_RAINRATE, V_WIND, V_GUST, V_DIRECTION, V_UV, V_WEIGHT, V_DISTANCE, - V_IMPEDANCE, V_ARMED, V_TRIPPED, V_WATT, V_KWH, V_SCENE_ON, V_SCENE_OFF, - V_HEATER, V_HEATER_SW, V_LIGHT_LEVEL, V_VAR1, V_VAR2, V_VAR3, V_VAR4, V_VAR5, - V_UP, V_DOWN, V_STOP, V_IR_SEND, V_IR_RECEIVE, V_FLOW, V_VOLUME, V_LOCK_STATUS -} variableType; - -// Internal messages -typedef enum { - I_BATTERY_LEVEL, I_BATTERY_DATE, I_LAST_TRIP, I_TIME, I_VERSION, I_REQUEST_ID, - I_INCLUSION_MODE, I_RELAY_NODE, I_LAST_UPDATE, I_PING, I_PING_ACK, - I_LOG_MESSAGE, I_CHILDREN, I_UNIT, I_SKETCH_NAME, I_SKETCH_VERSION -} internalMessageType; - -// Sensor types -typedef enum { - S_DOOR, S_MOTION, S_SMOKE, S_LIGHT, S_DIMMER, S_COVER, S_TEMP, S_HUM, S_BARO, S_WIND, - S_RAIN, S_UV, S_WEIGHT, S_POWER, S_HEATER, S_DISTANCE, S_LIGHT_LEVEL, S_ARDUINO_NODE, - S_ARDUINO_RELAY, S_LOCK, S_IR, S_WATER -} sensor; - -// Possible return values by validate() when doing crc check of received message. -enum { - VALIDATE_OK, VALIDATE_BAD_CRC, VALIDATE_BAD_VERSION -}; - -// The message structure -typedef struct { - uint8_t crc : 8; // 8 bits crc - uint8_t version : 3; // 3 bits protocol version - uint8_t binary : 1; // 1 bit. Data is binary and should be encoded when sent to sensor net gateway - uint8_t from : 8; // 8 bits. RadioId of sender node - uint8_t to : 8; // 8 bits. RadioId of destination node - uint8_t last : 8; // 8 bits. RadioId of last node this message passed - uint8_t childId : 8; // 1 byte. Up to MAX_CHILD_DEVICES child sensors per radioId - uint8_t messageType : 4; // 4 bits. Type of message. See messageType - - uint8_t type : 8; // 8 bits. variableType or deviceType depending on messageType -} header_s; - -typedef struct { - header_s header; - char data[MAX_MESSAGE_LENGTH - sizeof(header_s) + 1]; // Each message can transfer a payload. Add one extra byte for \0 -} message_s; - - -class Sensor : public RF24 -{ - public: - /** - * Constructor - * - * Creates a new instance of Sensor class. - * - * @param _cepin The pin attached to RF24 Chip Enable on the RF module (defualt 9) - * @param _cspin The pin attached to RF24 Chip Select (default 10) - */ - Sensor(uint8_t _cepin=9, uint8_t _cspin=10); - - /** - * Begin operation of the MySensors library - * - * Call this in setup(), before calling any other sensor net library methods. - * @param radioId The unique id (1-254) for this sensor. Default 255 (auto mode). - * @param paLevel Radio PA Level for this sensor. Default RF24_PA_MAX - * @param channel Radio channel. Default is channel 76 - * @param dataRate Radio transmission speed. Default RF24_2MBPS - */ - void begin(uint8_t radioId=AUTO, rf24_pa_dbm_e paLevel=RF24_PA_LEVEL, uint8_t channel=RF24_CHANNEL, rf24_datarate_e dataRate=RF24_DATARATE); - - uint8_t getRadioId(); - - /** - * The arduino node must send a presentation of all the sensors connected before any - * variable changes will be registered on sensor net gateway side. - * Usually it's good to present all sensors when arduino starts up (setup). - * Note that gateway must be in include mode to register new devices. - * - * @param childId The unique child id for the different sensors connected to this arduino. 0-254. - * @param sensorType Sensor types to create. They will be numbered from 0 up to 127. - */ - void sendSensorPresentation(uint8_t childId, uint8_t sensorType); - - - /** - * Sends a variable change to sensor net gateway (or another sensor if radioId is specified) - * - * @param radioId The radioId of other node in radio network (used for communicating between sensors) - * @param childId The child id for which to update variable. Value can be 0-254. - * @param variableType The variableType to update - * @param value New value of the variable - */ - - void sendVariable(uint8_t childId, uint8_t variableType, const char *value); - void sendVariable(uint8_t childId, uint8_t variableType, double value, int decimals); - void sendVariable(uint8_t childId, uint8_t variableType, unsigned long value); - void sendVariable(uint8_t childId, uint8_t variableType, long value); - void sendVariable(uint8_t childId, uint8_t variableType, unsigned int value); - void sendVariable(uint8_t childId, uint8_t variableType, int value); - - void sendVariable(uint8_t radioId, uint8_t childId, uint8_t variableType, const char *value); - void sendVariable(uint8_t radioId, uint8_t childId, uint8_t variableType, double value, int decimals); - void sendVariable(uint8_t radioId, uint8_t childId, uint8_t variableType, unsigned long value); - void sendVariable(uint8_t radioId, uint8_t childId, uint8_t variableType, long value); - void sendVariable(uint8_t radioId, uint8_t childId, uint8_t variableType, unsigned int value); - void sendVariable(uint8_t radioId, uint8_t childId, uint8_t variableType, int value); - - /** - * Sends sketch info to sensor net gateway for this radio node. - * @param name String containing a short Sketch name - * @param version String containing a short Sketch version - */ - void sendSketchInfo(const char *name, const char *version); - - /** - * Sends sketch name to sensor net gateway for this radio node. - * @param info String containing a short Sketch name and version - */ - void sendSketchName(const char *name); - - /** - * Sends sketch version to sensor net gateway for this radio node. - * @param info String containing a short Sketch name and version - */ - void sendSketchVersion(const char *version); - - /** - * Sends battery status to sensor net gateway for this radio node. - * @param value Set a value between 0-100(%) - */ - void sendBatteryLevel(int value); - - /** - * Requests a variable value from sensor net gateway (or another sensor). This method will not wait for an answer. - * You should use mesageAvailable/getMessage to pick up the response. - * - * @param radioId The radioId of other node in radio network (used when fetching from other node) - * @param childId The unique child id for the different sensors connected to this arduino. 0-254. - * @param variableType The variableType to fetch - */ - void requestStatus(uint8_t childId, uint8_t variableType); - void requestStatus(uint8_t radioId, int8_t childId, uint8_t variableType); - - /** - * Requests status for a sensor variable from sensor net gateway. Waits until message arrives. - * - * @param radioId The radioId of other node in radio network (used when fetching from other node) - * @param childId The unique child id for the different sensors connected to this arduino. 0-254. - * @param variableType The variableType to fetch - * @return The variable value - */ - char *getStatus(uint8_t childId, uint8_t variableType); - char *getStatus(uint8_t radioId, int8_t childId, uint8_t variableType); - - - /** - * Fetches time from sensor net gateway - */ - void requestTime(); - unsigned long getTime(); - - /** - * Fetches unit setting from sensor net gateway. Returns true if metric system has been selected which means - * that sensor should report it's information in: celsius, meter, cm, gram, km/h, m/s etc.. - * If false is returned the sensor should report data in imperial system which means - * fahrenheit, feet, gallon, mph etc... - */ - void requestIsMetricSystem(); - bool isMetricSystem(); - - - /** - * Busy waits until there is a message for this node available to be read - */ - message_s waitForMessage(void); - - /** - * Returns true if there is a message addressed for this node is available to be read - */ - boolean messageAvailable(void); - - /** - * Returns the last received message - */ - message_s getMessage(void); - - /** - * Validates consistency of the message including CRC and protocol version - */ - uint8_t validate(uint8_t length); - - -#ifdef DEBUG - void debugPrint(const char *fmt, ... ); - int freeRam(); -#endif - - - protected: - uint8_t failedTransmissions; - boolean isRelay; - uint8_t radioId; - uint8_t distance; // This nodes distance to sensor net gateway (number of hops) - uint8_t relayId; - message_s msg; // Buffer for incoming messages. - char convBuffer[20]; - - void setupRadio(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e dataRate); - void findRelay(); - boolean send(message_s message, int length); - boolean sendWrite(uint8_t dest, message_s message, int length); - //boolean readMessage(); - void buildMsg(uint8_t from, uint8_t to, uint8_t childId, uint8_t messageType, uint8_t type, const char *data, uint8_t length, boolean binary); - void sendInternal(uint8_t variableType, const char *value); - boolean sendVariableAck(); - boolean sendData(uint8_t from, uint8_t to, uint8_t childId, uint8_t messageType, uint8_t type, const char *data, uint8_t length, boolean binaryMessage); - - - - private: - message_s ack; // Buffer for ack messages. - - void initializeRadioId(); - uint8_t crc8Message(message_s, uint8_t length); - char* get(uint8_t nodeId, uint8_t childId, uint8_t sendType, uint8_t receiveType, uint8_t variableType); - char *getInternal(uint8_t variableType); -}; - -#endif diff --git a/libraries/MySensors/examples/RelayActuator/RelayActuator.ino b/libraries/MySensors/examples/RelayActuator/RelayActuator.ino index 47c73b7..e8a44c0 100644 --- a/libraries/MySensors/examples/RelayActuator/RelayActuator.ino +++ b/libraries/MySensors/examples/RelayActuator/RelayActuator.ino @@ -1,27 +1,31 @@ // Example sketch showing how to control physical relays. -#include +#include +#include +#include #include #include #include + #define RELAY_1 3 // Arduino Digital I/O pin number for first relay (second on pin+1 etc) #define NUMBER_OF_RELAYS 1 // Total number of attached relays #define RELAY_ON 1 // GPIO value to write to turn on attached relay #define RELAY_OFF 0 // GPIO value to write to turn off attached relay -Relay gw; +MySensor gw; void setup() -{ - gw.begin(); +{ + // Initialize library and add callback for incoming messages + gw.begin(incomingMessage); // Send the sketch version information to the gateway and Controller - gw.sendSketchInfo("Relay", "1.0"); + gw.sketchInfo("Relay", "1.0"); // Register all sensors to gw (they will be created as child devices) for (int i=0; i