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