This post covers the "final" versions of the code and hardware used for the nodes in the living room, parents and kids´ bedrooms. I say "final" as I expect to require some bugs, the nice thing of prototyping
. In the project status update, you can see that in this post I´m wrapping previous features and putting together the detailed workflow of the nodes.
The Living Room node - Process flow
Let´s start by the Living Room node as the other two will be a subset of this one. As a summary of posts 2 and 3, the hardware is an Arduino Nano, a movement sensor PIR, RF 433Mhz, IR receiver, temperature sensor DS18B20, humidity sensor DHT11, a photo-resistor and a RF2.4Ghz for the comms. Additionally I have added a button and a LED as you never know when you can need them
- in the posts you can find links to all of the components and some reasons for choosing them. For the software, the specific libraries I am using are the RF24, RF24Network, DallasTemperature, DHT, IRremote and one that I wrapped myself some code on the Internet into a library: DIO_lib. You may check the posts 2, 3 and 4 for some explanations on the libraries and external links.
An additionaly homemade library I'm using is the Defs_y_Clases.h, here I define some commands for the RF24 and I have also created a timer object to be able to control the time elapsed - this object takes care as well of overflows, approximately after 50 days the "long uint" variable will overflow, since the project will be used in the long run, there is a need to take care of this. The overflow control is though still under testing... Attached to the post.
As you can see in the diagram, the set-up and the loop functions are quite straight forward.
The living room node has a number of sensors that are polled synchronously in the loop function - it is the program which decides when to read the data. Additionally, it has the IR receiver that is read asynchronously, meaning that the IR library captures the IR data when it comes and then the loop function reads and process is synchronously. This approach, sync/async, is the same one used for the RF 2.4Ghz Comms. The library reads the data when it comes and then the loop function process is in sync.
Arduino code of the living room node:
#include <RF24Network.h>
#include <RF24.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <SPI.h>
#include <DHT.h>
#include <Defs_y_Clases.h>
#include <DIO_lib.h>
#include <IRremote.h>
/*
* Code for the Node at the living room
* It includes:
* IR receiver - reads IR data from the IR remote control
* Temperature sensor
* Humidity sensor
* Luminosity sensor
* RF card at 2.4GHz
*
*/
const bool debug = false; //If true, enables debug dump via Serial port
// Pin for the LED
int ledPin = 6;
// Pin where the IR receiver is connected to
int RECV_PIN = 9;
// Pin where the switch is connected
#define PIN_SWITCH_LIVING 5
//Pin for the PIR
#define PIN_PIR_LIVING 2
// Variables for the IR Receiver
IRrecv irrecv(RECV_PIN);
decode_results results;
#define BUTTON_SONY_RED 0x52E9
#define BUTTON_SONY_GREEN 0x32E9
#define BUTTON_SONY_YELLOW 0x72E9
#define BUTTON_SONY_BLUE 0x12E9
// Data wire is plugged into port 3 on the Arduino
#define ONE_WIRE_BUS 3
// The DHT data line is connected to pin 4 on the Arduino
#define DHTPIN 4
#define DHTTYPE DHT11 // Leave as is if you're using the DHT22. Change if not. // DHT 11 //#define DHTTYPE DHT22 // DHT 22 (AM2302) //#define DHTTYPE DHT21 // DHT 21 (AM2301)
DHT dht(DHTPIN, DHTTYPE);
// Setup a oneWire instance to communicate with any OneWire devices
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire); // Pass our oneWire reference to Dallas Temperature.
//NOTE: in line below I need to modify the address if I use a different temp sensor
DeviceAddress insideThermometer = {0x28, 0xFF, 0x1B, 0xB8, 0x54, 0x15, 0x03, 0xDD}; //Temp address of the sensor: 28FF1BB8541503DD // arrays to hold device address
// Photocell variable
byte photocellPin = A3;
// Radio with CE & CSN connected to pins 7 & 8
RF24 radio(7, 8);
RF24Network network(radio);
// Constants that identify this node and the node to send data to
const uint16_t this_node = 3;
const uint16_t parent_node = 0;
// Time between updates to the node (in ms)
unsigned long interval = 60000; // every x/1000 sec
// The below objects are type Temporizador (timer). They replace the need of using the delay()
// This allows the script to run and do other tasks while waiting for the time to pass
Temporizador tempo_tx(interval, true); //timer for the RF transmisions, every 60s it will force sending an update to the master node
Temporizador tempo_tx_maxdelay(900000, false); //Maximum delay Id allow without having sent a message to the master node, 15 mins
Temporizador tempo_watchdog(3600000, false); //Watchdog, if no update in 1h, activates watchdog
unsigned long sec2wait4tempo = 4000; //secons 2 wait for the timers, moved to variable to check if problems with constant alocation
Temporizador tempo_RED(sec2wait4tempo, false); //Timer for the red button - if the red button is pushed twice within the 4000ms, it does something (see later)
Temporizador tempo_GREEN(sec2wait4tempo, false); //Timer for the green button - if the red button is pushed twice within the 4000ms, it does something (see later)
Temporizador tempo_YELLOW(sec2wait4tempo, false); //Timer for the yellow button - if the red button is pushed twice within the 4000ms, it does something (see later)
// Variables for the sensors
float h, t; //humidity and temperature
int l; //luminosity
bool is_motionPIR = false; //Stores current status of the PIR motion sensor
bool is_motionPIR_prev=false; //Stores prev status of the PIR motion sensor,
//if different than current, then forces to send a message:new movement detected
//Variables for the DIO - the RF-controlled plugs
const int pinDIO = 10; // Pin where the RF 433Mhz transmitter is located
DIO_lib DIO1(ENCHUFE1, pinDIO); //note-translation, enchufe = plug
DIO_lib DIO2(ENCHUFE2, pinDIO);
DIO_lib DIO3(ENCHUFE3, pinDIO);
bool light1_status = true;
bool light2_status = true;
bool light3_status = true;
bool issecondtime_RED = false; //States if the button is already pushed for the 1st time
bool issecondtime_GREEN = false;
bool issecondtime_YELLOW = false;
int status_switch = 0; //States if the switch on the board was pushed
bool force_send_msg = false; //Forces the transmission of the RF 2.4Ghz message to the node
struct message_1 { // Structure of our message to send
int16_t temperature; //Temperature is sent as int16: I multiply by 100 the float value of the sensor and send it
//this way I avoid transmitting floats over RF
unsigned char humidity;
unsigned char light;
unsigned char motion;
unsigned char dooropen;
};
message_1 message_tx;
struct message_action { // Structure of our message to receive
unsigned char cmd;
unsigned char info;
};
message_action message_rx;
RF24NetworkHeader header(parent_node); // The network header initialized for this node
void setup(void)
{
if (debug) Serial.begin(9600);
// Initialize all radio related modules
SPI.begin();
radio.begin();
delay(50);
radio.setPALevel(RF24_PA_MAX); //This can lead to issues as per https://arduino-info.wikispaces.com/Nrf24L01-2.4GHz-HowTo
//use this radio.setPALevel(RF24_PA_LOW); if there are issues
delay(50);
radio.setChannel(108); //Set channel over the WIFI channels
delay(50);
radio.setDataRate(RF24_250KBPS); //Decrease speed and improve range. Other values: RF24_1MBPS y RF24_2MBPS
delay(50);
network.begin(90, this_node);
// Initialize the DHT library
dht.begin();
//Get oneWire devices on bus
sensors.begin();
sensors.setResolution(insideThermometer, 12);
// Prepare IR
irrecv.enableIRIn(); // Start the receiver
// Configure LED pin
pinMode(ledPin, OUTPUT);
//Config pin for the switch
pinMode(PIN_SWITCH_LIVING, INPUT_PULLUP);
//Config PIR
pinMode(PIN_PIR_LIVING, INPUT);
digitalWrite(ledPin, LOW);
if (debug) {
Serial.print("Starting Node Living room with IR. Node number: ");
Serial.println(this_node);
Serial.println("Remember to change termometer address if copy/pasting the script");
}
status_switch = digitalRead(PIN_SWITCH_LIVING); //read the switch status
}
void loop() {
// Update network data
network.update();
//Receive RF Data
while (network.available()) {
receive_data();
}
//Receive IR
IR_read_exec();
if (tempo_tx_maxdelay.is_Time()) {
//If we enter here, means that tmax has already elapsed and we are to force data transmission
force_send_msg = true;
}
if (switch_just_pushed()) {
//If the switch is just pushed, we force sending the RF data
force_send_msg = true;
}
if (change_inPIRsensor()){
//If there is change in PIR, forces the update to the node
force_send_msg = true;
}
//Get and send the sensor data. This is done only if the timer is over or we force RF data
if (tempo_tx.is_Time() || force_send_msg) {
tempo_tx_maxdelay.reiniciar_t0(); //Reinitialize the timer
read_sensors_data();
send_sensors_data();
force_send_msg = false;
}
check_watchdog();
}
void receive_data(){
RF24NetworkHeader header;
message_action message_rx;
network.peek(header);
if (header.type == '2') {
tempo_watchdog.reiniciar_t0(); //message received, it means there is communication
// with the master, reinitiates the watchdog timer
network.read(header, &message_rx, sizeof(message_rx));
if (debug) {
Serial.print("Data received from node ");
Serial.println(header.from_node);
}
unsigned char cmd = message_rx.cmd;
unsigned char info = message_rx.info;
if (debug) {
Serial.print("Command: "); Serial.println(cmd);
}
switch (cmd) {
case LUZ_ON: //Note translation: LUZ = light
turn_on_light(info);
modify_light_status(info, LUZ_ON);
send_cmd_per_RF(ACK, cmd);
break;
case LUZ_OFF:
turn_off_light(info);
modify_light_status(info, LUZ_OFF);
send_cmd_per_RF(ACK, cmd);
break;
case MODIFICAR_T_SLEEP: //Note translation: modificar = modify
interval = message_rx.info * 1000;
send_cmd_per_RF(ACK, cmd);
break;
case FORCE_SEND_MSG:
force_send_msg = true;
send_cmd_per_RF(ACK, cmd);
break;
case TODAS_LUCES_ON: //Note translation: Todas luces = all of the lights
int i;
for (i=1;i<=3; i++) {
turn_on_light(i);
modify_light_status(info, LUZ_ON);
}
send_cmd_per_RF(ACK, cmd);
break;
case TODAS_LUCES_OFF:
int j;
for (j=1;j<=3; j++) {
turn_off_light(j);
modify_light_status(info, LUZ_OFF);
}
send_cmd_per_RF(ACK, cmd);
break;
default:
//Do nothing
break;
}
} else {
// This is not a type we recognize
network.read(header, &message_rx, sizeof(message_rx));
if (debug) {
Serial.print("Unknown message received from node ");
Serial.println(header.from_node);
}
}
}
void read_sensors_data() {
sensors.requestTemperatures();
h = dht.readHumidity();
t = sensors.getTempC(insideThermometer);
//Sensor for PIR is read already when loop calls function change_inPIRsensor
//no need to re-read it here
// Read photocell and constrains data within a range and then maps it to range 0% - 100%
l = analogRead(photocellPin);
l = constrain(l, 70, 850);
l = map(l, 70, 850, 100, 0);
}
void send_sensors_data(){
header.type = '1'; // Headers will always be type 1 for this node
// Only send values if any of them are different enough from the last time we sent:
// 0.5 degree temp difference, 1% humdity or light difference, or different motion state
float t1 = t*100; //multiplies by 100 to get read of float and transmit only int16
int16_t t_tx = (int16_t) t1;
if (abs(t_tx - message_tx.temperature) > 30 || //sends data if there are significant changes in the sensors measurements
abs(h - message_tx.humidity) > 1.0 ||
abs(l - message_tx.light) > 5.0 ||
force_send_msg) {
// Construct the message we'll send
message_tx = (message_1){ t_tx, (unsigned char) h, (unsigned char)l, is_motionPIR, 0 }; //codigo original: {f, h, p, m, d}
// Writing the message to the network means sending it
if (network.write(header, &message_tx, sizeof(message_tx))) {
tempo_watchdog.reiniciar_t0(); //reinitiates the watchdog timer
if (debug) Serial.print("Message sent\n");
} else {
if (debug) Serial.print("Could not send message\n");
}
if (debug) {
Serial.print("Temp_tx: "); Serial.println((message_tx.temperature));
Serial.print("Temp_detected: "); Serial.println((t));
Serial.print("Hum_tx: "); Serial.println(message_tx.humidity);
Serial.print("Hum_detected: "); Serial.println((h));
Serial.print("Luminosity: "); Serial.println(message_tx.light);
Serial.print("Motion PIR: "); Serial.println(message_tx.motion);
}
}
}
void turn_on_light(int luz){
byte i;
for (i=0;i<3;i++) {
//repeats 3 times sending the RF signal
noInterrupts(); //need to stop interrupts, otherwise the signal is not understood by the RF plugs
switch (luz) {
case LUZ_1:
DIO1.On();
break;
case LUZ_2:
DIO2.On();
break;
case LUZ_3:
DIO3.On();
break;
}
interrupts();
delay(15+i);
}
if (debug) {
Serial.print("Light turned on: "); Serial.println(luz);
}
}
void turn_off_light (int luz){
noInterrupts();
switch (luz) {
case LUZ_1:
DIO1.Off();
break;
case LUZ_2:
DIO2.Off();
break;
case LUZ_3:
DIO3.Off();
break;
}
interrupts();
if (debug) {
Serial.print("Light turned off: "); Serial.println(luz);
}
}
void modify_light_status(int luz, int valor) {
switch (luz) {
case LUZ_1:
if (valor==LUZ_ON) light1_status=true;
else light1_status=false;
break;
case LUZ_2:
if (valor==LUZ_ON) light2_status=true;
else light2_status=false;
break;
case LUZ_3:
if (valor==LUZ_ON) light3_status=true;
else light3_status=false;
break;
}
}
void IR_read_exec() {
unsigned char cmd=NO_ACTION;
unsigned char info=NO_ACTION;
if (irrecv.decode(&results)) {
if (debug) Serial.println(results.value, HEX);
if (results.value==BUTTON_SONY_RED) {
if (debug) Serial.println("Detected RED. ");
if (issecondtime_RED) {
if (debug) Serial.println("issecondtime_red = true");
//If red button is pressed nad it was pressed before within the timing of the timer (4000ms)
//then we execute the action: either on or off
if (!tempo_RED.is_Time()) {
if (debug) Serial.println("Red is within Time");
issecondtime_RED = false;
if (light3_status) {
light3_status = false;
turn_off_light(LUZ_3);
cmd = LUZ_OFF;
info = LUZ_3;
} else {
light3_status = true;
turn_on_light(LUZ_3);
cmd = LUZ_ON;
info = LUZ_3;
}
} else {
//If red is received but after the time, we consider it as the first time it is pressed
//and reinitalize the red timer
tempo_RED.reiniciar_t0();
if (debug) Serial.println("Red is out of Time");
}
} else {
//If red is received for the 1st time, we reinitiate the timer and mark it as pressed
issecondtime_RED = true;
tempo_RED.reiniciar_t0();
}
} else if (results.value==BUTTON_SONY_GREEN) {
if (issecondtime_GREEN) {
//Same as with red button
if (!tempo_GREEN.is_Time()) {
issecondtime_GREEN = false;
if (light2_status) {
light2_status = false;
turn_off_light(LUZ_2);
cmd = LUZ_OFF;
info = LUZ_2;
} else {
light2_status = true;
turn_on_light(LUZ_2);
cmd = LUZ_ON;
info = LUZ_2;
}
} else {
//Same as with red button
tempo_GREEN.reiniciar_t0();
}
} else {
//Same as with red button
issecondtime_GREEN = true;
tempo_GREEN.reiniciar_t0();
}
} else if (results.value==BUTTON_SONY_YELLOW) {
if (issecondtime_YELLOW) {
//Same as with red button
if (!tempo_YELLOW.is_Time()) {
issecondtime_YELLOW = false;
if (light1_status) {
light1_status = false;
turn_off_light(LUZ_1);
cmd = LUZ_OFF;
info = LUZ_1;
} else {
light1_status = true;
turn_on_light(LUZ_1);
cmd = LUZ_ON;
info = LUZ_1;
}
} else {
//Same as with red button
tempo_YELLOW.reiniciar_t0();
}
} else {
//Same as with red button
issecondtime_YELLOW = true;
tempo_YELLOW.reiniciar_t0();
}
}
if (cmd != NO_ACTION) {
send_cmd_per_RF(cmd, info);
}
irrecv.resume(); // Receive the next value
}
}
void send_cmd_per_RF(unsigned char cmd, unsigned char info) {
//Sends via RF the command and info
//This is the way to update the master of the light status, ACK, etc
header.type = '2';
message_action message_x_RF;
message_x_RF = (message_action){ cmd, info };
// Writing the message to the network means sending it
if (network.write(header, &message_x_RF, sizeof(message_x_RF))) {
if (debug) Serial.print("Message sent RF\n");
//There is communication with the master -> reinitiate the watchdog timer
tempo_watchdog.reiniciar_t0(); //reinitiates the watchdog timer
} else {
if (debug) Serial.print("Could not send message RF\n");
}
}
bool switch_just_pushed() {
int v = digitalRead(PIN_SWITCH_LIVING);
bool r=false;
if (v==LOW) {
status_switch=LOW;
digitalWrite(ledPin, LOW);
}
else {
if (status_switch==LOW) {
//if low, then it is the first time we pushed the button
//this means we have detected a raising signal
status_switch=HIGH;
r=true;
digitalWrite(ledPin, HIGH);
}
}
return r;
}
bool change_inPIRsensor() {
//True if there is a change in the PIR sensor
bool b;
is_motionPIR = (digitalRead(PIN_PIR_LIVING)==HIGH); //updates current state of the PIR
b = (is_motionPIR!=is_motionPIR_prev); //if current != previous state -> change
is_motionPIR_prev = is_motionPIR;
return b;
}
void check_watchdog() {
//Controls the watchdog. Checks that there has been some communication with the
//central node in the last 1h, either receiving a message from the center
//or sending a message successfully to the center
if (tempo_watchdog.is_Time()) {
//At this point in time, this does nothing special, just print error via Serial
//Improvement: to deactivate the automation and force lights to off, etc
if (debug) Serial.println("Alarm!!! Watchdog timer. No successfull communication with the center");
tempo_tx_maxdelay.reiniciar_t0();
}
}
One issue to call out, call it mistake or just the typical bug you don´t know how to solve and spend lot of time trying to fix, is that turning on and off the lights with the TV remote was sometimes working and others not. I reviewed the code several times, thought there would be interference or just a week signal, maybe there was a problem with the double click of the TV buttons, etc... Until I found the solution, I had to add the lines 323 and 335 and also 344 and 354. Basically I had to disable any interrupts when sending the RF 433Mhz instruction to the RF plugs. My speculation: there are lots of IR interference and probably also on the 2.4Ghz, meaning that I "guess" that the Arduino was called via interrupts by the IR and RF24 libraries. Even if these could take only microseconds up to 1 millisecond, this pause in the RF433Mhz message is enough to damage the message and the plug would not recognize it correctly.
Another issue I am facing is that from time to time the Arduino is not processing the IR command to turn on/off the light. My feeling this time is that I need to send the RF433 message a couple of times - basically add a for loop from line 345 to 355. More to come when I test it!
A couple of pics of the node:
I hope that the rest of the code together with the info shared in previous posts is self explanatory, if not, just ask!
The Kids and parents rooms' nodes - Process flow
There are three main differences between these nodes and the living room one. The first one is that they don't require the IR receiver nor the RF433Mhz components, no need to implement reading the TV remote or sending commands to the RF plugs as this is only done by the living room node. The second difference is that they are based on Arduino Pro Mini instead of Nano, not a big difference in pings and performance, the only call out is that the Pro Mini you need to solder them. The third difference is how you power up the Arduino, while the Nano has a built in USB adapter, the Pro Mini comes without it. I still have to figure out the best way to power it up, probably just by cutting any old USB cable and connecting it to the RAW pin (which has a built in regulator and you can apply higher voltage than 3.3V and it will adapt it to the Arduino). So far I am using the FT232RL USB to Serial, which by the way is required for programming it from the computer.
The soldering itself is not that difficult but I looked in the Internet for some "best practices" and found a couple of good ideas. Here is a short video as an example.
In my case, it took me a bit longer, not a professional on soldering
Besides, that, nothing really special to raise. This is the processflow for the nodes:
Here is the code I am using for the kids' room that will be just the same as for the parent's but changing the line 42 to reflect the bedroom's ID - you can find the rooms' ids under post 4 PiIoT - DomPi 04: Movement detection and RF2.4Ghz comms
#include <RF24Network.h>
#include <RF24.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <SPI.h>
#include <DHT.h>
#include <Defs_y_Clases.h>
const bool debug = true; //If true, enables debug dump via Serial port
// Pin for the LED
int ledPin = 13;
// Pin for the switch
#define PIN_SWITCH_KIDS 5
// Pin for the PIR
#define PIN_PIR_KIDS 2
// Data wire is plugged into port 3 on the Arduino
#define ONE_WIRE_BUS 3
// The DHT data line is connected to pin 4 on the Arduino
#define DHTPIN 4
#define DHTTYPE DHT11 // Leave as is if you're using the DHT22. Change if not. // DHT 11 //#define DHTTYPE DHT22 // DHT 22 (AM2302) //#define DHTTYPE DHT21 // DHT 21 (AM2301)
DHT dht(DHTPIN, DHTTYPE);
// Setup a oneWire instance to communicate with any OneWire devices
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire); // Pass our oneWire reference to Dallas Temperature.
//NOTE: in line below I need to modify the address if I use a different temp sensor
DeviceAddress insideThermometer = {0x28, 0xFF, 0x5B, 0x0D, 0x01, 0x16, 0x04, 0xBC};
//Temp address of the sensor: 28FF5B0D011604BC // arrays to hold device address
// Photocell variable
byte photocellPin = A3;
// Radio with CE & CSN connected to pins 9 & 10
RF24 radio(9, 10);
RF24Network network(radio);
// Constants that identify this node and the node to send data to
const uint16_t this_node = 1;
const uint16_t parent_node = 0;
// Time between packets (in ms)
unsigned long interval = 60000; // every x/1000 sec
// The below objects are type Temporizador (timer). They replace the need of using the delay()
// This allows the script to run and do other tasks while waiting for the time to pass
Temporizador tempo_tx(interval, true); //timer for the RF transmisions, every 60s it will force sending an update to the master node
Temporizador tempo_tx_maxdelay(900000, false); //Maximum delay Id allow without having sent a message to the master node, 15 mins
Temporizador tempo_watchdog(3600000, false); //Watchdog, if no update in 1h, activates watchdog
// Variables for the sensors
float h, t; //humedad y temperatura
int l; //luminosidad
bool is_motionPIR = false; //Stores current status of the PIR motion sensor
bool is_motionPIR_prev=false; //Stores prev status of the PIR motion sensor,
//if different than current, then forces to send a message:new movement detected
int status_switch = 0; //States if the switch on the board was pushed
bool force_send_msg = false; //Fuerzo a enviar el msg independientemente del estado
struct message_1 { // Structure of our message to send
int16_t temperature; //Temperature is sent as int16: I multiply by 100 the float value of the sensor and send it
//this way I avoid transmitting floats over RF
unsigned char humidity;
unsigned char light;
unsigned char motion;
unsigned char dooropen;
};
message_1 message_tx;
struct message_action { // Structure of our message to receive
unsigned char cmd;
unsigned char info;
};
message_action message_rx;
RF24NetworkHeader header(parent_node); // The network header initialized for this node
void setup(void)
{
if (debug) Serial.begin(9600);
// Initialize all radio related modules
SPI.begin();
radio.begin();
delay(50);
radio.setPALevel(RF24_PA_MAX); //This can lead to issues as per https://arduino-info.wikispaces.com/Nrf24L01-2.4GHz-HowTo
//use this radio.setPALevel(RF24_PA_LOW); if there are issues
delay(50);
radio.setChannel(108); //Set channel over the WIFI channels
delay(50);
radio.setDataRate(RF24_250KBPS); //Decrease speed and improve range. Other values: RF24_1MBPS y RF24_2MBPS
delay(50);
network.begin(90, this_node);
// Initialize the DHT library
dht.begin();
//Get oneWire devices on bus
sensors.begin();
sensors.setResolution(insideThermometer, 12);
// Configure LED pin
pinMode(ledPin, OUTPUT);
//Config pin for the switch
pinMode(PIN_SWITCH_KIDS, INPUT_PULLUP);
//Config PIR
pinMode(PIN_PIR_KIDS, INPUT);
digitalWrite(ledPin, LOW);
if (debug) {
Serial.print("Starting Node Kids room. Node number: ");
Serial.println(this_node);
Serial.println("Remember to change termometer address if copy/pasting the script");
}
status_switch = digitalRead(PIN_SWITCH_KIDS); //read the switch status
}
void loop() {
// Update network data
network.update();
//Receive RF Data
while (network.available()) {
receive_data();
}
if (tempo_tx_maxdelay.is_Time()) {
//If we enter here, means that tmax has already elapsed and we are to force data transmission
force_send_msg = true;
}
if (switch_just_pushed()) {
//If the switch is just pushed, we force sending the RF data
force_send_msg = true;
}
if (change_inPIRsensor()){
//If there is change in PIR, forces the update to the node
force_send_msg = true;
}
//Get and send the sensor data. This is done only if the timer is over or we force RF data
if (tempo_tx.is_Time() || force_send_msg) {
tempo_tx_maxdelay.reiniciar_t0(); //Reinitialize the timer
read_sensors_data();
send_sensors_data();
force_send_msg = false;
}
check_watchdog();
}
void receive_data(){
RF24NetworkHeader header;
message_action message_rx;
network.peek(header);
if (header.type == '2') {
tempo_watchdog.reiniciar_t0(); //message received, it means there is communication
// with the master, reinitiates the watchdog timer
network.read(header, &message_rx, sizeof(message_rx));
if (debug) {
Serial.print("Data received from node ");
Serial.println(header.from_node);
}
unsigned char cmd = message_rx.cmd;
unsigned char info = message_rx.info;
if (debug) {
Serial.print("Command: "); Serial.println(cmd);
}
switch (cmd) {
case MODIFICAR_T_SLEEP:
interval = message_rx.info * 1000;
send_cmd_per_RF(ACK, cmd);
break;
case FORCE_SEND_MSG:
force_send_msg = true;
send_cmd_per_RF(ACK, cmd);
break;
default:
//Do nothing
break;
}
} else {
// This is not a type we recognize
network.read(header, &message_rx, sizeof(message_rx));
if (debug) {
Serial.print("Unknown message received from node ");
Serial.println(header.from_node);
}
}
}
void read_sensors_data() {
sensors.requestTemperatures();
h = dht.readHumidity();
t = sensors.getTempC(insideThermometer);
//Sensor for PIR is read already when loop calls function change_inPIRsensor
//no need to re-read it here
// Read photocell and constrains data within a range and then maps it to range 0% - 100%
l = analogRead(photocellPin);
l = constrain(l, 70, 850);
l = map(l, 70, 850, 0, 100);
}
void send_sensors_data(){
header.type = '1'; // Headers will always be type 1 for this node
// Only send values if any of them are different enough from the last time we sent:
// 0.5 degree temp difference, 1% humdity or light difference, or different motion state
float t1 = t*100; //multiplies by 100 to get read of float and transmit only int16
int16_t t_tx = (int16_t) t1;
if (abs(t_tx - message_tx.temperature) > 30 || //sends data if there are significant changes in the sensors measurements
abs(h - message_tx.humidity) > 1.0 ||
abs(l - message_tx.light) > 5.0 ||
force_send_msg) {
// Construct the message we'll send
message_tx = (message_1){ t_tx, (unsigned char) h, (unsigned char)l, is_motionPIR, 0 }; //codigo original: {f, h, p, m, d}
// Writing the message to the network means sending it
if (network.write(header, &message_tx, sizeof(message_tx))) {
tempo_watchdog.reiniciar_t0(); //reinitiates the watchdog timer
if (debug) Serial.print("Message sent\n");
} else {
if (debug) Serial.print("Could not send message\n");
}
if (debug) {
Serial.print("Temp_tx: "); Serial.println((message_tx.temperature));
Serial.print("Temp_detected: "); Serial.println((t));
Serial.print("Hum_tx: "); Serial.println(message_tx.humidity);
Serial.print("Hum_detected: "); Serial.println((h));
Serial.print("Luminosity: "); Serial.println(message_tx.light);
Serial.print("Motion PIR: "); Serial.println(message_tx.motion);
}
}
}
void send_cmd_per_RF(unsigned char cmd, unsigned char info) {
//Sends via RF the command and info
//This is the way to update the master of the light status, ACK, etc
header.type = '2';
message_action message_x_RF;
message_x_RF = (message_action){ cmd, info };
// Writing the message to the network means sending it
if (network.write(header, &message_x_RF, sizeof(message_x_RF))) {
if (debug) Serial.print("Message sent RF\n");
//There is communication with the master -> reinitiate the watchdog timer
tempo_watchdog.reiniciar_t0(); //reinitiates the watchdog timer
} else {
if (debug) Serial.print("Could not send message RF\n");
}
}
bool switch_just_pushed() {
int v = digitalRead(PIN_SWITCH_KIDS);
bool r=false;
if (v==LOW) {
status_switch=LOW;
digitalWrite(ledPin, LOW);
}
else {
if (status_switch==LOW) {
//if low, then it is the first time we pushed the button
//this means we have detected a raising signal
status_switch=HIGH;
r=true;
digitalWrite(ledPin, HIGH);
}
}
return r;
}
bool change_inPIRsensor() {
//True if there is a change in the PIR sensor
bool b;
is_motionPIR = (digitalRead(PIN_PIR_KIDS)==HIGH); //updates current state of the PIR
b = (is_motionPIR!=is_motionPIR_prev); //if current != previous state -> change
is_motionPIR_prev = is_motionPIR;
return b;
}
void check_watchdog() {
//Controls the watchdog. Checks that there has been some communication with the
//central node in the last 1h, either receiving a message from the center
//or sending a message successfully to the center
if (tempo_watchdog.is_Time()) {
//At this point in time, this does nothing special, just print error via Serial
//Improvement: to deactivate the automation and force lights to off, etc
if (debug) Serial.println("Alarm!!! Watchdog timer. No successfull communication with the center");
tempo_tx_maxdelay.reiniciar_t0();
}
}
A couple of pic of the kids room.
Nodes´ Dashboard
And to finalize, the dashboard of the nodes.









Top Comments
-
Robert Peter Oakes
-
Cancel
-
Vote Up
0
Vote Down
-
-
Sign in to reply
-
More
-
Cancel
-
mg.sergio
in reply to Robert Peter Oakes
-
Cancel
-
Vote Up
0
Vote Down
-
-
Sign in to reply
-
More
-
Cancel
Comment-
mg.sergio
in reply to Robert Peter Oakes
-
Cancel
-
Vote Up
0
Vote Down
-
-
Sign in to reply
-
More
-
Cancel
Children