* Converted time example. Callback for time request was the only clean solution. Now global callback for incoming messages only needs to care about SET commands coming from other nodes or gateway.

* New api for storing states. RelayActator now stores state in eeprom, making it more standalone.
* Controller config is now fetched in the background without needing any special tratment in sketch. Last received config is stored in eeprom:
This commit is contained in:
Henrik Ekblad
2014-05-16 14:48:18 +02:00
parent 9291a97d45
commit 891c826a0c
7 changed files with 270 additions and 256 deletions
+2 -8
View File
@@ -41,8 +41,8 @@ typedef enum {
// Type of internal messages (for internal messages) // Type of internal messages (for internal messages)
typedef enum { typedef enum {
I_BATTERY_LEVEL, I_TIME, I_VERSION, I_REQUEST_ID, I_BATTERY_LEVEL, I_TIME, I_VERSION, I_REQUEST_ID,
I_INCLUSION_MODE, I_RELAY_NODE, I_PING, I_PING_ACK, I_INCLUSION_MODE, I_CONFIG, I_PING, I_PING_ACK,
I_LOG_MESSAGE, I_CHILDREN, I_CONFIG, I_SKETCH_NAME, I_SKETCH_VERSION I_LOG_MESSAGE, I_CHILDREN, I_SKETCH_NAME, I_SKETCH_VERSION
} internal; } internal;
// Type of sensor (for presentation message) // Type of sensor (for presentation message)
@@ -99,12 +99,6 @@ typedef enum {
#define miSetPayloadType(_pt) BF_SET(command_payload, _pt, 4, 4) #define miSetPayloadType(_pt) BF_SET(command_payload, _pt, 4, 4)
// Check and fetch data from special messages
#define isTimeResponse(_msg) (mGetCommand(_msg)==C_INTERNAL && _msg.type==I_TIME)
#define isConfigResponse(_msg) (mGetCommand(_msg)==C_INTERNAL && _msg.type==I_CONFIG)
#define mIsMetric(_msg) (_msg.getByte() == 'M')
class MyMessage class MyMessage
{ {
public: public:
+116 -95
View File
@@ -11,7 +11,7 @@
#include "MySensor.h" #include "MySensor.h"
// Inline and macros // Inline function and macros
inline MyMessage build (MyMessage msg, uint8_t sender, uint8_t destination, uint8_t sensor, uint8_t command, uint8_t type) { inline MyMessage build (MyMessage msg, uint8_t sender, uint8_t destination, uint8_t sensor, uint8_t command, uint8_t type) {
msg.sender = sender; msg.sender = sender;
msg.destination = destination; msg.destination = destination;
@@ -61,8 +61,8 @@ void MySensor::setupRadio(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_
} }
void MySensor::setupRelayMode(){ void MySensor::setupRelayMode(){
s.childNodeTable = new uint8_t[256]; childNodeTable = new uint8_t[256];
eeprom_read_block((void*)s.childNodeTable, (void*)EEPROM_ROUTES_ADDRESS, 256); eeprom_read_block((void*)childNodeTable, (void*)EEPROM_ROUTES_ADDRESS, 256);
} }
void MySensor::begin(void (*_msgCallback)(MyMessage), boolean _relayMode, uint8_t _nodeId, rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e dataRate) { void MySensor::begin(void (*_msgCallback)(MyMessage), boolean _relayMode, uint8_t _nodeId, rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e dataRate) {
@@ -76,61 +76,72 @@ void MySensor::begin(void (*_msgCallback)(MyMessage), boolean _relayMode, uint8_
setupRadio(paLevel, channel, dataRate); setupRadio(paLevel, channel, dataRate);
// Read settings from EEPROM // Read settings from EEPROM
eeprom_read_block((void*)&s, (void*)EEPROM_START, 3*sizeof(uint8_t)); eeprom_read_block((void*)&nc, (void*)EEPROM_NODE_ID_ADDRESS, sizeof(NodeConfig));
// Read latest received controller configuration from EEPROM
eeprom_read_block((void*)&cc, (void*)EEPROM_LOCAL_CONFIG_ADDRESS, sizeof(ControllerConfig));
if (_nodeId != AUTO) { if (_nodeId != AUTO) {
// Set static id // Set static id
s.nodeId = _nodeId; nc.nodeId = _nodeId;
} }
// If no parent was found. Try to find one. // If no parent was found. Try to find one.
if (s.parentNodeId == 0xff) { if (nc.parentNodeId == 0xff) {
findParentNode(); findParentNode();
} }
// Try to fetch node-id from gateway // Try to fetch node-id from gateway
if (s.nodeId == AUTO) { if (nc.nodeId == AUTO) {
requestNodeId(); requestNodeId();
} }
debug(PSTR("%s started\n"), relayMode?"relay":"sensor"); debug(PSTR("%s started\n"), relayMode?"relay":"sensor");
// Open reading pipe for messages directed to this node (set write pipe to same) // Open reading pipe for messages directed to this node (set write pipe to same)
RF24::openReadingPipe(WRITE_PIPE, TO_ADDR(s.nodeId)); RF24::openReadingPipe(WRITE_PIPE, TO_ADDR(nc.nodeId));
RF24::openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(s.nodeId)); RF24::openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(nc.nodeId));
// Send presentation for this radio node // Send presentation for this radio node (attach
present(NODE_CHILD_ID, relayMode? S_ARDUINO_RELAY : S_ARDUINO_NODE); present(NODE_SENSOR_ID, relayMode? S_ARDUINO_RELAY : S_ARDUINO_NODE);
// Send parent information back to sensor net gateway. // Send a configuration exchange request to controller
buildSendPayload(msg, s.nodeId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, I_RELAY_NODE, s.parentNodeId); // Node sends parent node. Controller answers with latest node configuration
// which is picked up in process()
buildSendPayload(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_CONFIG, nc.parentNodeId);
} }
uint8_t MySensor::getNodeId() { uint8_t MySensor::getNodeId() {
return s.nodeId; return nc.nodeId;
}
ControllerConfig MySensor::getConfig() {
return cc;
} }
void MySensor::requestNodeId() { void MySensor::requestNodeId() {
debug(PSTR("req node id\n")); debug(PSTR("req node id\n"));
RF24::openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(s.nodeId)); RF24::openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(nc.nodeId));
buildSend(msg, s.nodeId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, I_REQUEST_ID); buildSend(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_REQUEST_ID);
} }
void MySensor::findParentNode() { void MySensor::findParentNode() {
if (s.nodeId == GATEWAY_ADDRESS) if (nc.nodeId == GATEWAY_ADDRESS)
return; // Gateway has no business here! return; // Gateway has no business here!
failedTransmissions = 0; failedTransmissions = 0;
// Open a reading pipe for current nodeId (if it differs from broadcast) // Open a reading pipe for current nodeId (if it differs from broadcast)
if (s.nodeId != BROADCAST_PIPE) { if (nc.nodeId != BROADCAST_PIPE) {
RF24::openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(s.nodeId)); RF24::openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(nc.nodeId));
} }
// Set distance to max // Set distance to max
s.distance = 255; nc.distance = 255;
// Send ping message to BROADCAST_ADDRESS (to which all relaying nodes and gateway listens and should reply to) // Send ping message to BROADCAST_ADDRESS (to which all relaying nodes and gateway listens and should reply to)
build(msg, s.nodeId, BROADCAST_ADDRESS, NODE_CHILD_ID, C_INTERNAL, I_PING); build(msg, nc.nodeId, BROADCAST_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_PING);
sendWrite(BROADCAST_ADDRESS, msg, true); sendWrite(BROADCAST_ADDRESS, msg, true);
unsigned long enter = millis(); unsigned long enter = millis();
@@ -146,7 +157,7 @@ boolean MySensor::sendRoute(MyMessage message) {
process(); process();
// If we still don't have any node id, re-request and skip this message. // If we still don't have any node id, re-request and skip this message.
if (s.nodeId == AUTO && !(mGetCommand(message) == C_INTERNAL && message.type == I_REQUEST_ID)) { if (nc.nodeId == AUTO && !(mGetCommand(message) == C_INTERNAL && message.type == I_REQUEST_ID)) {
requestNodeId(); requestNodeId();
return false; return false;
} }
@@ -162,15 +173,15 @@ boolean MySensor::sendRoute(MyMessage message) {
} }
} }
if (s.nodeId != GATEWAY_ADDRESS) { if (nc.nodeId != GATEWAY_ADDRESS) {
debug(PSTR("route parent\n")); debug(PSTR("route parent\n"));
// Should be routed back to gateway. // Should be routed back to gateway.
bool ok = sendWrite(s.parentNodeId, message); bool ok = sendWrite(nc.parentNodeId, message);
if (!ok) { if (!ok) {
// Failure when sending to parent node. The parent node might be down and we // Failure when sending to parent node. The parent node might be down and we
// need to find another route to gateway. Max 20 retries before giving up. // need to find another route to gateway.
if (failedTransmissions > FIND_RELAY_AFTER_FAILED_TRANSMISSIONS) { if (failedTransmissions > SEARCH_FAILURES) {
findParentNode(); findParentNode();
} }
failedTransmissions++; failedTransmissions++;
@@ -184,7 +195,7 @@ boolean MySensor::sendRoute(MyMessage message) {
boolean MySensor::sendWrite(uint8_t next, MyMessage message, bool broadcast) { boolean MySensor::sendWrite(uint8_t next, MyMessage message, bool broadcast) {
uint8_t length = mGetLength(message); uint8_t length = mGetLength(message);
message.last = s.nodeId; message.last = nc.nodeId;
mSetVersion(message, PROTOCOL_VERSION); mSetVersion(message, PROTOCOL_VERSION);
message.crc = crc8Message(message); message.crc = crc8Message(message);
@@ -204,7 +215,7 @@ boolean MySensor::sendWrite(uint8_t next, MyMessage message, bool broadcast) {
bool MySensor::send(MyMessage message, bool enableAck) { bool MySensor::send(MyMessage message, bool enableAck) {
message.sender = s.nodeId; message.sender = nc.nodeId;
if (enableAck) { if (enableAck) {
mSetCommand(message,C_SET_WITH_ACK); mSetCommand(message,C_SET_WITH_ACK);
} else { } else {
@@ -214,33 +225,31 @@ bool MySensor::send(MyMessage message, bool enableAck) {
} }
void MySensor::sendBatteryLevel(uint8_t value) { void MySensor::sendBatteryLevel(uint8_t value) {
buildSendPayload(msg, s.nodeId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, I_BATTERY_LEVEL, value); buildSendPayload(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_BATTERY_LEVEL, value);
} }
void MySensor::present(uint8_t childSensorId, uint8_t sensorType) { void MySensor::present(uint8_t childSensorId, uint8_t sensorType) {
buildSendPayload(msg, s.nodeId, GATEWAY_ADDRESS, childSensorId, C_PRESENTATION, sensorType, LIBRARY_VERSION); buildSendPayload(msg, nc.nodeId, GATEWAY_ADDRESS, childSensorId, C_PRESENTATION, sensorType, LIBRARY_VERSION);
} }
void MySensor::sketchInfo(const char *name, const char *version) { void MySensor::sendSketchInfo(const char *name, const char *version) {
if (name != NULL) { if (name != NULL) {
buildSendPayload(msg, s.nodeId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, I_SKETCH_NAME, name); buildSendPayload(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_SKETCH_NAME, name);
} }
if (version != NULL) { if (version != NULL) {
buildSendPayload(msg, s.nodeId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, I_SKETCH_VERSION, version); buildSendPayload(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_SKETCH_VERSION, version);
} }
} }
void MySensor::request(uint8_t childSensorId, uint8_t variableType, uint8_t destination) { void MySensor::request(uint8_t childSensorId, uint8_t variableType, uint8_t destination) {
buildSend(msg, s.nodeId, destination, childSensorId, C_REQ, variableType); buildSend(msg, nc.nodeId, destination, childSensorId, C_REQ, variableType);
} }
void MySensor::requestTime() { void MySensor::requestTime(void (* _timeCallback)(unsigned long)) {
buildSend(msg, s.nodeId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, I_TIME); timeCallback = _timeCallback;
buildSend(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_TIME);
} }
void MySensor::requestConfig() {
buildSend(msg, s.nodeId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, I_CONFIG);
}
boolean MySensor::process() { boolean MySensor::process() {
uint8_t pipe; uint8_t pipe;
@@ -266,7 +275,7 @@ boolean MySensor::process() {
uint8_t command = mGetCommand(msg); uint8_t command = mGetCommand(msg);
uint8_t type = msg.type; uint8_t type = msg.type;
uint8_t sender = msg.sender; //getSender(); uint8_t sender = msg.sender;
uint8_t last = msg.last; uint8_t last = msg.last;
uint8_t destination = msg.destination; uint8_t destination = msg.destination;
@@ -275,15 +284,13 @@ boolean MySensor::process() {
// Wait a random delay of 0-2 seconds to minimize collision // Wait a random delay of 0-2 seconds to minimize collision
// between ping ack messages from other relaying nodes // between ping ack messages from other relaying nodes
delay(millis() & 0x3ff); delay(millis() & 0x3ff);
build(msg, nc.nodeId, sender, NODE_SENSOR_ID, C_INTERNAL, I_PING_ACK);
build(msg, s.nodeId, sender, NODE_CHILD_ID, C_INTERNAL, I_PING_ACK); msg.set(nc.distance);
msg.set(s.distance);
sendWrite(sender, msg); sendWrite(sender, msg);
return false; return false;
} else if (destination == s.nodeId) { } else if (destination == nc.nodeId) {
// This message is addressed to this node // This message is addressed to this node
if (relayMode && last != nc.parentNodeId) {
if (relayMode && last != s.parentNodeId) {
// Message is from one of the child nodes. Add it to routing table. // Message is from one of the child nodes. Add it to routing table.
addChildRoute(sender, last); addChildRoute(sender, last);
} }
@@ -292,62 +299,68 @@ boolean MySensor::process() {
if (type == I_PING_ACK) { if (type == I_PING_ACK) {
// We've received a reply to our PING message looking for parent. // We've received a reply to our PING message looking for parent.
uint8_t distance = msg.getByte(); uint8_t distance = msg.getByte();
if (distance<s.distance-1) { if (distance<nc.distance-1) {
// Found a neighbor closer to GW than previously found // Found a neighbor closer to GW than previously found
s.distance = distance + 1; nc.distance = distance + 1;
s.parentNodeId = msg.sender; nc.parentNodeId = msg.sender;
eeprom_write_byte((uint8_t*)EEPROM_PARENT_NODE_ID_ADDRESS, s.parentNodeId); eeprom_write_byte((uint8_t*)EEPROM_PARENT_NODE_ID_ADDRESS, nc.parentNodeId);
eeprom_write_byte((uint8_t*)EEPROM_DISTANCE_ADDRESS, s.distance); eeprom_write_byte((uint8_t*)EEPROM_DISTANCE_ADDRESS, nc.distance);
debug(PSTR("p=%d, d=%d\n"), s.parentNodeId, s.distance); debug(PSTR("p=%d, d=%d\n"), nc.parentNodeId, nc.distance);
} }
return false; return false;
} else if (type == I_REQUEST_ID && sender == GATEWAY_ADDRESS) { } else if (sender == GATEWAY_ADDRESS) {
if (s.nodeId == AUTO) { bool isMetric;
s.nodeId = msg.getByte();
// Write id to EEPROM switch (type) {
if (s.nodeId == AUTO) { case I_REQUEST_ID:
// sensor net gateway will return max id if all sensor id are taken if (nc.nodeId == AUTO) {
debug(PSTR("full\n")); nc.nodeId = msg.getByte();
while (1); // Wait here. Nothing else we can do... // Write id to EEPROM
} else { if (nc.nodeId == AUTO) {
RF24::openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(s.nodeId)); // sensor net gateway will return max id if all sensor id are taken
eeprom_write_byte((uint8_t*)EEPROM_NODE_ID_ADDRESS, s.nodeId); debug(PSTR("full\n"));
} while (1); // Wait here. Nothing else we can do...
debug(PSTR("id=%d\n"), s.nodeId); } else {
} RF24::openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(nc.nodeId));
return false; eeprom_write_byte((uint8_t*)EEPROM_NODE_ID_ADDRESS, nc.nodeId);
} else if (relayMode && sender == GATEWAY_ADDRESS) {
if (type == I_RELAY_NODE && destination != GATEWAY_ADDRESS) {
// Gatewa/controller wants this node to refresh its parent node
findParentNode();
// Send relay information back to sensor net gateway node device.
buildSendPayload(msg,s.nodeId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, I_RELAY_NODE,s.parentNodeId);
return false;
} else if (type == I_CHILDREN) {
if (msg.getByte() == 'F') {
// TODO: We should send in as many children we can fit in a message (binary)
// For now we only print routing table for this node.
for (uint8_t i=0;i< 50; i++) {
debug(PSTR("rd=%d, %d\n"), i, getChildRoute(i) );
} }
buildSendPayload(msg, s.nodeId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, I_CHILDREN, "n/a"); debug(PSTR("id=%d\n"), nc.nodeId);
} else if (msg.getByte() == 'C') { }
break;
case I_CONFIG:
// Pick up configuration from controller (currently only metric/imperial)
// and store it in eeprom if changed
isMetric = msg.getByte() == 'M' ;
if (cc.isMetric != isMetric) {
cc.isMetric = isMetric;
eeprom_write_byte((uint8_t*)EEPROM_CONTROLLER_CONFIG_ADDRESS, isMetric);
//eeprom_write_block((const void*)&cc, (uint8_t*)EEPROM_CONTROLLER_CONFIG_ADDRESS, sizeof(ControllerConfig));
}
break;
case I_CHILDREN:
if (relayMode && msg.getByte() == 'C') {
// Clears child relay data for this node // Clears child relay data for this node
debug(PSTR("rd=clear\n")); debug(PSTR("rd=clear\n"));
for (uint8_t i=0;i< sizeof(s.childNodeTable); i++) { for (uint8_t i=0;i< sizeof(childNodeTable); i++) {
removeChildRoute(i); removeChildRoute(i);
} }
buildSendPayload(msg, s.nodeId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, I_CHILDREN,""); buildSendPayload(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_CHILDREN,"");
}
break;
case I_TIME:
if (timeCallback != NULL) {
// Deliver time to callback
timeCallback(msg.getULong());
} }
return false;
} }
return false;
} }
} }
// Check if sender requests an ack back. // Check if sender requests an ack back.
if (command == C_SET_WITH_ACK) { if (command == C_SET_WITH_ACK) {
ack = msg; ack = msg;
mSetCommand(ack,C_SET); mSetCommand(ack,C_SET);
ack.sender = s.nodeId; ack.sender = nc.nodeId;
ack.destination = msg.sender; ack.destination = msg.sender;
sendRoute(ack); sendRoute(ack);
// The library user should not need to care about this ack request. Just treat it as a normal SET. // The library user should not need to care about this ack request. Just treat it as a normal SET.
@@ -389,7 +402,7 @@ boolean MySensor::process() {
// Message should be passed to node A (this nodes relay) // Message should be passed to node A (this nodes relay)
// This message should be routed back towards sensor net gateway // This message should be routed back towards sensor net gateway
sendWrite(s.parentNodeId, msg); sendWrite(nc.parentNodeId, msg);
// Add this child to our "routing table" if it not already exist // Add this child to our "routing table" if it not already exist
addChildRoute(sender, last); addChildRoute(sender, last);
} else { } else {
@@ -411,7 +424,7 @@ boolean MySensor::process() {
// THIS SHOULD NOT HAPPEN ANY MORE // THIS SHOULD NOT HAPPEN ANY MORE
/*if (s.nodeId == GATEWAY_ADDRESS) { /*if (nc.nodeId == GATEWAY_ADDRESS) {
// We should perhaps inform gateway about the no-route situation?! // We should perhaps inform gateway about the no-route situation?!
debug(PSTR("ro=?\n")); debug(PSTR("ro=?\n"));
@@ -489,31 +502,39 @@ uint8_t MySensor::validate(MyMessage message) {
return VALIDATE_OK; return VALIDATE_OK;
} }
void MySensor::saveState(uint8_t pos, uint8_t value) {
if (loadState(pos) != value) {
eeprom_write_byte((uint8_t*)EEPROM_LOCAL_CONFIG_ADDRESS+pos, value);
}
}
uint8_t MySensor::loadState(uint8_t pos) {
return eeprom_read_byte((uint8_t*)EEPROM_LOCAL_CONFIG_ADDRESS+pos);
}
void MySensor::addChildRoute(uint8_t childId, uint8_t route) { void MySensor::addChildRoute(uint8_t childId, uint8_t route) {
if (s.childNodeTable[childId] != route) { if (childNodeTable[childId] != route) {
s.childNodeTable[childId] = route; childNodeTable[childId] = route;
eeprom_write_byte((uint8_t*)EEPROM_ROUTES_ADDRESS+childId, route); eeprom_write_byte((uint8_t*)EEPROM_ROUTES_ADDRESS+childId, route);
} }
} }
void MySensor::removeChildRoute(uint8_t childId) { void MySensor::removeChildRoute(uint8_t childId) {
if (s.childNodeTable[childId] != 0xff) { if (childNodeTable[childId] != 0xff) {
s.childNodeTable[childId] = 0xff; childNodeTable[childId] = 0xff;
eeprom_write_byte((uint8_t*)EEPROM_ROUTES_ADDRESS+childId, 0xff); eeprom_write_byte((uint8_t*)EEPROM_ROUTES_ADDRESS+childId, 0xff);
} }
} }
uint8_t MySensor::getChildRoute(uint8_t childId) { uint8_t MySensor::getChildRoute(uint8_t childId) {
return s.childNodeTable[childId]; return childNodeTable[childId];
} }
#ifdef DEBUG #ifdef DEBUG
void MySensor::debugPrint(const char *fmt, ... ) { void MySensor::debugPrint(const char *fmt, ... ) {
char fmtBuffer[300]; char fmtBuffer[300];
if (s.nodeId == GATEWAY_ADDRESS) { if (nc.nodeId == GATEWAY_ADDRESS) {
// prepend debug message to be handled correctly by gw // prepend debug message to be handled correctly by gw
Serial.write("0;0;4;11;"); Serial.write("0;0;4;11;");
} }
@@ -521,7 +542,7 @@ void MySensor::debugPrint(const char *fmt, ... ) {
va_start (args, fmt ); va_start (args, fmt );
vsnprintf_P(fmtBuffer, 300, fmt, args); vsnprintf_P(fmtBuffer, 300, fmt, args);
va_end (args); va_end (args);
if (s.nodeId == GATEWAY_ADDRESS) { if (nc.nodeId == GATEWAY_ADDRESS) {
// Truncate message if this is gateway node // Truncate message if this is gateway node
vsnprintf_P(fmtBuffer, 60, fmt, args); vsnprintf_P(fmtBuffer, 60, fmt, args);
fmtBuffer[59] = '\n'; fmtBuffer[59] = '\n';
+64 -35
View File
@@ -34,14 +34,19 @@
#define BAUD_RATE 115200 #define BAUD_RATE 115200
#define AUTO 0xFF // 0-254. Id 255 is reserved for auto initialization of nodeId. #define AUTO 0xFF // 0-254. Id 255 is reserved for auto initialization of nodeId.
#define NODE_CHILD_ID 0xFF // Node child id is always created for when a new sensor is detected #define NODE_SENSOR_ID 0xFF // Node child id is always created for when a node
// EEPROM start address for mysensors library data
#define EEPROM_START 0 #define EEPROM_START 0
#define EEPROM_NODE_ID_ADDRESS EEPROM_START // Where to store radio id in EEPROM // EEPROM location of node id
#define EEPROM_PARENT_NODE_ID_ADDRESS (EEPROM_START+1) // Where to store relay id in EEPROM #define EEPROM_NODE_ID_ADDRESS EEPROM_START
#define EEPROM_DISTANCE_ADDRESS (EEPROM_PARENT_NODE_ID_ADDRESS+1) // Where to store distance to gateway in EEPROM // EEPROM location of parent id
#define EEPROM_PARENT_NODE_ID_ADDRESS (EEPROM_START+1)
// EEPROM location of distance to gateway
#define EEPROM_DISTANCE_ADDRESS (EEPROM_PARENT_NODE_ID_ADDRESS+1)
#define EEPROM_ROUTES_ADDRESS (EEPROM_DISTANCE_ADDRESS+1) // Where to start storing routing information in EEPROM. Will allocate 256 bytes. #define EEPROM_ROUTES_ADDRESS (EEPROM_DISTANCE_ADDRESS+1) // Where to start storing routing information in EEPROM. Will allocate 256 bytes.
#define EEPROM_CONTROLLER_CONFIG_ADDRESS (EEPROM_ROUTES_ADDRESS+256) // Location of controller sent configuration (we allow one payload of config data from controller)
#define EEPROM_LOCAL_CONFIG_ADDRESS (EEPROM_CONTROLLER_CONFIG_ADDRESS+24) // First free address for sketch static configuration
// This is the nodeId for sensor net gateway receiver sketch (where all sensors should send their data). // This is the nodeId for sensor net gateway receiver sketch (where all sensors should send their data).
// This is also act as base value for sensor nodeId // This is also act as base value for sensor nodeId
@@ -54,7 +59,20 @@
#define CURRENT_NODE_PIPE ((uint8_t)1) #define CURRENT_NODE_PIPE ((uint8_t)1)
#define BROADCAST_PIPE ((uint8_t)2) #define BROADCAST_PIPE ((uint8_t)2)
#define FIND_RELAY_AFTER_FAILED_TRANSMISSIONS 5 // Search for a new parent node after this many transmission failures
#define SEARCH_FAILURES 5
struct NodeConfig
{
uint8_t nodeId; // Current node id
uint8_t parentNodeId; // Where this node sends its messages
uint8_t distance; // This nodes distance to sensor net gateway (number of hops)
};
struct ControllerConfig {
uint8_t isMetric;
};
class MySensor : public RF24 class MySensor : public RF24
@@ -74,7 +92,7 @@ class MySensor : public RF24
* Begin operation of the MySensors library * Begin operation of the MySensors library
* *
* Call this in setup(), before calling any other sensor net library methods. * Call this in setup(), before calling any other sensor net library methods.
* @param incomingMessageCallback Callback function for incoming messages. Default is NULL * @param incomingMessageCallback Callback function for incoming messages from other nodes or controller and request responses. 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 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 nodeId The unique id (1-254) for this sensor. Default is AUTO(255) which means sensor tries to fetch an id from controller. * @param nodeId The unique id (1-254) for this sensor. Default is AUTO(255) which means sensor tries to fetch an id from controller.
* @param paLevel Radio PA Level for this sensor. Default RF24_PA_MAX * @param paLevel Radio PA Level for this sensor. Default RF24_PA_MAX
@@ -103,7 +121,7 @@ class MySensor : public RF24
* @param name String containing a short Sketch name or NULL if not applicable * @param name String containing a short Sketch name or NULL if not applicable
* @param version String containing a short Sketch version or NULL if not applicable * @param version String containing a short Sketch version or NULL if not applicable
*/ */
void sketchInfo(const char *name, const char *version); void sendSketchInfo(const char *name, const char *version);
/** /**
* Sends a message to gateway or one of the other nodes in the radio network * Sends a message to gateway or one of the other nodes in the radio network
@@ -122,30 +140,20 @@ class MySensor : public RF24
/** /**
* Requests a value from gateway or some other sensor in the radio network. * Requests a value from gateway or some other sensor in the radio network.
* Make sure to add callback in begin() to handle request response. * Make sure to add callback-method in begin-method to handle request responses.
* *
* @param childSensorId The unique child id for the different sensors connected to this arduino. 0-254. * @param childSensorId The unique child id for the different sensors connected to this arduino. 0-254.
* @param variableType The variableType to fetch * @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 nodeId of other node in radio network. Default is gateway * @param destination The nodeId of other node in radio network. Default is gateway
*/ */
void request(uint8_t childSensorId, uint8_t variableType, uint8_t destination=GATEWAY_ADDRESS); void request(uint8_t childSensorId, uint8_t variableType, uint8_t destination=GATEWAY_ADDRESS);
/** /**
* Requests time from controller. Pick up response in callback. * Requests time from controller. Answer will be delivered to callback.
* *
* @return Seconds since 1970. * @param callback for time request. Incoming argument is seconds since 1970.
*/ */
void requestTime(); void requestTime(void (* timeCallback)(unsigned long));
/**
* Requests configuration from controller. 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 requestConfig();
/** /**
@@ -153,7 +161,32 @@ class MySensor : public RF24
* Returns true if there is a message addressed for this node just was received. * Returns true if there is a message addressed for this node just was received.
* Use callback to handle incoming messages. * Use callback to handle incoming messages.
*/ */
boolean process(void); boolean process();
/**
* Returns the most recent node configuration received from controller
*/
ControllerConfig getConfig();
/**
* Save a state (in local EEPROM). Good for actuators to "remember" state between
* power cycles.
*
* You have 256 bytes to play with. Note that there is a limitation on the number
* of writes the EEPROM can handle (~100 000 cycles).
*
* @param pos The position to store value in (0-255)
* @param Value to store in position
*/
void saveState(uint8_t pos, uint8_t value);
/**
* Load a state (from local EEPROM).
*
* @param pos The position to fetch value from (0-255)
* @return Value to store in position
*/
uint8_t loadState(uint8_t pos);
/** /**
* Returns the last received message * Returns the last received message
@@ -168,31 +201,27 @@ class MySensor : public RF24
#endif #endif
protected: protected:
struct settings_t NodeConfig nc; // Essential settings for node to work
{ ControllerConfig cc; // Configuration coming from controller
uint8_t nodeId; // Current node id
uint8_t parentNodeId; // Where this node sends its messages
uint8_t distance; // This nodes distance to sensor net gateway (number of hops)
uint8_t *childNodeTable; // In memory buffer for routing to other nodes. also stored in EEPROM
} s;
MyMessage msg; // Buffer for incoming messages.
uint8_t failedTransmissions;
boolean relayMode; boolean relayMode;
void setupRelayMode(); void setupRelayMode();
void setupRadio(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e dataRate); void setupRadio(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e dataRate);
void findParentNode();
boolean sendRoute(MyMessage message); boolean sendRoute(MyMessage message);
boolean sendWrite(uint8_t dest, MyMessage message, bool broadcast=false); boolean sendWrite(uint8_t dest, MyMessage message, bool broadcast=false);
uint8_t validate(MyMessage message); uint8_t validate(MyMessage message);
private: private:
uint8_t failedTransmissions;
uint8_t *childNodeTable; // In memory buffer for routing information to other nodes. also stored in EEPROM
MyMessage msg; // Buffer for incoming messages.
MyMessage ack; // Buffer for ack messages. MyMessage ack; // Buffer for ack messages.
void (*msgCallback)(MyMessage); // Callback for undefined incoming messages void (*timeCallback)(unsigned long); // Callback for requested time messages
void (*msgCallback)(MyMessage); // Callback for incoming messages from other nodes and gateway.
void requestNodeId(); void requestNodeId();
void findParentNode();
uint8_t crc8Message(MyMessage); uint8_t crc8Message(MyMessage);
uint8_t getChildRoute(uint8_t childId); uint8_t getChildRoute(uint8_t childId);
void addChildRoute(uint8_t childId, uint8_t route); void addChildRoute(uint8_t childId, uint8_t route);
void removeChildRoute(uint8_t childId); void removeChildRoute(uint8_t childId);
@@ -25,10 +25,10 @@ void setup()
sensors.begin(); sensors.begin();
// Startup and initialize MySensors library. Set callback for incoming messages. // Startup and initialize MySensors library. Set callback for incoming messages.
gw.begin(response); gw.begin();
// Send the sketch version information to the gateway and Controller // Send the sketch version information to the gateway and Controller
gw.sketchInfo("Temperature Sensor", "1.0"); gw.sendSketchInfo("Temperature Sensor", "1.0");
// Fetch the number of attached temperature sensors // Fetch the number of attached temperature sensors
numSensors = sensors.getDeviceCount(); numSensors = sensors.getDeviceCount();
@@ -38,56 +38,39 @@ void setup()
gw.present(i, S_TEMP); gw.present(i, S_TEMP);
} }
// Request configuration from controller (use this to determine if we're supposed to send in Ceclscius or Farenheit) // Initialize outgoing message to always send temperature
gw.requestConfig();
// Initialize outgoing message to always send temperature
out.type = V_TEMP; out.type = V_TEMP;
} }
void loop() void loop()
{ {
// Process incoming messages // Process incoming messages (like config from server)
gw.process(); gw.process();
if (receivedConfig) { // Fetch temperatures from Dallas sensors
// Fetch temperatures from Dallas sensors sensors.requestTemperatures();
sensors.requestTemperatures();
// Read temperatures and send them to controller // Read temperatures and send them to controller
for (int i=0; i<numSensors && i<MAX_ATTACHED_DS18B20; i++) { for (int i=0; i<numSensors && i<MAX_ATTACHED_DS18B20; i++) {
// Fetch and round temperature to one decimal // Fetch and round temperature to one decimal
float temperature = static_cast<float>(static_cast<int>((metric?sensors.getTempCByIndex(i):sensors.getTempFByIndex(i)) * 10.)) / 10.; float temperature = static_cast<float>(static_cast<int>((gw.getConfig().isMetric?sensors.getTempCByIndex(i):sensors.getTempFByIndex(i)) * 10.)) / 10.;
// Only send data if temperature has changed and no error // Only send data if temperature has changed and no error
if (lastTemperature[i] != temperature && temperature != -127.00) { if (lastTemperature[i] != temperature && temperature != -127.00) {
// Send in the new temperature // Send in the new temperature
gw.send(out.setSensor(i).set(temperature,1)); gw.send(out.setSensor(i).set(temperature,1));
lastTemperature[i]=temperature; lastTemperature[i]=temperature;
}
} }
// Power down the radio and sleep Arduino to save batteries.
delay(500);
gw.powerDown();
sleep.pwrDownMode(); //set sleep mode
sleep.sleepDelay(SLEEP_TIME * 1000); //sleep for: sleepTime
} else {
// We're still missing the configuration. Wait a little bit and request it again.
delay(1000);
gw.requestConfig();
}
}
void response(MyMessage msg) {
// Check that received message is a configuration
if (isConfigResponse(msg)) {
// Read config from message
metric = mIsMetric(msg);
receivedConfig = true;
} }
// Power down the radio and sleep Arduino to save batteries.
delay(500);
gw.powerDown();
sleep.pwrDownMode(); //set sleep mode
sleep.sleepDelay(SLEEP_TIME * 1000); //sleep for: sleepTime
} }
@@ -1,4 +1,5 @@
// Example sketch showing how to control physical relays. // Example sketch showing how to control physical relays.
// This example will remember relay state even after power failure.
#include <MySensor.h> #include <MySensor.h>
#include <SPI.h> #include <SPI.h>
@@ -17,29 +18,23 @@ void setup()
// Initialize library and add callback for incoming messages // Initialize library and add callback for incoming messages
gw.begin(incomingMessage, false); gw.begin(incomingMessage, false);
// Send the sketch version information to the gateway and Controller // Send the sketch version information to the gateway and Controller
gw.sketchInfo("Relay", "1.0"); gw.sendSketchInfo("Relay", "1.0");
// Register all sensors to gw (they will be created as child devices)
for (int i=0; i<NUMBER_OF_RELAYS;i++) {
gw.present(RELAY_1+i, S_LIGHT);
}
// Fetch relay status // Fetch relay status
for (int i=0; i<NUMBER_OF_RELAYS;i++) { for (int sensor=0, pin=RELAY_1; sensor<=NUMBER_OF_RELAYS;sensor++, pin++) {
// Make sure relays are off when starting up // Register all sensors to gw (they will be created as child devices)
digitalWrite(RELAY_1+i, RELAY_OFF); gw.present(sensor, S_LIGHT);
// Then set relay pins in output mode // Then set relay pins in output mode
pinMode(RELAY_1+i, OUTPUT); pinMode(pin, OUTPUT);
// Set relay to last known state (using eeprom storage)
// Request and wait for relay status from controller digitalWrite(pin, gw.loadState(sensor));
gw.request(RELAY_1+i, V_LIGHT);
} }
} }
void loop() void loop()
{ {
// Process incoming messages // Alway process incoming messages whenever possible
gw.process(); gw.process();
} }
@@ -48,6 +43,8 @@ void incomingMessage(MyMessage message) {
if (message.type==V_LIGHT) { if (message.type==V_LIGHT) {
// Change relay state // Change relay state
digitalWrite(message.sensor, message.getBool()?RELAY_ON:RELAY_OFF); digitalWrite(message.sensor, message.getBool()?RELAY_ON:RELAY_OFF);
// Store state in eeprom
gw.saveState(message.sensor, message.getBool());
// Write some debug info // Write some debug info
Serial.print("Incoming change for sensor:"); Serial.print("Incoming change for sensor:");
@@ -1,66 +0,0 @@
// Example sketch showing how to request time from vera
// and how to set a cutom variable on a sensor
#include <SPI.h>
#include <EEPROM.h>
#include <RF24.h>
#include <Sensor.h>
#include <Time.h>
#define CHILD_ID 0 // Id of the sensor child
Sensor gw;
void setup()
{
gw.begin();
// Send the sketch version information to the gateway and Controller
gw.sendSketchInfo("Clock", "1.0");
// Register any sensortype. This example we just create a motion sensor.
gw.sendSensorPresentation(CHILD_ID, S_MOTION);
setSyncProvider(getTime);
setSyncInterval(3600); // get time from gw every hour
}
unsigned long getTime() {
return gw.getTime(); // Vera is time provider
}
void loop()
{
// long time = gw.getTime();
// setTime(time);
Serial.print(hour());
printDigits(minute());
printDigits(second());
Serial.print(" ");
Serial.print(day());
Serial.print(" ");
Serial.print(month());
Serial.print(" ");
Serial.print(year());
Serial.println();
// Ok now send time back to gw and store in in Variable1
// of the motion sensor device
gw.sendVariable(CHILD_ID, V_VAR1, minute());
// wait 10 seconds...
delay(10000);
}
void printDigits(int digits){
// utility function for digital clock display: prints preceding colon and leading 0
Serial.print(":");
if(digits < 10)
Serial.print('0');
Serial.print(digits);
}
@@ -0,0 +1,56 @@
// Example sketch showing how to request time from controller
#include <SPI.h>
#include <RF24.h>
#include <MySensor.h>
#include <Time.h>
MySensor gw;
boolean timeReceived = false;
void setup()
{
gw.begin();
// Send the sketch version information to the gateway and Controller
gw.sendSketchInfo("Clock", "1.0");
// Request time from controller.
gw.requestTime(receiveTime);
}
// This is called when a new time value was received
void receiveTime(unsigned long time) {
// Ok, set incoming time
setTime(time);
timeReceived = true;
}
void loop()
{
gw.process();
// Print time
if (timeReceived) {
Serial.print(hour());
printDigits(minute());
printDigits(second());
Serial.print(" ");
Serial.print(day());
Serial.print(" ");
Serial.print(month());
Serial.print(" ");
Serial.print(year());
Serial.println();
}
}
void printDigits(int digits){
// utility function for digital clock display: prints preceding colon and leading 0
Serial.print(":");
if(digits < 10)
Serial.print('0');
Serial.print(digits);
}