* 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:
@@ -41,8 +41,8 @@ typedef enum {
|
||||
// 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_CONFIG, I_SKETCH_NAME, I_SKETCH_VERSION
|
||||
I_INCLUSION_MODE, I_CONFIG, I_PING, I_PING_ACK,
|
||||
I_LOG_MESSAGE, I_CHILDREN, I_SKETCH_NAME, I_SKETCH_VERSION
|
||||
} internal;
|
||||
|
||||
// Type of sensor (for presentation message)
|
||||
@@ -99,12 +99,6 @@ typedef enum {
|
||||
#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
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
#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) {
|
||||
msg.sender = sender;
|
||||
msg.destination = destination;
|
||||
@@ -61,8 +61,8 @@ void MySensor::setupRadio(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_
|
||||
}
|
||||
|
||||
void MySensor::setupRelayMode(){
|
||||
s.childNodeTable = new uint8_t[256];
|
||||
eeprom_read_block((void*)s.childNodeTable, (void*)EEPROM_ROUTES_ADDRESS, 256);
|
||||
childNodeTable = new uint8_t[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) {
|
||||
@@ -76,61 +76,72 @@ void MySensor::begin(void (*_msgCallback)(MyMessage), boolean _relayMode, uint8_
|
||||
setupRadio(paLevel, channel, dataRate);
|
||||
|
||||
// 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) {
|
||||
// Set static id
|
||||
s.nodeId = _nodeId;
|
||||
nc.nodeId = _nodeId;
|
||||
}
|
||||
|
||||
// If no parent was found. Try to find one.
|
||||
if (s.parentNodeId == 0xff) {
|
||||
if (nc.parentNodeId == 0xff) {
|
||||
findParentNode();
|
||||
}
|
||||
|
||||
// Try to fetch node-id from gateway
|
||||
if (s.nodeId == AUTO) {
|
||||
if (nc.nodeId == AUTO) {
|
||||
requestNodeId();
|
||||
}
|
||||
|
||||
debug(PSTR("%s started\n"), relayMode?"relay":"sensor");
|
||||
|
||||
// Open reading pipe for messages directed to this node (set write pipe to same)
|
||||
RF24::openReadingPipe(WRITE_PIPE, TO_ADDR(s.nodeId));
|
||||
RF24::openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(s.nodeId));
|
||||
RF24::openReadingPipe(WRITE_PIPE, TO_ADDR(nc.nodeId));
|
||||
RF24::openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(nc.nodeId));
|
||||
|
||||
// Send presentation for this radio node
|
||||
present(NODE_CHILD_ID, relayMode? S_ARDUINO_RELAY : S_ARDUINO_NODE);
|
||||
// Send presentation for this radio node (attach
|
||||
present(NODE_SENSOR_ID, relayMode? S_ARDUINO_RELAY : S_ARDUINO_NODE);
|
||||
|
||||
// Send parent information back to sensor net gateway.
|
||||
buildSendPayload(msg, s.nodeId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, I_RELAY_NODE, s.parentNodeId);
|
||||
// Send a configuration exchange request to controller
|
||||
// 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() {
|
||||
return s.nodeId;
|
||||
return nc.nodeId;
|
||||
}
|
||||
|
||||
ControllerConfig MySensor::getConfig() {
|
||||
return cc;
|
||||
}
|
||||
|
||||
void MySensor::requestNodeId() {
|
||||
debug(PSTR("req node id\n"));
|
||||
RF24::openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(s.nodeId));
|
||||
buildSend(msg, s.nodeId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, I_REQUEST_ID);
|
||||
RF24::openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(nc.nodeId));
|
||||
buildSend(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_REQUEST_ID);
|
||||
}
|
||||
|
||||
|
||||
void MySensor::findParentNode() {
|
||||
if (s.nodeId == GATEWAY_ADDRESS)
|
||||
if (nc.nodeId == GATEWAY_ADDRESS)
|
||||
return; // Gateway has no business here!
|
||||
|
||||
failedTransmissions = 0;
|
||||
|
||||
// Open a reading pipe for current nodeId (if it differs from broadcast)
|
||||
if (s.nodeId != BROADCAST_PIPE) {
|
||||
RF24::openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(s.nodeId));
|
||||
if (nc.nodeId != BROADCAST_PIPE) {
|
||||
RF24::openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(nc.nodeId));
|
||||
}
|
||||
// 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)
|
||||
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);
|
||||
|
||||
unsigned long enter = millis();
|
||||
@@ -146,7 +157,7 @@ boolean MySensor::sendRoute(MyMessage message) {
|
||||
process();
|
||||
|
||||
// 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();
|
||||
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"));
|
||||
// Should be routed back to gateway.
|
||||
bool ok = sendWrite(s.parentNodeId, message);
|
||||
bool ok = sendWrite(nc.parentNodeId, message);
|
||||
|
||||
if (!ok) {
|
||||
// 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.
|
||||
if (failedTransmissions > FIND_RELAY_AFTER_FAILED_TRANSMISSIONS) {
|
||||
// need to find another route to gateway.
|
||||
if (failedTransmissions > SEARCH_FAILURES) {
|
||||
findParentNode();
|
||||
}
|
||||
failedTransmissions++;
|
||||
@@ -184,7 +195,7 @@ boolean MySensor::sendRoute(MyMessage message) {
|
||||
|
||||
boolean MySensor::sendWrite(uint8_t next, MyMessage message, bool broadcast) {
|
||||
uint8_t length = mGetLength(message);
|
||||
message.last = s.nodeId;
|
||||
message.last = nc.nodeId;
|
||||
mSetVersion(message, PROTOCOL_VERSION);
|
||||
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) {
|
||||
message.sender = s.nodeId;
|
||||
message.sender = nc.nodeId;
|
||||
if (enableAck) {
|
||||
mSetCommand(message,C_SET_WITH_ACK);
|
||||
} else {
|
||||
@@ -214,33 +225,31 @@ bool MySensor::send(MyMessage message, bool enableAck) {
|
||||
}
|
||||
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
buildSend(msg, s.nodeId, destination, childSensorId, C_REQ, variableType);
|
||||
buildSend(msg, nc.nodeId, destination, childSensorId, C_REQ, variableType);
|
||||
}
|
||||
|
||||
void MySensor::requestTime() {
|
||||
buildSend(msg, s.nodeId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, I_TIME);
|
||||
void MySensor::requestTime(void (* _timeCallback)(unsigned long)) {
|
||||
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() {
|
||||
uint8_t pipe;
|
||||
@@ -266,7 +275,7 @@ boolean MySensor::process() {
|
||||
|
||||
uint8_t command = mGetCommand(msg);
|
||||
uint8_t type = msg.type;
|
||||
uint8_t sender = msg.sender; //getSender();
|
||||
uint8_t sender = msg.sender;
|
||||
uint8_t last = msg.last;
|
||||
uint8_t destination = msg.destination;
|
||||
|
||||
@@ -275,15 +284,13 @@ boolean MySensor::process() {
|
||||
// Wait a random delay of 0-2 seconds to minimize collision
|
||||
// between ping ack messages from other relaying nodes
|
||||
delay(millis() & 0x3ff);
|
||||
|
||||
build(msg, s.nodeId, sender, NODE_CHILD_ID, C_INTERNAL, I_PING_ACK);
|
||||
msg.set(s.distance);
|
||||
build(msg, nc.nodeId, sender, NODE_SENSOR_ID, C_INTERNAL, I_PING_ACK);
|
||||
msg.set(nc.distance);
|
||||
sendWrite(sender, msg);
|
||||
return false;
|
||||
} else if (destination == s.nodeId) {
|
||||
} else if (destination == nc.nodeId) {
|
||||
// This message is addressed to this node
|
||||
|
||||
if (relayMode && last != s.parentNodeId) {
|
||||
if (relayMode && last != nc.parentNodeId) {
|
||||
// Message is from one of the child nodes. Add it to routing table.
|
||||
addChildRoute(sender, last);
|
||||
}
|
||||
@@ -292,62 +299,68 @@ boolean MySensor::process() {
|
||||
if (type == I_PING_ACK) {
|
||||
// We've received a reply to our PING message looking for parent.
|
||||
uint8_t distance = msg.getByte();
|
||||
if (distance<s.distance-1) {
|
||||
if (distance<nc.distance-1) {
|
||||
// Found a neighbor closer to GW than previously found
|
||||
s.distance = distance + 1;
|
||||
s.parentNodeId = msg.sender;
|
||||
eeprom_write_byte((uint8_t*)EEPROM_PARENT_NODE_ID_ADDRESS, s.parentNodeId);
|
||||
eeprom_write_byte((uint8_t*)EEPROM_DISTANCE_ADDRESS, s.distance);
|
||||
debug(PSTR("p=%d, d=%d\n"), s.parentNodeId, s.distance);
|
||||
nc.distance = distance + 1;
|
||||
nc.parentNodeId = msg.sender;
|
||||
eeprom_write_byte((uint8_t*)EEPROM_PARENT_NODE_ID_ADDRESS, nc.parentNodeId);
|
||||
eeprom_write_byte((uint8_t*)EEPROM_DISTANCE_ADDRESS, nc.distance);
|
||||
debug(PSTR("p=%d, d=%d\n"), nc.parentNodeId, nc.distance);
|
||||
}
|
||||
return false;
|
||||
} else if (type == I_REQUEST_ID && sender == GATEWAY_ADDRESS) {
|
||||
if (s.nodeId == AUTO) {
|
||||
s.nodeId = msg.getByte();
|
||||
// Write id to EEPROM
|
||||
if (s.nodeId == AUTO) {
|
||||
// sensor net gateway will return max id if all sensor id are taken
|
||||
debug(PSTR("full\n"));
|
||||
while (1); // Wait here. Nothing else we can do...
|
||||
} else {
|
||||
RF24::openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(s.nodeId));
|
||||
eeprom_write_byte((uint8_t*)EEPROM_NODE_ID_ADDRESS, s.nodeId);
|
||||
}
|
||||
debug(PSTR("id=%d\n"), s.nodeId);
|
||||
}
|
||||
return false;
|
||||
} 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) );
|
||||
} else if (sender == GATEWAY_ADDRESS) {
|
||||
bool isMetric;
|
||||
|
||||
switch (type) {
|
||||
case I_REQUEST_ID:
|
||||
if (nc.nodeId == AUTO) {
|
||||
nc.nodeId = msg.getByte();
|
||||
// Write id to EEPROM
|
||||
if (nc.nodeId == AUTO) {
|
||||
// sensor net gateway will return max id if all sensor id are taken
|
||||
debug(PSTR("full\n"));
|
||||
while (1); // Wait here. Nothing else we can do...
|
||||
} else {
|
||||
RF24::openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(nc.nodeId));
|
||||
eeprom_write_byte((uint8_t*)EEPROM_NODE_ID_ADDRESS, nc.nodeId);
|
||||
}
|
||||
buildSendPayload(msg, s.nodeId, GATEWAY_ADDRESS, NODE_CHILD_ID, C_INTERNAL, I_CHILDREN, "n/a");
|
||||
} else if (msg.getByte() == 'C') {
|
||||
debug(PSTR("id=%d\n"), nc.nodeId);
|
||||
}
|
||||
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
|
||||
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);
|
||||
}
|
||||
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.
|
||||
if (command == C_SET_WITH_ACK) {
|
||||
ack = msg;
|
||||
mSetCommand(ack,C_SET);
|
||||
ack.sender = s.nodeId;
|
||||
ack.sender = nc.nodeId;
|
||||
ack.destination = msg.sender;
|
||||
sendRoute(ack);
|
||||
// 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)
|
||||
|
||||
// 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
|
||||
addChildRoute(sender, last);
|
||||
} else {
|
||||
@@ -411,7 +424,7 @@ boolean MySensor::process() {
|
||||
|
||||
// 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?!
|
||||
debug(PSTR("ro=?\n"));
|
||||
|
||||
@@ -489,31 +502,39 @@ uint8_t MySensor::validate(MyMessage message) {
|
||||
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) {
|
||||
if (s.childNodeTable[childId] != route) {
|
||||
s.childNodeTable[childId] = route;
|
||||
if (childNodeTable[childId] != route) {
|
||||
childNodeTable[childId] = route;
|
||||
eeprom_write_byte((uint8_t*)EEPROM_ROUTES_ADDRESS+childId, route);
|
||||
}
|
||||
}
|
||||
|
||||
void MySensor::removeChildRoute(uint8_t childId) {
|
||||
if (s.childNodeTable[childId] != 0xff) {
|
||||
s.childNodeTable[childId] = 0xff;
|
||||
if (childNodeTable[childId] != 0xff) {
|
||||
childNodeTable[childId] = 0xff;
|
||||
eeprom_write_byte((uint8_t*)EEPROM_ROUTES_ADDRESS+childId, 0xff);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t MySensor::getChildRoute(uint8_t childId) {
|
||||
return s.childNodeTable[childId];
|
||||
return childNodeTable[childId];
|
||||
}
|
||||
|
||||
|
||||
#ifdef DEBUG
|
||||
void MySensor::debugPrint(const char *fmt, ... ) {
|
||||
char fmtBuffer[300];
|
||||
if (s.nodeId == GATEWAY_ADDRESS) {
|
||||
if (nc.nodeId == GATEWAY_ADDRESS) {
|
||||
// prepend debug message to be handled correctly by gw
|
||||
Serial.write("0;0;4;11;");
|
||||
}
|
||||
@@ -521,7 +542,7 @@ void MySensor::debugPrint(const char *fmt, ... ) {
|
||||
va_start (args, fmt );
|
||||
vsnprintf_P(fmtBuffer, 300, fmt, args);
|
||||
va_end (args);
|
||||
if (s.nodeId == GATEWAY_ADDRESS) {
|
||||
if (nc.nodeId == GATEWAY_ADDRESS) {
|
||||
// Truncate message if this is gateway node
|
||||
vsnprintf_P(fmtBuffer, 60, fmt, args);
|
||||
fmtBuffer[59] = '\n';
|
||||
|
||||
@@ -34,14 +34,19 @@
|
||||
#define BAUD_RATE 115200
|
||||
|
||||
#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_NODE_ID_ADDRESS EEPROM_START // Where to store radio id in EEPROM
|
||||
#define EEPROM_PARENT_NODE_ID_ADDRESS (EEPROM_START+1) // Where to store relay id in EEPROM
|
||||
#define EEPROM_DISTANCE_ADDRESS (EEPROM_PARENT_NODE_ID_ADDRESS+1) // Where to store distance to gateway in EEPROM
|
||||
// EEPROM location of node id
|
||||
#define EEPROM_NODE_ID_ADDRESS EEPROM_START
|
||||
// EEPROM location of parent id
|
||||
#define EEPROM_PARENT_NODE_ID_ADDRESS (EEPROM_START+1)
|
||||
// EEPROM location of distance to gateway
|
||||
#define EEPROM_DISTANCE_ADDRESS (EEPROM_PARENT_NODE_ID_ADDRESS+1)
|
||||
#define EEPROM_ROUTES_ADDRESS (EEPROM_DISTANCE_ADDRESS+1) // Where to start storing routing information in EEPROM. Will allocate 256 bytes.
|
||||
|
||||
#define EEPROM_CONTROLLER_CONFIG_ADDRESS (EEPROM_ROUTES_ADDRESS+256) // Location of controller sent configuration (we allow one payload of config data from controller)
|
||||
#define EEPROM_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 also act as base value for sensor nodeId
|
||||
@@ -54,7 +59,20 @@
|
||||
#define CURRENT_NODE_PIPE ((uint8_t)1)
|
||||
#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
|
||||
@@ -74,7 +92,7 @@ class MySensor : public RF24
|
||||
* 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 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 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
|
||||
@@ -103,7 +121,7 @@ class MySensor : public RF24
|
||||
* @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);
|
||||
void sendSketchInfo(const char *name, const char *version);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* 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 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
|
||||
*/
|
||||
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();
|
||||
|
||||
/**
|
||||
* 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();
|
||||
void requestTime(void (* timeCallback)(unsigned long));
|
||||
|
||||
|
||||
/**
|
||||
@@ -153,7 +161,32 @@ class MySensor : public RF24
|
||||
* Returns true if there is a message addressed for this node just was received.
|
||||
* 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
|
||||
@@ -168,31 +201,27 @@ class MySensor : public RF24
|
||||
#endif
|
||||
|
||||
protected:
|
||||
struct settings_t
|
||||
{
|
||||
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;
|
||||
NodeConfig nc; // Essential settings for node to work
|
||||
ControllerConfig cc; // Configuration coming from controller
|
||||
boolean relayMode;
|
||||
|
||||
void setupRelayMode();
|
||||
void setupRadio(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e dataRate);
|
||||
void findParentNode();
|
||||
boolean sendRoute(MyMessage message);
|
||||
boolean sendWrite(uint8_t dest, MyMessage message, bool broadcast=false);
|
||||
uint8_t validate(MyMessage message);
|
||||
|
||||
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.
|
||||
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 findParentNode();
|
||||
uint8_t crc8Message(MyMessage);
|
||||
|
||||
uint8_t getChildRoute(uint8_t childId);
|
||||
void addChildRoute(uint8_t childId, uint8_t route);
|
||||
void removeChildRoute(uint8_t childId);
|
||||
|
||||
@@ -25,10 +25,10 @@ void setup()
|
||||
sensors.begin();
|
||||
|
||||
// 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
|
||||
gw.sketchInfo("Temperature Sensor", "1.0");
|
||||
gw.sendSketchInfo("Temperature Sensor", "1.0");
|
||||
|
||||
// Fetch the number of attached temperature sensors
|
||||
numSensors = sensors.getDeviceCount();
|
||||
@@ -38,56 +38,39 @@ void setup()
|
||||
gw.present(i, S_TEMP);
|
||||
}
|
||||
|
||||
// Request configuration from controller (use this to determine if we're supposed to send in Ceclscius or Farenheit)
|
||||
gw.requestConfig();
|
||||
|
||||
// Initialize outgoing message to always send temperature
|
||||
// Initialize outgoing message to always send temperature
|
||||
out.type = V_TEMP;
|
||||
}
|
||||
|
||||
|
||||
void loop()
|
||||
{
|
||||
// Process incoming messages
|
||||
// Process incoming messages (like config from server)
|
||||
gw.process();
|
||||
|
||||
if (receivedConfig) {
|
||||
// Fetch temperatures from Dallas sensors
|
||||
sensors.requestTemperatures();
|
||||
// Fetch temperatures from Dallas sensors
|
||||
sensors.requestTemperatures();
|
||||
|
||||
// Read temperatures and send them to controller
|
||||
for (int i=0; i<numSensors && i<MAX_ATTACHED_DS18B20; i++) {
|
||||
// Read temperatures and send them to controller
|
||||
for (int i=0; i<numSensors && i<MAX_ATTACHED_DS18B20; i++) {
|
||||
|
||||
// Fetch and round temperature to one decimal
|
||||
float temperature = static_cast<float>(static_cast<int>((metric?sensors.getTempCByIndex(i):sensors.getTempFByIndex(i)) * 10.)) / 10.;
|
||||
// Fetch and round temperature to one decimal
|
||||
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
|
||||
if (lastTemperature[i] != temperature && temperature != -127.00) {
|
||||
// Only send data if temperature has changed and no error
|
||||
if (lastTemperature[i] != temperature && temperature != -127.00) {
|
||||
|
||||
// Send in the new temperature
|
||||
gw.send(out.setSensor(i).set(temperature,1));
|
||||
lastTemperature[i]=temperature;
|
||||
}
|
||||
// Send in the new temperature
|
||||
gw.send(out.setSensor(i).set(temperature,1));
|
||||
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.
|
||||
// This example will remember relay state even after power failure.
|
||||
|
||||
#include <MySensor.h>
|
||||
#include <SPI.h>
|
||||
@@ -17,29 +18,23 @@ void setup()
|
||||
// Initialize library and add callback for incoming messages
|
||||
gw.begin(incomingMessage, false);
|
||||
// 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
|
||||
for (int i=0; i<NUMBER_OF_RELAYS;i++) {
|
||||
// Make sure relays are off when starting up
|
||||
digitalWrite(RELAY_1+i, RELAY_OFF);
|
||||
for (int sensor=0, pin=RELAY_1; sensor<=NUMBER_OF_RELAYS;sensor++, pin++) {
|
||||
// Register all sensors to gw (they will be created as child devices)
|
||||
gw.present(sensor, S_LIGHT);
|
||||
// Then set relay pins in output mode
|
||||
pinMode(RELAY_1+i, OUTPUT);
|
||||
|
||||
// Request and wait for relay status from controller
|
||||
gw.request(RELAY_1+i, V_LIGHT);
|
||||
pinMode(pin, OUTPUT);
|
||||
// Set relay to last known state (using eeprom storage)
|
||||
digitalWrite(pin, gw.loadState(sensor));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void loop()
|
||||
{
|
||||
// Process incoming messages
|
||||
// Alway process incoming messages whenever possible
|
||||
gw.process();
|
||||
}
|
||||
|
||||
@@ -48,6 +43,8 @@ void incomingMessage(MyMessage message) {
|
||||
if (message.type==V_LIGHT) {
|
||||
// Change relay state
|
||||
digitalWrite(message.sensor, message.getBool()?RELAY_ON:RELAY_OFF);
|
||||
// Store state in eeprom
|
||||
gw.saveState(message.sensor, message.getBool());
|
||||
|
||||
// Write some debug info
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user