From 891c826a0c574e858727e67b128584aeb632e44c Mon Sep 17 00:00:00 2001 From: Henrik Ekblad Date: Fri, 16 May 2014 14:48:18 +0200 Subject: [PATCH] * 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: --- libraries/MySensors/MyMessage.h | 10 +- libraries/MySensors/MySensor.cpp | 211 ++++++++++-------- libraries/MySensors/MySensor.h | 99 +++++--- .../DallasTemperatureSensor.ino | 59 ++--- .../examples/RelayActuator/RelayActuator.ino | 25 +-- .../TimeAndVariableSensor.ino | 66 ------ .../TimeAwareSensor/TimeAwareSensor.ino | 56 +++++ 7 files changed, 270 insertions(+), 256 deletions(-) delete mode 100644 libraries/MySensors/examples/TimeAndVariableSensor/TimeAndVariableSensor.ino create mode 100644 libraries/MySensors/examples/TimeAwareSensor/TimeAwareSensor.ino diff --git a/libraries/MySensors/MyMessage.h b/libraries/MySensors/MyMessage.h index 27b16a2..db71049 100644 --- a/libraries/MySensors/MyMessage.h +++ b/libraries/MySensors/MyMessage.h @@ -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: diff --git a/libraries/MySensors/MySensor.cpp b/libraries/MySensors/MySensor.cpp index 41ae49d..76e2510 100644 --- a/libraries/MySensors/MySensor.cpp +++ b/libraries/MySensors/MySensor.cpp @@ -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(static_cast((metric?sensors.getTempCByIndex(i):sensors.getTempFByIndex(i)) * 10.)) / 10.; + // Fetch and round temperature to one decimal + float temperature = static_cast(static_cast((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 } + diff --git a/libraries/MySensors/examples/RelayActuator/RelayActuator.ino b/libraries/MySensors/examples/RelayActuator/RelayActuator.ino index 0d57c94..f9800fb 100644 --- a/libraries/MySensors/examples/RelayActuator/RelayActuator.ino +++ b/libraries/MySensors/examples/RelayActuator/RelayActuator.ino @@ -1,4 +1,5 @@ // Example sketch showing how to control physical relays. +// This example will remember relay state even after power failure. #include #include @@ -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 -#include -#include -#include -#include - -#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); -} - - diff --git a/libraries/MySensors/examples/TimeAwareSensor/TimeAwareSensor.ino b/libraries/MySensors/examples/TimeAwareSensor/TimeAwareSensor.ino new file mode 100644 index 0000000..f7197ba --- /dev/null +++ b/libraries/MySensors/examples/TimeAwareSensor/TimeAwareSensor.ino @@ -0,0 +1,56 @@ + +// Example sketch showing how to request time from controller + +#include +#include +#include +#include + +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); +} + +