Move master branch from from mios git repo

This commit is contained in:
hek
2014-01-23 00:23:15 +01:00
commit 609d9e5be9
352 changed files with 53418 additions and 0 deletions
+245
View File
@@ -0,0 +1,245 @@
/*
The Sensor Net Arduino 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 "Gateway.h"
Gateway::Gateway(uint8_t _cepin, uint8_t _cspin, uint8_t _inclusion_time) : Relay(_cepin, _cspin) {
ledMode = false;
inclusionTime = _inclusion_time;
}
Gateway::Gateway(uint8_t _cepin, uint8_t _cspin, uint8_t _inclusion, uint8_t _inclusion_time, uint8_t _rx, uint8_t _tx, uint8_t _er) : Relay(_cepin, _cspin) {
ledMode = true;
pinInclusion = _inclusion;
inclusionTime = _inclusion_time;
pinRx = _rx;
pinTx = _tx;
pinEr = _er;
}
void Gateway::begin(uint8_t _radioId) {
radioId = 0;
distance = 0;
inclusionMode = 0;
buttonTriggeredInclusion = false;
countRx = 0;
countTx = 0;
countErr = 0;
if (ledMode) {
// Setup led pins
pinMode(pinRx, OUTPUT);
pinMode(pinTx, OUTPUT);
pinMode(pinEr, OUTPUT);
digitalWrite(pinRx, LOW);
digitalWrite(pinTx, LOW);
digitalWrite(pinEr, LOW);
// Setup digital in that triggers inclusion mode
pinMode(pinInclusion, INPUT);
digitalWrite(pinInclusion, HIGH);
// Set initial state of leds
digitalWrite(pinRx, HIGH);
digitalWrite(pinTx, HIGH);
digitalWrite(pinEr, HIGH);
}
// Set baudrate of serial link
Serial.begin(BAUD_RATE);
// Start up the radio library
setupRadio();
RF24::openReadingPipe(CURRENT_NODE_PIPE, BASE_RADIO_ID);
RF24::startListening();
// Send startup log message on serial
serial(PSTR("0;0;%d;%d;Arduino startup complete.\n"), M_INTERNAL, I_LOG_MESSAGE);
}
boolean Gateway::isLedMode() {
return ledMode;
}
void Gateway::startInclusionInterrupt() {
buttonTriggeredInclusion = true;
}
void Gateway::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);
buttonTriggeredInclusion = false;
setInclusionMode(true);
}
}
void Gateway::checkInclusionFinished() {
if (inclusionMode && millis()-inclusionStartTime>60000L*inclusionTime) {
// inclusionTimeInMinutes minute(s) has passed.. stop inclusion mode
setInclusionMode(false);
}
}
void Gateway::parseAndSend(String inputString) {
boolean ok;
char *str, *p, *value;
int i = 0;
uint16_t sensorRadioId;
uint8_t childId;
uint8_t messageType;
uint8_t type;
// Copy command to char* buffer
inputString.toCharArray(commandBuffer, MAX_RECEIVE_LENGTH);
// Extract command data coming on serial line
for (str = strtok_r(commandBuffer, ";", &p); // split using semicolon
str && i < 5; // loop while str is not null an max 5 times
str = strtok_r(NULL, ";", &p) // get subsequent tokens
) {
switch (i) {
case 0: // Radioid (destination)
sensorRadioId = atoi(str);
break;
case 1: // Childid
childId = atoi(str);
break;
case 2: // Message type
messageType = atoi(str);
break;
case 3: // Data type
type = atoi(str);
break;
case 4: // Variable value
value = str;
break;
}
i++;
}
if (sensorRadioId==GATEWAY_ADDRESS && messageType==M_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);
} 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);
if (!ok) {
errBlink(1);
}
}
}
void Gateway::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);
if (inclusionMode) {
inclusionStartTime = millis();
}
}
// Override normal validate to add error blink if crc check fails
uint8_t Gateway::validate() {
uint8_t res = Sensor::validate();
if (res == VALIDATE_BAD_CRC) {
errBlink(1);
}
return res;
}
void Gateway::processRadioMessage() {
if (messageAvailable()) {
// A new message was received from one of the sensors
message_s msg = getMessage();
if (msg.header.messageType == M_PRESENTATION && inclusionMode) {
rxBlink(3);
} else {
rxBlink(1);
}
// Pass along the message from sensors to serial line
serial(msg);
}
checkButtonTriggeredInclusion();
checkInclusionFinished();
}
void Gateway::serial(const char *fmt, ... ) {
va_list args;
va_start (args, fmt );
vsnprintf_P(serialBuffer, MAX_SEND_LENGTH, fmt, args);
va_end (args);
Serial.print(serialBuffer);
}
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 Gateway::ledTimersInterrupt() {
if(countRx && countRx != 255) {
// switch led on
digitalWrite(pinRx, LOW);
} else if(!countRx) {
// switching off
digitalWrite(pinRx, HIGH);
}
if(countRx != 255) { countRx--; }
if(countTx && countTx != 255) {
// switch led on
digitalWrite(pinTx, LOW);
} else if(!countTx) {
// switching off
digitalWrite(pinTx, HIGH);
}
if(countTx != 255) { countTx--; }
else if(inclusionMode) { countTx = 8; }
if(countErr && countErr != 255) {
// switch led on
digitalWrite(pinEr, LOW);
} else if(!countErr) {
// switching off
digitalWrite(pinEr, HIGH);
}
if(countErr != 255) { countErr--; }
}
void Gateway::rxBlink(uint8_t cnt) {
if(countRx == 255) { countRx = cnt; }
}
void Gateway::txBlink(uint8_t cnt) {
if(countTx == 255 && !inclusionMode) { countTx = cnt; }
}
void Gateway::errBlink(uint8_t cnt) {
if(countErr == 255) { countErr = cnt; }
}
+82
View File
@@ -0,0 +1,82 @@
/*
The Sensor Net Arduino 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 Gateway_h
#define Gateway_h
#include "Relay.h"
#define MAX_RECEIVE_LENGTH 50 // Max buffersize needed for messages coming from vera
#define MAX_SEND_LENGTH 120 // Max buffersize needed for messages coming from vera
class Gateway : public Relay
{
public:
/**
* Constructor
*
* Creates a new instance of Gateway class.
* If you don't use status leds and/or inclusion mode button on your arduino gateway
* you can disable this functionality by calling the 2 argument constructor.
*
* @param _cepin The pin attached to RF24 Chip Enable on the RF module
* @param _cspin The pin attached to RF24 Chip Select
* @param _inclusion Digital pin that triggers inclusion mode
* @param _inclusion_time Time of inclusion mode (in minutes)
* @param _rx Digital pin for receive led
* @param _tx Digital pin for transfer led
* @param _er Digital pin for error led
*
*/
Gateway(uint8_t _cepin, uint8_t _cspin, uint8_t _inclusion_time);
Gateway(uint8_t _cepin, uint8_t _cspin, uint8_t _inclusion, uint8_t _inclusion_time, uint8_t _rx, uint8_t _tx, uint8_t _er);
void begin(uint8_t _radioId = 0);
void processRadioMessage();
void parseAndSend(String inputString);
boolean isLedMode();
void ledTimersInterrupt();
void startInclusionInterrupt();
private:
char commandBuffer[MAX_RECEIVE_LENGTH];
char serialBuffer[MAX_SEND_LENGTH]; // Buffer for building string when sending data to vera
unsigned long inclusionStartTime;
boolean inclusionMode; // Keeps track on inclusion mode
boolean buttonTriggeredInclusion;
volatile uint8_t countRx;
volatile uint8_t countTx;
volatile uint8_t countErr;
boolean ledMode;
uint8_t pinInclusion;
uint8_t inclusionTime;
uint8_t pinRx;
uint8_t pinTx;
uint8_t pinEr;
uint8_t validate();
void serial(message_s msg);
void interruptStartInclusion();
void checkButtonTriggeredInclusion();
void setInclusionMode(boolean newMode);
void serial(const char *fmt, ... );
void checkInclusionFinished();
void ledTimers();
void rxBlink(uint8_t cnt);
void txBlink(uint8_t cnt);
void errBlink(uint8_t cnt);
};
#endif
+212
View File
@@ -0,0 +1,212 @@
/*
The Sensor Net Arduino 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) {
Sensor::begin(_radioId);
// Read routing table from EEPROM
for (int i=0;i<sizeof(childNodeTable);i++) {
childNodeTable[i] = EEPROM.read(EEPROM_ROUTES_ADDRESS+i);
}
// Start listening for incoming messages
RF24::startListening();
}
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.\n"));
// Message destination is not gateway and is in routing table for this node.
// Send it downstream
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);
}
}
boolean Relay::messageAvailable() {
uint8_t pipe;
boolean available = RF24::available(&pipe);
if (available) {
uint8_t len = RF24::getDynamicPayloadSize()-sizeof(msg.header);
readMessage();
boolean ok = validate() == VALIDATE_OK;
debug(PSTR("Message crc %s.\n"),ok?"ok":"error");
if (ok) {
if (msg.header.messageType == M_INTERNAL &&
msg.header.type == I_PING) {
// We always answer ping messages
debug(PSTR("Answer ping message.\n"));
// 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...
return true;
} else {
// If this is variable message from sensor net gateway. Send ack back.
debug(PSTR("Message addressed for this node.\n"));
if (msg.header.from == GATEWAY_ADDRESS &&
msg.header.messageType == M_SET_VARIABLE) {
// Send back ack message to sensor net gateway
sendVariableAck();
}
// Return message to waiting sketch...
return true;
}
} else {
// We should probably try to relay this message
relayMessage(len, 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 (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.
debug(PSTR("Send child info to sensor gateway.\n"));
sendInternal(I_CHILDREN, "not implemented");
// TODO: Fix this
}
+60
View File
@@ -0,0 +1,60 @@
/*
The Sensor Net Arduino 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
* @param _cspin The pin attached to RF24 Chip Select
*/
Relay(uint8_t _cepin, uint8_t _cspin);
void begin(uint8_t _radioId);
boolean messageAvailable();
boolean send(message_s message, int length);
protected:
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 sendChildren();
void relayMessage(uint8_t length, uint8_t pipe);
};
#endif
+541
View File
@@ -0,0 +1,541 @@
/*
The Sensor Net Arduino 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() {
failedTransmissions = 0;
// Start up the radio library
RF24::begin();
RF24::enableDynamicPayloads();
RF24::setAutoAck(true);
RF24::setRetries(15, 15);
//RF24::setChannel(70);
RF24::setDataRate(RF24_1MBPS);
RF24::setCRCLength(RF24_CRC_16);
// All (relaying) nodes listen to broadcast pipe (for PING messages)
RF24::openReadingPipe(BROADCAST_PIPE, BASE_RADIO_ID + BROADCAST_ADDRESS);
}
void Sensor::begin(uint8_t _radioId) {
radioId = _radioId;
debug(PSTR("Started %s.\n"), isRelay?"relay":"sensor");
setupRadio();
// 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
RF24::openReadingPipe(CURRENT_NODE_PIPE, BASE_RADIO_ID+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, BASE_RADIO_ID+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.
// Stay in this method until one or more relay nodes answers ping message.
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, BASE_RADIO_ID+radioId);
}
distance = 255;
uint8_t oldRelayId = relayId;
while (distance == 255) {
// 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);
}
}
// Store new relay address in EEPROM
if (relayId != oldRelayId) {
EEPROM.write(EEPROM_RELAY_ID_ADDRESS, relayId);
EEPROM.write(EEPROM_DISTANCE_ADDRESS, distance);
}
// Always listen to relay pipe (relay broadcasts messages that might be for this
// nodes children)
RF24::openReadingPipe(RELAY_PIPE, BASE_RADIO_ID + relayId);
}
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) {
bool ok = false;
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>20) {
findRelay();
}
failedTransmissions++;
} else {
failedTransmissions = 0;
}
return ok;
}
boolean Sensor::sendWrite(uint8_t dest, message_s message, int length) {
// Add current radioId to last-field and calculate crc
message.header.last = radioId;
message.header.crc = crc8Message(message);
debug(PSTR("Sending: from=%d, to=%d, childId=%d, mtype=%d, type=%d, crc=%d, '%s', sent via %d\n"),
message.header.from,message.header.to, message.header.childId, message.header.messageType, message.header.type, message.header.crc, message.data, dest);
RF24::stopListening();
bool ok = false;
RF24::openWritingPipe(BASE_RADIO_ID + dest);
int retry = 5;
do {
ok = RF24::write(&message, min(MAX_MESSAGE_LENGTH, sizeof(message.header) + length));
}
while ( !ok && --retry );
RF24::startListening();
if (ok) {
debug(PSTR("Sent successfully\n"));
} else {
debug(PSTR("Send failed. No ack received.\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::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_ACK_VARIABLE, variableType);
}
char * Sensor::getStatus(uint8_t nodeId, int8_t childId, uint8_t variableType) {
return get(nodeId, childId, M_REQ_VARIABLE, M_ACK_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() {
if (RF24::available()) {
readMessage();
boolean ok = validate() == VALIDATE_OK;
debug(PSTR("Mess crc %s.\n"),ok?"ok":"error");
if (ok && msg.header.to == radioId) {
// This message is addressed to this node
debug(PSTR("Message addressed for this node.\n"));
if (msg.header.from == GATEWAY_ADDRESS &&
// If this is variable message from sensor net gateway. Send ack back.
msg.header.messageType == M_SET_VARIABLE) {
// Send back ack message to sensor net gateway
sendVariableAck();
}
// 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;
}
message_s Sensor::readMessage() {
uint8_t len = RF24::getDynamicPayloadSize();
bool done = RF24::read(&msg, len);
// Make sure string gets terminated ok.
msg.data[len - sizeof(header_s) ] = '\0';
debug(PSTR("Received: from=%d, to=%d, childId=%d, mtype=%d, type=%d, crc=%d, '%s'\n"),
msg.header.from,msg.header.to, msg.header.childId, msg.header.messageType, msg.header.type,msg.header.crc, msg.data);
return msg;
}
/*
* raw CRC 8 bit calculation
*/
uint8_t Sensor::crc8Raw ( uint8_t *data_in, uint8_t number_of_bytes_to_read )
{
uint8_t crc;
uint8_t loop_count;
uint8_t bit_counter;
uint8_t data;
uint8_t feedback_bit;
crc = CRC8INIT;
for (loop_count = 0; loop_count != number_of_bytes_to_read; loop_count++)
{
data = data_in[loop_count];
bit_counter = 8;
do {
feedback_bit = (crc ^ data) & 0x01;
if ( feedback_bit == 0x01 ) {
crc = crc ^ CRC8POLY;
}
crc = (crc >> 1) & 0x7F;
if ( feedback_bit == 0x01 ) {
crc = crc | 0x80;
}
data = data >> 1;
bit_counter--;
} while (bit_counter > 0);
}
return crc;
}
/*
* calculate CRC8 on message_s data taking care of data structure and protocol version
*/
uint8_t Sensor::crc8Message(message_s var_msg) {
// Must set crc to a constant value.
var_msg.header.crc = 0;
// fill unused space by zeroes for string data only
if(!var_msg.header.binary) {
uint8_t len = strlen(var_msg.data);
if(len < sizeof(var_msg.data)-1) {
memset(&var_msg.data[len], 0, sizeof(var_msg.data) - 1 - len);
}
}
return crc8Raw((uint8_t*)&var_msg, (uint8_t)sizeof(var_msg));
}
/*
uint8_t Sensor::crc8Message(message_s var_msg) {
struct {
message_s msg;
uint8_t protocol_version;
} crc_data;
memcpy(&crc_data, &var_msg, sizeof(var_msg));
crc_data.protocol_version = PROTOCOL_VERSION;
// some clean up needed for repeated result
crc_data.msg.header.crc = 0;
// fill unused space by zeroes for string data only
if(!crc_data.msg.header.binary) {
uint8_t len = strlen(crc_data.msg.data);
if(len < sizeof(crc_data.msg.data)-1) {
memset(&crc_data.msg.data[len], 0, sizeof(crc_data.msg.data) - 1 - len);
}
}
return crc8Raw((uint8_t*)&crc_data, (uint8_t)sizeof(crc_data));
} */
/*
* true if message is consistent *
*/
bool Sensor::checkCRC(message_s var_msg) {
uint8_t oldCrc = var_msg.header.crc;
//debug(PSTR("CRC: in %d, calc %d\n"),oldCrc, crc8Message(var_msg));
return (oldCrc == crc8Message(var_msg))?true:false;
}
uint8_t Sensor::validate() {
bool crc_check = checkCRC(msg);
bool version_check = (msg.header.version == PROTOCOL_VERSION);
if(!version_check) return VALIDATE_BAD_VERSION;
if(!crc_check) 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) {
// Truncte message if this is gateway node
vsnprintf_P(fmtBuffer, 97, fmt, args);
fmtBuffer[96] = '\n';
fmtBuffer[97] = '\0';
} else {
vsnprintf_P(fmtBuffer, 299, fmt, args);
}
va_end (args);
Serial.print(fmtBuffer);
//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
+279
View File
@@ -0,0 +1,279 @@
/*
The Sensor Net Arduino 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 <nRF24L01.h>
#include <RF24.h>
#include <RF24_config.h>
#include <Arduino.h>
#include <stddef.h>
#include <SPI.h>
#include <EEPROM.h>
#include <avr/progmem.h>
#include <stdarg.h>
#define DEBUG // Turn on/off debug mode by having/removing this define
#ifdef DEBUG
#define debug(x,...) debugPrint(x, ##__VA_ARGS__)
#else
#define debug(x,...)
#endif
#define LIBRARY_VERSION "1.2+"
#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 0xABCDABC000LL
#define GATEWAY_ADDRESS ((uint8_t)0)
#define BROADCAST_ADDRESS ((uint8_t)0xFF)
#define MAX_MESSAGE_LENGTH 32
#define CURRENT_NODE_PIPE ((uint8_t)1)
#define BROADCAST_PIPE ((uint8_t)2)
#define RELAY_PIPE ((uint8_t)3)
#define CRC8INIT 0x00
#define CRC8POLY 0x18 //0X18 = X^8+X^5+X^4+X^0
// Message types
typedef enum {
M_PRESENTATION = 0,
M_SET_VARIABLE = 1,
M_REQ_VARIABLE = 2,
M_ACK_VARIABLE = 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
} 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
} 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
* @param _cspin The pin attached to RF24 Chip Select
*/
Sensor(uint8_t _cepin, uint8_t _cspin);
/**
* Begin operation of the sensor net library
*
* Call this in setup(), before calling any other sensor net library methods.
* @param _radioId The unique id (1-254) for this sensor. Specify 255 for auto id mode.
*/
void begin(uint8_t _radioId);
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 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();
#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();
void findRelay();
boolean send(message_s message, int length);
boolean sendWrite(uint8_t dest, message_s message, int length);
message_s readMessage(void);
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 crc8Raw(uint8_t *data_in, uint8_t number_of_bytes_to_read);
uint8_t crc8Message(message_s);
bool checkCRC(message_s);
char* get(uint8_t nodeId, uint8_t childId, uint8_t sendType, uint8_t receiveType, uint8_t variableType);
char *getInternal(uint8_t variableType);
};
#endif
+63
View File
@@ -0,0 +1,63 @@
/* please read copyright-notice at EOF */
#include <stdint.h>
#define CRC8INIT 0x00
#define CRC8POLY 0x18 //0X18 = X^8+X^5+X^4+X^0
uint8_t crc8( uint8_t *data, uint16_t number_of_bytes_in_data )
{
uint8_t crc;
uint16_t loop_count;
uint8_t bit_counter;
uint8_t b;
uint8_t feedback_bit;
crc = CRC8INIT;
for (loop_count = 0; loop_count != number_of_bytes_in_data; loop_count++)
{
b = data[loop_count];
bit_counter = 8;
do {
feedback_bit = (crc ^ b) & 0x01;
if ( feedback_bit == 0x01 ) {
crc = crc ^ CRC8POLY;
}
crc = (crc >> 1) & 0x7F;
if ( feedback_bit == 0x01 ) {
crc = crc | 0x80;
}
b = b >> 1;
bit_counter--;
} while (bit_counter > 0);
}
return crc;
}
/*
This code is from Colin O'Flynn - Copyright (c) 2002
only minor changes by M.Thomas 9/2004
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
+41
View File
@@ -0,0 +1,41 @@
#ifndef CRC8_H_
#define CRC8_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
uint8_t crc8( uint8_t* data, uint16_t number_of_bytes_in_data );
#ifdef __cplusplus
}
#endif
#endif
/*
This is based on code from :
Copyright (c) 2002 Colin O'Flynn
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "crc8.c"
@@ -0,0 +1,20 @@
/*
*
* This sketch clears radioId, relayId and routing info in EEPROM
*
*/
#include <SPI.h>
#include <EEPROM.h>
void setup()
{
for (int i=0;i<512;i++) {
EEPROM.write(i, 0xff);
}
}
void loop()
{
// Nothing to do here...
}
@@ -0,0 +1,70 @@
#include <Sleep_n0m1.h>
#include <SPI.h>
#include <EEPROM.h>
#include <DallasTemperature.h>
#include <OneWire.h>
#include <RF24.h>
#include <Sensor.h>
// Set RADIO_ID to something unique in your sensor network (1-254)
// or set to AUTO if you want gw to assign a RADIO_ID for you.
#define RADIO_ID AUTO
#define ONE_WIRE_BUS 3 // Pin where dallase sensor is connected
unsigned long SLEEP_TIME = 30; // Sleep time between reads (in seconds)
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
Sensor gw(9, 10);
Sleep sleep;
#define MAX_ATTACHED_DS18B20 16
float lastTemperature[MAX_ATTACHED_DS18B20];
int numSensors=0;
boolean metric = true;
void setup()
{
Serial.begin(BAUD_RATE); // Used to type in characters
sensors.begin();
gw.begin(RADIO_ID);
// Fetch the number of attached sensors
numSensors = sensors.getDeviceCount();
// Register all sensors to gw (they will be created as child devices)
for (int i=0; i<numSensors; i++) {
gw.sendSensorPresentation(i, S_TEMP);
}
metric = gw.isMetricSystem();
}
void loop()
{
sensors.requestTemperatures(); // Fetch temperatures from Dallas
delay(100);
for (int i=0; i<numSensors; i++) {
// Fetch and round temperature to one decimal
float temperature = static_cast<float>(static_cast<int>((metric?sensors.getTempCByIndex(i):sensors.getTempFByIndex(i)) * 10.)) / 10.;
// Only send data if temperature has changed and no error
if (lastTemperature[i] != temperature && temperature != -127.00) {
gw.powerUp(); // Powerup introduces a small delay (which is missing in radio.write powerup)
// Send variable (using registered shortcut) to gw
gw.sendVariable(i, V_TEMP, temperature, 1);
lastTemperature[i]=temperature;
}
}
// Power down the radio. Note that the radio will get powered back up
// on the next write() call.
delay(500);
gw.powerDown();
sleep.pwrDownMode(); //set sleep mode
sleep.sleepDelay(SLEEP_TIME * 1000); //sleep for: sleepTime
}
@@ -0,0 +1,58 @@
#include <Sleep_n0m1.h>
#include <SPI.h>
#include <EEPROM.h>
#include <RF24.h>
#include <Sensor.h>
#include <NewPing.h>
// Set RADIO_ID to something unique in your sensor network (1-254)
// or set to AUTO if you want gw to assign a RADIO_ID for you.
#define RADIO_ID 8
#define TRIGGER_PIN 6 // Arduino pin tied to trigger pin on the ultrasonic sensor.
#define ECHO_PIN 5 // Arduino pin tied to echo pin on the ultrasonic sensor.
#define MAX_DISTANCE 300 // Maximum distance we want to ping for (in centimeters). Maximum sensor distance is rated at 400-500cm.
unsigned long SLEEP_TIME = 5; // Sleep time between reads (in seconds)
Sensor gw(9, 10);
NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE); // NewPing setup of pins and maximum distance.
Sleep sleep;
int lastDist;
boolean metric;
void setup()
{
Serial.begin(BAUD_RATE); // Used to type in characters
gw.begin(RADIO_ID);
// Register all sensors to gw (they will be created as child devices)
gw.sendSensorPresentation(0, S_DISTANCE);
metric = gw.isMetricSystem();
}
void loop()
{
int dist = metric?sonar.ping_cm():sonar.ping_in();
Serial.print("Ping: ");
Serial.print(dist); // Convert ping time to distance in cm and print result (0 = outside set distance range)
Serial.println(metric?" cm":" in");
if (dist != lastDist) {
gw.sendVariable(0, V_DISTANCE, dist);
lastDist = dist;
}
// Power down the radio. Note that the radio will get powered back up
// on the next write() call.
delay(1000); //delay to allow serial to fully print before sleep
gw.powerDown();
sleep.pwrDownMode(); //set sleep mode
sleep.sleepDelay(SLEEP_TIME * 1000); //sleep for: sleepTime
}
@@ -0,0 +1,118 @@
// Use this sensor to measure KWH and Watt of your house meeter
// You need to set the correct pulsefactor of your meeter (blinks per KWH).
// The sensor starts by fetching current KWH value from gateway.
// Reports both KWH and Watt back to gateway.
//
// Unfortunately millis() won't increment when the Arduino is in
// sleepmode. So we cannot make this sensor sleep if we also want
// to calculate/report watt-number.
#include <Sleep_n0m1.h>
#include <SPI.h>
#include <EEPROM.h>
#include <RF24.h>
#include <Sensor.h>
// Set RADIO_ID to something unique in your sensor network (1-254)
// or set to AUTO if you want gw to assign a RADIO_ID for you.
#define RADIO_ID AUTO
#define DIGITAL_INPUT_SENSOR 3 // The digital input you attached your light sensor. (Only 2 and 3 generates interrupt!)
#define PULSE_FACTOR 1000 // Nummber of blinks per KWH of your meeter
#define SLEEP_MODE false // Watt-value can only be reported when sleep mode is false.
#define MAX_WATT 10000 // Max watt value to report. This filetrs outliers.
unsigned long SEND_FREQUENCY = 20; // Minimum time between send (in seconds). We don't wnat to spam the gateway.
#define INTERRUPT DIGITAL_INPUT_SENSOR-2 // Usually the interrupt = pin -2 (on uno/nano anyway)
#define CHILD_ID 5 // Id of the sensor child
Sensor gw(9,10);
Sleep sleep;
//double kwhPerBlink = 1.0/((double)PULSE_FACTOR);
double ppwh = ((double)PULSE_FACTOR)/1000; // Pulses per watt hour
volatile unsigned long pulseCount = 0;
volatile unsigned long lastBlink = 0;
volatile unsigned long watt = 0;
unsigned long oldPulseCount = 0;
unsigned long oldWatt = 0;
double oldKwh;
unsigned long lastSend;
void setup()
{
Serial.begin(BAUD_RATE); // Used to type in characters
gw.begin(RADIO_ID);
// Register this device as power sensor
gw.sendSensorPresentation(CHILD_ID, S_POWER);
// Fetch last known pulse count value from gw
pulseCount = oldPulseCount = atol(gw.getStatus(CHILD_ID, V_VAR1));
Serial.print("Last pulse count from gw:");
Serial.println(pulseCount);
attachInterrupt(INTERRUPT, onPulse, RISING);
lastSend=millis();
}
void loop()
{
unsigned long now = millis();
// Only send values at a maximum frequency or woken up from sleep
if (SLEEP_MODE || now - lastSend > 1000*SEND_FREQUENCY) {
// New watt value has been calculated
if (!SLEEP_MODE && watt != oldWatt) {
// Check that we dont get unresonable large watt value.
// could hapen when long wraps or false interrupt triggered
if (watt<((unsigned long)MAX_WATT)) {
gw.sendVariable(CHILD_ID, V_WATT, watt); // Send watt value to gw
}
Serial.print("Watt:");
Serial.println(watt);
oldWatt = watt;
}
// Pulse cout has changed
if (pulseCount != oldPulseCount) {
gw.sendVariable(CHILD_ID, V_VAR1, pulseCount); // Send kwh value to gw
double kwh = ((double)pulseCount/((double)PULSE_FACTOR));
oldPulseCount = pulseCount;
//Serial.print("Pulse count:");
//Serial.println(pulseCount);
if (kwh != oldKwh) {
gw.sendVariable(CHILD_ID, V_KWH, kwh, 4); // Send kwh value to gw
//Serial.print("KWH:");
//Serial.println(kwh);
oldKwh = kwh;
}
}
lastSend = now;
}
if (SLEEP_MODE) {
delay(300); //delay to allow serial to fully print before sleep
gw.powerDown();
sleep.pwrDownMode(); //set sleep mode
sleep.sleepDelay(SEND_FREQUENCY * 1000); //sleep for: sleepTime
}
}
void onPulse()
{
if (!SLEEP_MODE) {
unsigned long newBlink = micros();
unsigned long interval = newBlink-lastBlink;
if (interval<10000L) { // Sometimes we get interrupt on RISING
return;
}
watt = (3600000000.0 /interval) / ppwh;
lastBlink = newBlink;
}
pulseCount++;
}
@@ -0,0 +1,74 @@
#include <Sleep_n0m1.h>
#include <SPI.h>
#include <EEPROM.h>
#include <RF24.h>
#include <Sensor.h>
#include <DHT.h>
// Set RADIO_ID to something unique in your sensor network (1-254)
// or set to AUTO if you want gw to assign a RADIO_ID for you.
#define RADIO_ID AUTO
#define CHILD_ID_HUM 0
#define CHILD_ID_TEMP 1
#define HUMIDITY_SENSOR_DIGITAL_PIN 3
unsigned long SLEEP_TIME = 30; // Sleep time between reads (in seconds)
Sensor gw(9, 10);
DHT dht;
Sleep sleep;
float lastTemp;
float lastHum;
boolean metric = true;
void setup()
{
Serial.begin(BAUD_RATE); // Used to type in characters
gw.begin(RADIO_ID);
dht.setup(HUMIDITY_SENSOR_DIGITAL_PIN);
// Register all sensors to gw (they will be created as child devices)
gw.sendSensorPresentation(CHILD_ID_HUM, S_HUM);
gw.sendSensorPresentation(CHILD_ID_TEMP, S_TEMP);
metric = gw.isMetricSystem();
}
void loop()
{
delay(dht.getMinimumSamplingPeriod());
float temperature = dht.getTemperature();
if (isnan(temperature)) {
Serial.println("Failed reading temperature from DHT");
} else if (temperature != lastTemp) {
lastTemp = temperature;
if (!metric) {
temperature = dht.toFahrenheit(temperature);
}
gw.sendVariable(CHILD_ID_TEMP, V_TEMP, temperature, 1);
Serial.print("T: ");
Serial.println(temperature);
}
float humidity = dht.getHumidity();
if (isnan(humidity)) {
Serial.println("Failed reading humidity from DHT");
} else if (humidity != lastHum) {
lastHum = humidity;
gw.sendVariable(CHILD_ID_HUM, V_HUM, humidity, 1);
Serial.print("H: ");
Serial.println(humidity);
}
// Power down the radio. Note that the radio will get powered back up
// on the next write() call.
delay(1000); //delay to allow serial to fully print before sleep
gw.powerDown();
sleep.pwrDownMode(); //set sleep mode
sleep.sleepDelay(SLEEP_TIME * 1000); //sleep for: sleepTime
}
@@ -0,0 +1,119 @@
// Example sketch showing how to control ir devices
// An IR LED must be connected to Arduino PWM pin 3.
// An optional ir receiver can be connected to PWM pin 8.
// All receied ir signals will be sent to gateway device stored in VAR_1.
// When binary light on is clicked - sketch will send volume up ir command
// When binary light off is clicked - sketch will send volume down ir command
#include <Sensor.h>
#include <SPI.h>
#include <EEPROM.h>
#include <RF24.h>
#include <IRremote.h>
// Set RADIO_ID to something unique in your sensor network (1-254)
// or set to AUTO if you want gw to assign a RADIO_ID for you.
#define RADIO_ID AUTO
int RECV_PIN = 8;
#define CHILD_1 3 // childId
IRsend irsend;
IRrecv irrecv(RECV_PIN);
decode_results results;
Sensor gw(9,10);
void setup()
{
Serial.begin(BAUD_RATE); // Used to write debug info
irrecv.enableIRIn(); // Start the ir receiver
gw.begin(RADIO_ID);
// Register a sensors to gw. Use binary light for test purposes.
gw.sendSensorPresentation(CHILD_1, S_LIGHT);
}
void loop()
{
if (gw.messageAvailable()) {
message_s message = gw.getMessage();
int incomingRelayStatus = atoi(message.data);
if (incomingRelayStatus == 1) {
irsend.sendNEC(0x1EE17887, 32); // Vol up yamaha ysp-900
} else {
irsend.sendNEC(0x1EE1F807, 32); // Vol down yamaha ysp-900
}
// Start receiving ir again...
irrecv.enableIRIn();
}
if (irrecv.decode(&results)) {
char buffer[10];
sprintf(buffer, "%08lx", results.value);
dump(&results);
// Send ir result to gw
gw.sendVariable(CHILD_1, V_VAR1, buffer);
irrecv.resume(); // Receive the next value
}
}
// Dumps out the decode_results structure.
// Call this after IRrecv::decode()
// void * to work around compiler issue
//void dump(void *v) {
// decode_results *results = (decode_results *)v
void dump(decode_results *results) {
int count = results->rawlen;
if (results->decode_type == UNKNOWN) {
Serial.print("Unknown encoding: ");
}
else if (results->decode_type == NEC) {
Serial.print("Decoded NEC: ");
}
else if (results->decode_type == SONY) {
Serial.print("Decoded SONY: ");
}
else if (results->decode_type == RC5) {
Serial.print("Decoded RC5: ");
}
else if (results->decode_type == RC6) {
Serial.print("Decoded RC6: ");
}
else if (results->decode_type == PANASONIC) {
Serial.print("Decoded PANASONIC - Address: ");
Serial.print(results->panasonicAddress,HEX);
Serial.print(" Value: ");
}
else if (results->decode_type == JVC) {
Serial.print("Decoded JVC: ");
}
Serial.print(results->value, HEX);
Serial.print(" (");
Serial.print(results->bits, DEC);
Serial.println(" bits)");
Serial.print("Raw (");
Serial.print(count, DEC);
Serial.print("): ");
for (int i = 0; i < count; i++) {
if ((i % 2) == 1) {
Serial.print(results->rawbuf[i]*USECPERTICK, DEC);
}
else {
Serial.print(-(int)results->rawbuf[i]*USECPERTICK, DEC);
}
Serial.print(" ");
}
Serial.println("");
}
@@ -0,0 +1,47 @@
#include <Sleep_n0m1.h>
#include <SPI.h>
#include <RF24.h>
#include <EEPROM.h>
#include <Sensor.h>
// Set RADIO_ID to something unique in your sensor network (1-254)
// or set to AUTO if you want gw to assign a RADIO_ID for you.
#define RADIO_ID AUTO
#define CHILD_ID_LIGHT 0
#define LIGHT_SENSOR_ANALOG_PIN 0
unsigned long SLEEP_TIME = 30; // Sleep time between reads (in seconds)
Sensor gw(9,10);
int lastLightLevel;
Sleep sleep;
void setup()
{
Serial.begin(BAUD_RATE); // Used to type in characters
gw.begin(RADIO_ID);
// Register all sensors to gateway (they will be created as child devices)
gw.sendSensorPresentation(CHILD_ID_LIGHT, S_LIGHT_LEVEL);
}
void loop()
{
int lightLevel = (1023-analogRead(LIGHT_SENSOR_ANALOG_PIN))/10.23;
Serial.println(lightLevel);
if (lightLevel != lastLightLevel) {
gw.sendVariable(CHILD_ID_LIGHT, V_LIGHT_LEVEL, lightLevel);
lastLightLevel = lightLevel;
}
// Power down the radio. Note that the radio will get powered back up
// on the next write() call.
delay(1000); //delay to allow serial to fully print before sleep
gw.powerDown();
sleep.pwrDownMode(); //set sleep mode
sleep.sleepDelay(SLEEP_TIME * 1000); //sleep for: sleepTime
}
@@ -0,0 +1,45 @@
#include <Sleep_n0m1.h>
#include <SPI.h>
#include <EEPROM.h>
#include <RF24.h>
#include <Sensor.h>
// Set RADIO_ID to something unique in your sensor network (1-254)
// or set to AUTO if you want gw to assign a RADIO_ID for you.
#define RADIO_ID AUTO
#define DIGITAL_INPUT_SENSOR 3 // The digital input you attached your motion sensor. (Only 2 and 3 generates interrupt!)
#define INTERRUPT DIGITAL_INPUT_SENSOR-2 // Usually the interrupt = pin -2 (on uno/nano anyway)
#define CHILD_ID 0 // Id of the sensor child
Sensor gw(9,10);
Sleep sleep;
void setup()
{
Serial.begin(BAUD_RATE); // Used to type in characters
gw.begin(RADIO_ID);
pinMode(DIGITAL_INPUT_SENSOR, INPUT); // sets the motion sensor digital pin as input
// Register all sensors to gw (they will be created as child devices)
gw.sendSensorPresentation(CHILD_ID, S_MOTION);
}
void loop()
{
// Read digital motion value
boolean tripped = digitalRead(DIGITAL_INPUT_SENSOR) == HIGH;
Serial.println(tripped);
gw.sendVariable(CHILD_ID, V_TRIPPED, tripped?"1":"0"); // Send tripped value to gw
// Power down the radio. Note that the radio will get powered back up
// on the next write() call.
delay(200); //delay to allow serial to fully print before sleep
gw.powerDown();
sleep.pwrDownMode(); //set sleep mode
sleep.sleepInterrupt(INTERRUPT,CHANGE);
}
@@ -0,0 +1,198 @@
#include <Sleep_n0m1.h>
#include <SPI.h>
#include <EEPROM.h>
#include <RF24.h>
#include <Sensor.h>
#include <Wire.h>
#include <Adafruit_BMP085.h>
// Set RADIO_ID to something unique in your sensor network (1-254)
// or set to AUTO if you want gw to assign a RADIO_ID for you.
#define RADIO_ID AUTO
#define LIGHT_SENSOR_ANALOG_PIN 0
unsigned long SLEEP_TIME = 60; // Sleep time between reads (in seconds)
Adafruit_BMP085 bmp = Adafruit_BMP085(); // Digital Pressure Sensor
Sensor gw(9, 10);
float lastPressure = -1;
float lastTemp = -1;
int lastForecast = -1;
Sleep sleep;
char *weather[] = {"stable","sunny","cloudy","unstable","thunderstorm","unknown"};
int minutes;
float pressureSamples[180];
int minuteCount = 0;
bool firstRound = true;
float pressureAvg[7];
float dP_dt;
boolean metric;
void setup() {
Serial.begin(BAUD_RATE); // Used to type in characters
gw.begin(RADIO_ID);
if (!bmp.begin()) {
Serial.println("Could not find a valid BMP085 sensor, check wiring!");
while (1) { }
}
// Register sensors to gw (they will be created as child devices)
gw.sendSensorPresentation(0, S_BARO);
gw.sendSensorPresentation(1, S_TEMP);
metric = gw.isMetricSystem();
}
void loop() {
float pressure = bmp.readPressure();
float temperature = bmp.readTemperature();
if (!metric) {
// Convert to fahrenheit
temperature = temperature * 9.0 / 5.0 + 32.0;
}
int forecast = sample(pressure);
Serial.print("Temperature = ");
Serial.print(temperature);
Serial.println(metric?" *C":" *F");
Serial.print("Pressure = ");
Serial.print(pressure);
Serial.println(" Pa");
Serial.println(weather[forecast]);
if (temperature != lastTemp) {
gw.sendVariable(1, V_TEMP, temperature,1);
lastTemp = temperature;
}
if (pressure != lastPressure) {
gw.sendVariable(0, V_PRESSURE, pressure, 0);
lastPressure = pressure;
}
if (forecast != lastForecast) {
gw.sendVariable(0, V_FORECAST, weather[forecast]);
lastForecast = forecast;
}
/*
DP/Dt explanation
0 = "Stable Weather Pattern"
1 = "Slowly rising Good Weather", "Clear/Sunny "
2 = "Slowly falling L-Pressure ", "Cloudy/Rain "
3 = "Quickly rising H-Press", "Not Stable"
4 = "Quickly falling L-Press", "Thunderstorm"
5 = "Unknown (More Time needed)
*/
// Power down the radio. Note that the radio will get powered back up
// on the next write() call.
delay(1000); //delay to allow serial to fully print before sleep
gw.powerDown();
sleep.pwrDownMode(); //set sleep mode
sleep.sleepDelay(SLEEP_TIME * 1000); // sleep to conserve power
}
int sample(float pressure) {
// Algorithm found here
// http://www.freescale.com/files/sensors/doc/app_note/AN3914.pdf
if (minuteCount > 180)
minuteCount = 6;
pressureSamples[minuteCount] = pressure;
minuteCount++;
if (minuteCount == 5) {
// Avg pressure in first 5 min, value averaged from 0 to 5 min.
pressureAvg[0] = ((pressureSamples[1] + pressureSamples[2]
+ pressureSamples[3] + pressureSamples[4] + pressureSamples[5])
/ 5);
} else if (minuteCount == 35) {
// Avg pressure in 30 min, value averaged from 0 to 5 min.
pressureAvg[1] = ((pressureSamples[30] + pressureSamples[31]
+ pressureSamples[32] + pressureSamples[33]
+ pressureSamples[34]) / 5);
float change = (pressureAvg[1] - pressureAvg[0]);
if (firstRound) // first time initial 3 hour
dP_dt = ((65.0 / 1023.0) * 2 * change); // note this is for t = 0.5hour
else
dP_dt = (((65.0 / 1023.0) * change) / 1.5); // divide by 1.5 as this is the difference in time from 0 value.
} else if (minuteCount == 60) {
// Avg pressure at end of the hour, value averaged from 0 to 5 min.
pressureAvg[2] = ((pressureSamples[55] + pressureSamples[56]
+ pressureSamples[57] + pressureSamples[58]
+ pressureSamples[59]) / 5);
float change = (pressureAvg[2] - pressureAvg[0]);
if (firstRound) //first time initial 3 hour
dP_dt = ((65.0 / 1023.0) * change); //note this is for t = 1 hour
else
dP_dt = (((65.0 / 1023.0) * change) / 2); //divide by 2 as this is the difference in time from 0 value
} else if (minuteCount == 95) {
// Avg pressure at end of the hour, value averaged from 0 to 5 min.
pressureAvg[3] = ((pressureSamples[90] + pressureSamples[91]
+ pressureSamples[92] + pressureSamples[93]
+ pressureSamples[94]) / 5);
float change = (pressureAvg[3] - pressureAvg[0]);
if (firstRound) // first time initial 3 hour
dP_dt = (((65.0 / 1023.0) * change) / 1.5); // note this is for t = 1.5 hour
else
dP_dt = (((65.0 / 1023.0) * change) / 2.5); // divide by 2.5 as this is the difference in time from 0 value
} else if (minuteCount == 120) {
// Avg pressure at end of the hour, value averaged from 0 to 5 min.
pressureAvg[4] = ((pressureSamples[115] + pressureSamples[116]
+ pressureSamples[117] + pressureSamples[118]
+ pressureSamples[119]) / 5);
float change = (pressureAvg[4] - pressureAvg[0]);
if (firstRound) // first time initial 3 hour
dP_dt = (((65.0 / 1023.0) * change) / 2); // note this is for t = 2 hour
else
dP_dt = (((65.0 / 1023.0) * change) / 3); // divide by 3 as this is the difference in time from 0 value
} else if (minuteCount == 155) {
// Avg pressure at end of the hour, value averaged from 0 to 5 min.
pressureAvg[5] = ((pressureSamples[150] + pressureSamples[151]
+ pressureSamples[152] + pressureSamples[153]
+ pressureSamples[154]) / 5);
float change = (pressureAvg[5] - pressureAvg[0]);
if (firstRound) // first time initial 3 hour
dP_dt = (((65.0 / 1023.0) * change) / 2.5); // note this is for t = 2.5 hour
else
dP_dt = (((65.0 / 1023.0) * change) / 3.5); // divide by 3.5 as this is the difference in time from 0 value
} else if (minuteCount == 180) {
// Avg pressure at end of the hour, value averaged from 0 to 5 min.
pressureAvg[6] = ((pressureSamples[175] + pressureSamples[176]
+ pressureSamples[177] + pressureSamples[178]
+ pressureSamples[179]) / 5);
float change = (pressureAvg[6] - pressureAvg[0]);
if (firstRound) // first time initial 3 hour
dP_dt = (((65.0 / 1023.0) * change) / 3); // note this is for t = 3 hour
else
dP_dt = (((65.0 / 1023.0) * change) / 4); // divide by 4 as this is the difference in time from 0 value
pressureAvg[0] = pressureAvg[5]; // Equating the pressure at 0 to the pressure at 2 hour after 3 hours have past.
firstRound = false; // flag to let you know that this is on the past 3 hour mark. Initialized to 0 outside main loop.
}
if (minuteCount < 35 && firstRound) //if time is less than 35 min on the first 3 hour interval.
return 5; // Unknown, more time needed
else if (dP_dt < (-0.25))
return 4; // Quickly falling LP, Thunderstorm, not stable
else if (dP_dt > 0.25)
return 3; // Quickly rising HP, not stable weather
else if ((dP_dt > (-0.25)) && (dP_dt < (-0.05)))
return 2; // Slowly falling Low Pressure System, stable rainy weather
else if ((dP_dt > 0.05) && (dP_dt < 0.25))
return 1; // Slowly rising HP stable good weather
else if ((dP_dt > (-0.05)) && (dP_dt < 0.05))
return 0; // Stable weather
else
return 5; // Unknown
}
@@ -0,0 +1,69 @@
// Example sketch showing how to control physical relays.
#include <Relay.h>
#include <SPI.h>
#include <EEPROM.h>
#include <RF24.h>
// Set RADIO_ID to something unique in your sensor network (1-254)
// or set to AUTO if you want gw to assign a RADIO_ID for you.
#define RADIO_ID AUTO
#define RELAY_1 3 // Arduino Digital I/O pin number for first relay (second on pin+1 etc)
#define NUMBER_OF_RELAYS 2
#define RELAY_ON 0
#define RELAY_OFF 1
Relay gw(9,10);
void setup()
{
Serial.begin(BAUD_RATE); // Used to write debug info
gw.begin(RADIO_ID);
// 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);
}
// Fetch relay status
for (int i=0; i<NUMBER_OF_RELAYS;i++) {
// Make sure relays are off when starting up
digitalWrite(RELAY_1+i, RELAY_OFF);
// 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
}
}
/*
* Example on how to asynchronously check for new messages from gw
*/
void loop()
{
if (gw.messageAvailable()) {
message_s message = gw.getMessage();
setRelayStatus(message);
}
}
void setRelayStatus(message_s message) {
if (message.header.messageType=M_SET_VARIABLE &&
message.header.type==V_LIGHT) {
int incomingRelayStatus = atoi(message.data);
// Change relay state
digitalWrite(message.header.childId, incomingRelayStatus==1?RELAY_ON:RELAY_OFF);
// Write some debug info
Serial.print("Incoming change for relay on pin:");
Serial.print(message.header.childId);
Serial.print(", New status: ");
Serial.println(incomingRelayStatus);
}
}
@@ -0,0 +1,96 @@
// Example sketch för a "light switch" where you can control light or something
// else from both vera and a local physical button (connected between digital
// pin 3 and +5V).
#include <Relay.h>
#include <SPI.h>
#include <EEPROM.h>
#include <RF24.h>
// Set RADIO_ID to something unique in your sensor network (1-254)
// or set to AUTO if you want gw to assign a RADIO_ID for you.
#define RADIO_ID AUTO
#define RELAY_PIN 4 // Arduino Digital I/O pin number for relay
#define BUTTON_PIN 3 // Arduino Digital I/O pin number for button (with 10k pulldown)
#define CHILD_ID 1 // Id of the sensor child
#define RELAY_ON 0
#define RELAY_OFF 1
int buttVal;
int previousVal;
int state;
int switched = 0;
long pressTime = 0; // the last time the button pin was pressed
long debounce = 200; // the debounce time, increase if the output flickers
Relay gw(9,10);
void setup()
{
Serial.begin(BAUD_RATE); // Used to write debug info
gw.begin(RADIO_ID);
// Set buttonPin as Input
pinMode(BUTTON_PIN, INPUT);
buttVal = digitalRead(BUTTON_PIN); //read initial state
// Register all sensors to gw (they will be created as child devices)
gw.sendSensorPresentation(CHILD_ID, S_LIGHT);
// Make sure relays are off when starting up
digitalWrite(RELAY_PIN, RELAY_OFF);
// Then set relay pins in output mode
pinMode(RELAY_PIN, OUTPUT);
// Request/wait for relay status
gw.getStatus(CHILD_ID, V_LIGHT);
setRelayStatus(gw.getMessage()); // Wait here until status message arrive from gw
}
/*
* Example on how to asynchronously check for new messages from gw
*/
void loop()
{
if (gw.messageAvailable()) {
// ot new messsage from gw
message_s message = gw.getMessage();
setRelayStatus(message);
}
buttVal = digitalRead(BUTTON_PIN); // read input value and store it in val
if (buttVal == HIGH && previousVal == LOW) {
// Start counter from when button was pressed
pressTime = millis();
switched = 0;
}
if (buttVal == HIGH && switched == 0 && millis() - pressTime > debounce) {
// Switch state if button pressed more than 200 msec
state = state==1?0:1;
gw.sendVariable(CHILD_ID, V_LIGHT, state); // We will receive an ack message
switched = 1; // No more switches until button is released
}
previousVal = buttVal;
}
void setRelayStatus(message_s message) {
if (message.header.type==V_LIGHT) { // This could be M_ACK_VARIABLE or M_SET_VARIABLE
state = atoi(message.data);
// Change relay state
digitalWrite(RELAY_PIN, state==1?RELAY_ON:RELAY_OFF);
// Write some debug info
Serial.print(message.header.messageType == M_ACK_VARIABLE ? "Button":"Gateway");
Serial.print(" change. New state: ");
Serial.println(state == 1 ?"on":"off" );
}
}
@@ -0,0 +1,31 @@
// Example sketch showing how to create a node thay relays messages
// from nodes far from gateway back to gateway.
// Important here is that node uses Relay-class and calls
// gw.messageAvailable() frequently. This sketch should not
// sleep.
#include <Relay.h>
#include <SPI.h>
#include <EEPROM.h>
#include <RF24.h>
// Set RADIO_ID to something unique in your sensor network (1-254)
// or set to AUTO if you want gw to assign a RADIO_ID for you.
#define RADIO_ID AUTO
Relay gw(9,10);
void setup()
{
Serial.begin(BAUD_RATE); // Used to write debug info
gw.begin(RADIO_ID);
}
void loop()
{
if (gw.messageAvailable()) {
// Incoming message for this node...
}
}
@@ -0,0 +1,71 @@
// Example showing how to create an atuator for a servo.
// Connect red to +5V, Black or brown to GND and the last cable to Digital pin 3.
// The servo consumes much power and should probably have its own powersource.'
// The arduino might spontanally restart if too much power is used (happend
// to me when servo tried to pass the extreme positions = full load).
#include <Sensor.h>
#include <SPI.h>
#include <EEPROM.h>
#include <Servo.h>
#include <RF24.h>
#define SERVO_DIGITAL_OUT_PIN 3
#define SERVO_MIN 30 // Fine tune your servos min. 0-180
#define SERVO_MAX 180 // Fine tune your servos max. 0-180
// Set RADIO_ID to something unique in your sensor network (1-254)
// or set to AUTO if you want gw to assign a RADIO_ID for you.
#define RADIO_ID AUTO
#define CHILD_ID 10 // Id of the sensor child
Sensor gw(9, 10);
Servo myservo; // create servo object to control a servo
// a maximum of eight servo objects can be created Sensor gw(9,10);
void setup()
{
Serial.begin(BAUD_RATE);
gw.begin(RADIO_ID);
myservo.attach(SERVO_DIGITAL_OUT_PIN);
// Register all sensors to gw (they will be created as child devices)
gw.sendSensorPresentation(CHILD_ID, S_COVER);
// Fetch servo state at startup
gw.getStatus(CHILD_ID, V_DIMMER);
setRelayStatus(gw.getMessage()); // Wait here until status message arrive from gw
}
void loop()
{
if (gw.messageAvailable()) {
// New messsage from gw
message_s message = gw.getMessage();
setRelayStatus(message);
}
}
void setRelayStatus(message_s message) {
if (message.header.type==V_DIMMER) { // This could be M_ACK_VARIABLE or M_SET_VARIABLE
int val = atoi(message.data);
myservo.write(SERVO_MIN + (SERVO_MAX-SERVO_MIN)/100 * val); // sets the servo position 0-180
// Write some debug info
Serial.print("Servo changed. new state: ");
Serial.println(val);
} else if (message.header.type==V_UP) {
Serial.println("Servo UP command");
myservo.write(SERVO_MAX);
gw.sendVariable(CHILD_ID, V_DIMMER, 100);
} else if (message.header.type==V_DOWN) {
Serial.println("Servo DOWN command");
myservo.write(SERVO_MIN);
gw.sendVariable(CHILD_ID, V_DIMMER, 0);
} else if (message.header.type==V_STOP) {
Serial.println("Servo STOP command");
// Servo is pretty fast.. don't think we have time to send a stop
}
}
@@ -0,0 +1,47 @@
#include <Sleep_n0m1.h>
#include <SPI.h>
#include <EEPROM.h>
#include <RF24.h>
#include <Sensor.h>
// Set RADIO_ID to something unique in your sensor network (1-254)
// or set to AUTO if you want gw to assign a RADIO_ID for you.
#define RADIO_ID AUTO
#define DIGITAL_INPUT_SOIL_SENSOR 3 // Digital input did you attach your soil sensor.
#define INTERRUPT DIGITAL_INPUT_SOIL_SENSOR-2 // Usually the interrupt = pin -2 (on uno/nano anyway)
#define CHILD_ID 0 // Id of the sensor child
Sensor gw(9, 10);
Sleep sleep;
void setup()
{
Serial.begin(BAUD_RATE); // Used to type in characters
gw.begin(RADIO_ID);
pinMode(DIGITAL_INPUT_SOIL_SENSOR, INPUT); // sets the soil sensor digital pin as input
// Register all sensors to gw (they will be created as child devices)
gw.sendSensorPresentation(CHILD_ID, S_MOTION);
}
int lastSoilValue = -1;
void loop()
{
// Read digital soil value
int soilValue = digitalRead(DIGITAL_INPUT_SOIL_SENSOR); // 1 = Not triggered, 0 = In soil with water
if (soilValue != lastSoilValue) {
Serial.println(soilValue);
gw.sendVariable(CHILD_ID, V_TRIPPED, soilValue==0?"1":"0"); // Send the inverse to gw as tripped should be when no water in soil
lastSoilValue = soilValue;
}
// Power down the radio. Note that the radio will get powered back up
// on the next write() call.
gw.powerDown();
sleep.pwrDownMode(); //set sleep mode
sleep.sleepInterrupt(INTERRUPT,CHANGE);
}
@@ -0,0 +1,68 @@
// Example sketch showing how to request time from vera
// and how to set a cutom variable on a sensor
#include <SPI.h>
#include <EEPROM.h>
#include <RF24.h>
#include <Sensor.h>
#include <Time.h>
// Set RADIO_ID to something unique in your sensor network (1-254)
// or set to AUTO if you want gw to assign a RADIO_ID for you.
#define RADIO_ID AUTO
#define CHILD_ID 0 // Id of the sensor child
Sensor gw(9, 10);
void setup()
{
Serial.begin(BAUD_RATE); // Used to type in characters
gw.begin(RADIO_ID);
// Register any sensortype. This example we just create a motion sensor.
gw.sendSensorPresentation(CHILD_ID, S_MOTION);
setSyncProvider(getTime);
setSyncInterval(3600); // get time from gw every hour
}
unsigned long getTime() {
return gw.getTime(); // Vera is time provider
}
void loop()
{
// long time = gw.getTime();
// setTime(time);
Serial.print(hour());
printDigits(minute());
printDigits(second());
Serial.print(" ");
Serial.print(day());
Serial.print(" ");
Serial.print(month());
Serial.print(" ");
Serial.print(year());
Serial.println();
// Ok now send time back to gw and store in in Variable1
// of the motion sensor device
gw.sendVariable(CHILD_ID, V_VAR1, minute());
// wait 10 seconds...
delay(10000);
}
void printDigits(int digits){
// utility function for digital clock display: prints preceding colon and leading 0
Serial.print(":");
if(digits < 10)
Serial.print('0');
Serial.print(digits);
}
@@ -0,0 +1,151 @@
//
// Use this sensor to measure volume and flow of your house watermeter.
// You need to set the correct pulsefactor of your meter (pulses per m3).
// The sensor starts by fetching current volume reading from gateway (VAR 1).
// Reports both volume and flow back to gateway.
//
// Unfortunately millis() won't increment when the Arduino is in
// sleepmode. So we cannot make this sensor sleep if we also want
// to calculate/report flow.
//
// Sensor on pin 3
//
//
//
#include <Relay.h>
#include <Sleep_n0m1.h>
#include <SPI.h>
#include <EEPROM.h>
#include <RF24.h>
#include <Sensor.h>
// Set RADIO_ID to something unique in your sensor network (1-254)
// or set to AUTO if you want gw to assign a RADIO_ID for you.
#define RADIO_ID AUTO
#define DIGITAL_INPUT_SENSOR 3 // The digital input you attached your sensor. (Only 2 and 3 generates interrupt!)
#define PULSE_FACTOR 1000 // Nummber of blinks per m3 of your meter (One rotation/liter)
#define SLEEP_MODE false // flowvalue can only be reported when sleep mode is false.
#define MAX_FLOW 40 // Max flow (l/min) value to report. This filetrs outliers.
unsigned long SEND_FREQUENCY = 20; // Minimum time between send (in seconds). We don't want to spam the gateway.
#define INTERRUPT DIGITAL_INPUT_SENSOR-2 // Usually the interrupt = pin -2 (on uno/nano anyway)
#define CHILD_ID 5 // Id of the sensor child
Sensor gw(9,10);
//enable sleep
Sleep sleep;
double ppl = ((double)PULSE_FACTOR)/1000; // Pulses per liter
volatile unsigned long pulseCount = 0;
volatile unsigned long lastBlink = 0;
volatile double flow = 0;
unsigned long oldPulseCount = 0;
unsigned long newBlink = 0;
double oldflow = 0;
double volume;
double oldvolume;
unsigned long lastSend;
unsigned long lastPulse;
unsigned long currentTime;
boolean metric;
void setup()
{
Serial.begin(BAUD_RATE); // Used to type in characters
gw.begin(RADIO_ID);
// Register this device as Waterflow sensor
gw.sendSensorPresentation(CHILD_ID, S_WATER);
// Fetch last known pulse count value from gw
pulseCount = oldPulseCount = atol(gw.getStatus(CHILD_ID, V_VAR1));
//Serial.print("Last pulse count from gw:");
//Serial.println(pulseCount);
attachInterrupt(INTERRUPT, onPulse, RISING);
lastSend = millis();
}
void loop()
{
currentTime = millis();
// Only send values at a maximum frequency or woken up from sleep
if (SLEEP_MODE || currentTime - lastSend > 1000*SEND_FREQUENCY) {
// New flow value has been calculated
if (!SLEEP_MODE && flow != oldflow) {
// Check that we dont get unresonable large flow value.
// could hapen when long wraps or false interrupt triggered
if (flow<((unsigned long)MAX_FLOW)) {
gw.sendVariable(CHILD_ID, V_FLOW, flow, 2); // Send flow value to gw
}
//Serial.print("l/min:");
//Serial.println(flow);
oldflow = flow;
}
// No Pulse count in 2min
//Serial.print("currentTime");
//Serial.println(currentTime);
//Serial.print("lastPulse");
//Serial.println(lastPulse);
if(currentTime - lastPulse > 120000){
flow = 0;
}
// Pulse count has changed
if (pulseCount != oldPulseCount) {
gw.sendVariable(CHILD_ID, V_VAR1, pulseCount); // Send volumevalue to gw VAR1
double volume = ((double)pulseCount/((double)PULSE_FACTOR));
oldPulseCount = pulseCount;
//Serial.print("Pulse count:");
//Serial.println(pulseCount);
if (volume != oldvolume) {
gw.sendVariable(CHILD_ID, V_VOLUME, volume, 3); // Send volume value to gw
//Serial.print("m3:");
//Serial.println(volume, 3);
oldvolume = volume;
}
}
lastSend = currentTime;
}
if (SLEEP_MODE) {
delay(300); //delay to allow serial to fully print before sleep
gw.powerDown();
sleep.pwrDownMode(); //set sleep mode
sleep.sleepDelay(SEND_FREQUENCY * 1000); //sleep for: sleepTime
}
}
void onPulse()
{
if (!SLEEP_MODE) {
unsigned long newBlink = micros();
unsigned long interval = newBlink-lastBlink;
lastPulse = millis();
if (interval<500000L) {
// Sometimes we get interrupt on RISING, 500000 = 0.5sek debounce ( max 120 l/min)
return;
}
flow = (60000000.0 /interval) / ppl;
lastBlink = newBlink;
//Serial.println(flow, 4);
}
pulseCount++;
}