This project will be a tutorial for how to build a variety of long range wireless sensors, and how to integrate these sensors into a sophisticated open source home automation server. Aside from looking at a mobile app to see what's happening, you'll also receive timely email and audio notifications. This is a long project, but you can jump to the sensor you're interested in building.
These elements are the focus of the design:
Low Cost. Each sensor node costs less than $20 to make, so you can inexpensively scale up.
Flexibility. Arduino based nodes allow anyone to extend the system to their particular sensing needs. You're not tied to only the examples I'm providing, even though I strive to provide many examples.
Very good reliability, up-time, and wireless sensor range.
I'm providing both a battery powered and wall-power sensor node design
/*
RFM69 Pinout:
MOSI = 11
MISO = 12 <<<<
SCK = 13
SS = 8
*/
/*
Ethernet Pinout:
MOSI = 11
MISO = 12
SCK = 13
SS = 10
DI00 to Pin 2 (interrupt)
*/
//general --------------------------------
#define SERIAL_BAUD 115200
#if 0
#define DEBUG1(expression) Serial.print(expression)
#define DEBUG2(expression, arg) Serial.print(expression, arg)
#define DEBUGLN1(expression) Serial.println(expression)
#else
#define DEBUG1(expression)
#define DEBUG2(expression, arg)
#define DEBUGLN1(expression)
#endif
//RFM69 ----------------------------------
#include <RFM69.h>
#include <SPI.h>
#define NODEID 1 //unique for each node on same network
#define NETWORKID 100 //the same on all nodes that talk to each other
//#define FREQUENCY RF69_433MHZ
//#define FREQUENCY RF69_868MHZ
#define FREQUENCY RF69_915MHZ
#define ENCRYPTKEY "MysampleEncryptKey" //exactly the same 16 characters/bytes on all nodes!
#define IS_RFM69HW //uncomment only for RFM69HW! Leave out if you have RFM69W!
#define ACK_TIME 30 // max # of ms to wait for an ack
#define RFM69_SS 8
RFM69 radio(RFM69_SS);
bool promiscuousMode = false; //set to 'true' to sniff all packets on the same network
#include <Ethernet.h>
bool conn_ok;
bool mydebug = true;
//Ethernet
byte mac[] = {
0x90, 0xA2, 0xDA, 0x0D, 0x11, 0x11
};
byte server[] = {192, 168, 0, 134 }; //Linux server address
IPAddress ip(192, 168, 0, 4); //address of gateway device
EthernetClient ethClient;
#define DHCP_RETRY 500
// Mosquitto---------------
#include <PubSubClient.h>
PubSubClient client(server, 1883, callback, ethClient);
#define MQTT_CLIENT_ID "arduinoClient"
#define MQTT_RETRY 500
int sendMQTT = 0;
unsigned long MQTT_reconnect = 0;
void MQTTSendInt(PubSubClient* _client, int node, int sensor, int var, int val);
void MQTTSendULong(PubSubClient* _client, int node, int sensor, int var, unsigned long val);
void MQTTSendFloat(PubSubClient* _client, int node, int sensor, int var, float val);
//use LED for indicating MQTT connection status.
int led = 13;
typedef struct {
int nodeID;
int sensorID;
unsigned long var1_usl;
float var2_float;
float var3_float;
}
Payload;
Payload theData;
volatile struct
{
int nodeID;
int sensorID;
unsigned long var1_usl;
float var2_float;
float var3_float; //
int var4_int;
}
SensorNode;
void setup()
{
Serial.begin(SERIAL_BAUD);
Serial.println("Rebooting Ethernet_RFM_Gateways_Combinedv1.1");
//Ethernet -------------------------
//Ethernet.begin(mac, ip);
// //wait for IP address
while (Ethernet.begin(mac) != 1) {
Serial.println("Error getting IP address via DHCP, trying again...");
delay(DHCP_RETRY);
}
Serial.println("ethernet OK");
// print your local IP address:
Serial.println("My IP address: ");
for (byte thisByte = 0; thisByte < 4; thisByte++) {
// print the value of each byte of the IP address:
Serial.print(Ethernet.localIP()[thisByte], DEC);
Serial.print(".");
}
Serial.println();
Serial.println("Connecting to MQTT via ethernet: ");
// Mosquitto ------------------------------
while (client.connect(MQTT_CLIENT_ID) != 1) {
DEBUGLN1("Error connecting to MQTT");
delay(MQTT_RETRY);
}
Serial.println("Connected... ");
//RFM69 ---------------------------
Serial.println("Initializing radio");
radio.initialize(FREQUENCY, NODEID, NETWORKID);
#ifdef IS_RFM69HW
radio.setHighPower(); //uncomment only for RFM69HW!
Serial.println("setHighPower ok ");
#endif
radio.encrypt(ENCRYPTKEY);
radio.promiscuous(promiscuousMode);
Serial.println("Radio initialized");
char buff[50];
sprintf(buff, "\nListening at %d Mhz...", FREQUENCY == RF69_433MHZ ? 433 : FREQUENCY == RF69_868MHZ ? 868 : 915);
Serial.println(buff);
Serial.println("setup complete");
} // end of setup
byte ackCount = 0;
long watchdogInterval = 2000;
long watchdog = 0;
void loop() {
// calling client.loop too often block the system at some point quite early (~up to 5 loop)
// Here is a temporized call to it on a regular interval
// This need to be as fast as the fastest sensor received
// if (millis() > watchdog) {
// Serial.print("loop ");
// Serial.println(millis());
// watchdog += watchdogInterval;
// //client.loop needs to run every iteration. Previous version did not. Big opps.
// client.loop();
// }
//Serial.println("Other loop");
if (radio.receiveDone()) {
if (mydebug) Serial.print('[');
if (mydebug) Serial.print(radio.SENDERID, DEC);
if (mydebug) Serial.print("] ");
if (promiscuousMode) {
if (mydebug) Serial.print("to [");
if (mydebug) Serial.print(radio.TARGETID, DEC);
if (mydebug) Serial.print("] ");
}
if (mydebug) Serial.println();
if (mydebug) Serial.println("before checking payload");
if (radio.DATALEN != sizeof(Payload))
Serial.println(F("Invalid payload received, not matching Payload struct!"));
else {
theData = *(Payload*)radio.DATA; //assume radio.DATA actually contains our struct and not something else
if (mydebug) Serial.println("payload is valid");
//save it for i2c:
SensorNode.nodeID = theData.nodeID;
SensorNode.sensorID = theData.sensorID;
SensorNode.var1_usl = theData.var1_usl;
SensorNode.var2_float = theData.var2_float;
SensorNode.var3_float = theData.var3_float;
SensorNode.var4_int = radio.RSSI;
sendMQTT = 1;
}
if (radio.ACK_REQUESTED)
{
if (mydebug) Serial.println("ACK Requested");
byte theNodeID = radio.SENDERID;
radio.sendACK();
// When a node requests an ACK, respond to the ACK
// and also send a packet requesting an ACK (every 3rd one only)
// This way both TX/RX NODE functions are tested on 1 end at the GATEWAY
if (ackCount++ % 3 == 0)
{
//Serial.print(" Pinging node ");
//Serial.print(theNodeID);
//Serial.print(" - ACK...");
//delay(3); //need this when sending right after reception .. ?
//if (radio.sendWithRetry(theNodeID, "ACK TEST", 8, 0)) // 0 = only 1 attempt, no retries
// Serial.print("ok!");
//else Serial.print("nothing");
}
}//end if radio.ACK_REQESTED
} //end if radio.receive
if (sendMQTT == 1)
{
if (mydebug) Serial.println("starting MQTT send");
conn_ok = client.connected();
if (conn_ok == 1)
{
digitalWrite(led, HIGH);
if (mydebug) Serial.println("MQTT connected OK from MQTT Send");
}
else
{
digitalWrite(led, LOW);
if (mydebug) Serial.println("MQTT NOT connected OK from MQTT Send");
}
//no connection, reconnect
if (conn_ok == 0)
{
client.disconnect();
delay(5000);
while (client.connect("arduinoClient") != 1)
{
digitalWrite(led, LOW);
Serial.println("Error connecting to MQTT");
delay(4000);
digitalWrite(led, HIGH);
}
digitalWrite(led, HIGH);
Serial.println("reconnected to MQTT");
MQTT_reconnect = millis();
}
int varnum;
char buff_topic[6];
char buff_message[12];
Serial.println("******************************");
//send var1_usl
varnum = 1;
buff_topic[6];
buff_message[12];
//sprintf creates buff_topic by combining SensorNode.nodeID, SensorNode.sensorID, varnum
sprintf(buff_topic, "%02d%01d%01d", SensorNode.nodeID, SensorNode.sensorID, varnum);
dtostrf (SensorNode.var1_usl, 10, 1, buff_message);
client.publish(buff_topic, buff_message);
Serial.println("Publishing");
decodeBuffTopic(buff_topic);
Serial.print("buff_topic: ");
Serial.print(buff_topic);
Serial.print(" varnum 1: ");
Serial.println(buff_message);
//%02d means format the integer with 2 digits, left padding with zeros
//send var2_float
varnum = 2;
buff_topic[6];
buff_message[7];
sprintf(buff_topic, "%02d%01d%01d", SensorNode.nodeID, SensorNode.sensorID, varnum);
dtostrf (SensorNode.var2_float, 2, 1, buff_message);
client.publish(buff_topic, buff_message);
decodeBuffTopic(buff_topic);
Serial.print("buff_topic: ");
Serial.print(buff_topic);
Serial.print(" varnum 2: ");
Serial.println(buff_message);
delay(200);
//send var3_float
varnum = 3;
sprintf(buff_topic, "%02d%01d%01d", SensorNode.nodeID, SensorNode.sensorID, varnum);
dtostrf (SensorNode.var3_float, 2, 2, buff_message);
client.publish(buff_topic, buff_message);
decodeBuffTopic(buff_topic);
Serial.print("buff_topic: ");
Serial.print(buff_topic);
Serial.print(" varnum 3: ");
Serial.println(buff_message);
delay(200);
//send var4_int, RSSI
varnum = 4;
sprintf(buff_topic, "%02d%01d%01d", SensorNode.nodeID, SensorNode.sensorID, varnum);
sprintf(buff_message, "%04d%", SensorNode.var4_int);
client.publish(buff_topic, buff_message);
decodeBuffTopic(buff_topic);
Serial.print("buff_topic: ");
Serial.print(buff_topic);
Serial.print(" varnum 4: ");
Serial.println(buff_message);
Serial.println("******************************");
sendMQTT = 0;
// Serial.println("finished MQTT send");
}//end if sendMQTT
}//end loop
void MQTTSendInt(PubSubClient* _client, int node, int sensor, int var, int val) {
char buff_topic[6];
char buff_message[7];
sprintf(buff_topic, "%02d%01d%01d", node, sensor, var);
sprintf(buff_message, "%04d%", val);
_client->publish(buff_topic, buff_message);
}
void MQTTSendULong(PubSubClient* _client, int node, int sensor, int var, unsigned long val) {
char buff_topic[6];
char buff_message[12];
sprintf(buff_topic, "%02d%01d%01d", node, sensor, var);
sprintf(buff_message, "%u", val);
_client->publish(buff_topic, buff_message);
}
void MQTTSendFloat(PubSubClient* _client, int node, int sensor, int var, float val) {
char buff_topic[6];
char buff_message[12];
sprintf(buff_topic, "%02d%01d%01d", node, sensor, var);
dtostrf (val, 2, 1, buff_message);
_client->publish(buff_topic, buff_message);
}
// Handing of Mosquitto messages
void callback(char* topic, byte* payload, unsigned int length) {
// handle message arrived
Serial.println(F("Mosquitto Callback"));
}
void decodeBuffTopic (char mybufftopic[6])
{
//print the OpenHAB Items value
int myInt;
myInt = atoi(mybufftopic);
if (myInt == 4032)
{
Serial.print("Garage Distance: ");
}
else if (myInt == 4122)
{
Serial.print("Mailbox count: ");
}
else if (myInt == 4124)
{
Serial.print("Mailbox RSSI: ");
}
else if (myInt == 4123)
{
Serial.print("Mailbox battery: ");
}
else if (myInt == 4022)
{
Serial.print("Garage light: ");
}
else if (myInt == 4042)
{
Serial.print("Garage temp: ");
}
else if (myInt == 4043)
{
Serial.print("Garage humidity: ");
}
else if (myInt == 3042)
{
Serial.print("Dryer: ");
}
else if (myInt == 3052)
{
Serial.print("Washer: ");
}
else if (myInt == 3032)
{
Serial.print("Water leak: ");
}
else if (myInt == 3062)
{
Serial.print("Laundry light: ");
}
else
Serial.print("unknown: ");
}
Comments