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); +} + +