element14 Community
element14 Community
    Register Log In
  • Site
  • Search
  • Log In Register
  • Community Hub
    Community Hub
    • What's New on element14
    • Feedback and Support
    • Benefits of Membership
    • Personal Blogs
    • Members Area
    • Achievement Levels
  • Learn
    Learn
    • Ask an Expert
    • eBooks
    • element14 presents
    • Learning Center
    • Tech Spotlight
    • STEM Academy
    • Webinars, Training and Events
    • Learning Groups
  • Technologies
    Technologies
    • 3D Printing
    • FPGA
    • Industrial Automation
    • Internet of Things
    • Power & Energy
    • Sensors
    • Technology Groups
  • Challenges & Projects
    Challenges & Projects
    • Design Challenges
    • element14 presents Projects
    • Project14
    • Arduino Projects
    • Raspberry Pi Projects
    • Project Groups
  • Products
    Products
    • Arduino
    • Avnet & Tria Boards Community
    • Dev Tools
    • Manufacturers
    • Multicomp Pro
    • Product Groups
    • Raspberry Pi
    • RoadTests & Reviews
  • About Us
  • Store
    Store
    • Visit Your Store
    • Choose another store...
      • Europe
      •  Austria (German)
      •  Belgium (Dutch, French)
      •  Bulgaria (Bulgarian)
      •  Czech Republic (Czech)
      •  Denmark (Danish)
      •  Estonia (Estonian)
      •  Finland (Finnish)
      •  France (French)
      •  Germany (German)
      •  Hungary (Hungarian)
      •  Ireland
      •  Israel
      •  Italy (Italian)
      •  Latvia (Latvian)
      •  
      •  Lithuania (Lithuanian)
      •  Netherlands (Dutch)
      •  Norway (Norwegian)
      •  Poland (Polish)
      •  Portugal (Portuguese)
      •  Romania (Romanian)
      •  Russia (Russian)
      •  Slovakia (Slovak)
      •  Slovenia (Slovenian)
      •  Spain (Spanish)
      •  Sweden (Swedish)
      •  Switzerland(German, French)
      •  Turkey (Turkish)
      •  United Kingdom
      • Asia Pacific
      •  Australia
      •  China
      •  Hong Kong
      •  India
      • Japan
      •  Korea (Korean)
      •  Malaysia
      •  New Zealand
      •  Philippines
      •  Singapore
      •  Taiwan
      •  Thailand (Thai)
      • Vietnam
      • Americas
      •  Brazil (Portuguese)
      •  Canada
      •  Mexico (Spanish)
      •  United States
      Can't find the country/region you're looking for? Visit our export site or find a local distributor.
  • Translate
  • Profile
  • Settings
Pi IoT
  • Challenges & Projects
  • Design Challenges
  • Pi IoT
  • More
  • Cancel
Pi IoT
Blog PiIoT - DomPi 05: Ready for use Living Room and parents and kids´ bedrooms
  • Blog
  • Forum
  • Documents
  • Polls
  • Files
  • Events
  • Mentions
  • Sub-Groups
  • Tags
  • More
  • Cancel
  • New
  • Share
  • More
  • Cancel
Group Actions
  • Group RSS
  • More
  • Cancel
Engagement
  • Author Author: mg.sergio
  • Date Created: 30 Jun 2016 2:08 PM Date Created
  • Views 1425 views
  • Likes 5 likes
  • Comments 10 comments
  • piiot
  • piiot challenge
Related
Recommended

PiIoT - DomPi 05: Ready for use Living Room and parents and kids´ bedrooms

mg.sergio
mg.sergio
30 Jun 2016

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 image. 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.

image

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 image - 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.

image

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:

imageimage

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.

You don't have permission to edit metadata of this video.
Edit media
x
image
Upload Preview
image

In my case, it took me a bit longer, not a professional on soldering image Besides, that, nothing really special to raise. This is the processflow for the nodes:

image

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.

imageimageimage

 

Nodes´ Dashboard

And to finalize, the dashboard of the nodes.

image

Attachments:
Defs_y_Clases.zip
  • Sign in to reply

Top Comments

  • mg.sergio
    mg.sergio over 9 years ago in reply to DAB +1
    Hi DAB, thanks as always for your comments. I totally agree with you, even if you could, it does not make much sense to flood with RF messages that don't provide any value to the system. I did not mention…
  • mg.sergio
    mg.sergio over 9 years ago in reply to mcb1 +1
    Hi Mark, Nice call out, I will add a list to my previous posts in the next one, hope it will help to navigate through them. Also good that repeating the RF transmission helped you, I will definitely give…
Parents
  • Robert Peter Oakes
    Robert Peter Oakes over 9 years ago

    I am very impressed with the quality of your post, it is not often I see this level of detail and clear pictures etc. Well done

     

    I am curious why you use the Dallas Temp Sensor when you have the DHT Temperature and Humidity sensor already (Aside from the fact the Dallas is more accurate than the DHT), for normal room sensing the DHT would normally be good enough

     

    Also are you aware of the ESP8266 modules that would allow you to connect directly to your local WIFI network and that they can be had for just a few $$$ each. An alternative if you have issues with the RF24 modules and probably almost as cheap as the RF433 modules.

     

    Peter

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • mg.sergio
    mg.sergio over 9 years ago in reply to Robert Peter Oakes

    Many thanks Peter, I'm trying to share at least the key details so that people can really understand what I'm doing and even replicate parts of it at wish.

     

    I got the DHT11 and unfortunately this sensor does not provide decimals in the measurements - defaults them to 0. You are right, by knowing it is 23 or 24 degrees it should be more than enough. However, at home we have the typical room thermometer which goes up to 0.1ºC accuracy and it'd not be easy for me to "sell" at home that the system I'm creating and investing time has less accuracy or quality than the existing one... It is more a matter of "you don't want to give worse results" than the current thermometer...

     

    I was not aware of the ESP8266 modules, but will have a look at them. The RF24 ones are working ok so far - from now and then I have issues with the comms, but nothing that pushes me to abandon them. There is though an issue with the 2.4Ghz coverage in the garage and the node I plan to install there. I'm testing it and I lose the signal just 1 meter before the desired position (that 1 meter includes a wall...). Also my Wifi signal is too weak over there. I'm looking for a RF24 module with an external antenna. Hope to get it soon and test it.

     

    Many thanks again Peter for your comments, very rewarding.

    Sergio

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
Comment
  • mg.sergio
    mg.sergio over 9 years ago in reply to Robert Peter Oakes

    Many thanks Peter, I'm trying to share at least the key details so that people can really understand what I'm doing and even replicate parts of it at wish.

     

    I got the DHT11 and unfortunately this sensor does not provide decimals in the measurements - defaults them to 0. You are right, by knowing it is 23 or 24 degrees it should be more than enough. However, at home we have the typical room thermometer which goes up to 0.1ºC accuracy and it'd not be easy for me to "sell" at home that the system I'm creating and investing time has less accuracy or quality than the existing one... It is more a matter of "you don't want to give worse results" than the current thermometer...

     

    I was not aware of the ESP8266 modules, but will have a look at them. The RF24 ones are working ok so far - from now and then I have issues with the comms, but nothing that pushes me to abandon them. There is though an issue with the 2.4Ghz coverage in the garage and the node I plan to install there. I'm testing it and I lose the signal just 1 meter before the desired position (that 1 meter includes a wall...). Also my Wifi signal is too weak over there. I'm looking for a RF24 module with an external antenna. Hope to get it soon and test it.

     

    Many thanks again Peter for your comments, very rewarding.

    Sergio

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
Children
  • Robert Peter Oakes
    Robert Peter Oakes over 9 years ago in reply to mg.sergio

    So a couple of things comes to mind with your reply

     

    Accuracy and Resolution are not the same thing, just because a temperature sensor shows a display to 0.1deg C does not mean it is accurate, it does not always mean it has that resolution either.

    There are very few Temperature sensors in the cost bracket of Home systems that will be any better than 1 or 2%, there resolution may be better but that's all. If your aware of this then sorry to preach to the converted but if not you should look into it. or I can elaborate if needed.

     

    In the case of your system, The ESP8266 can completely replace the remote nodes as there quite powerful and able to perform most of the sensor activities you need for the nodes. I will be publishing videos in the next few days all about it

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • mg.sergio
    mg.sergio over 9 years ago in reply to Robert Peter Oakes

    Hi Peter,

     

    You are right, I misused resolution and accuracy. Re the resolution, the Dallas sensor is "user-configurable to 9, 10, 11, or 12 bits, corresponding to increments of 0.5°C, 0.25°C, 0.125°C, and 0.0625°C, respectively". For the accuracy it "is accurate to ±0.5°C over the range of -10°C to +85°C" - source the manufacturer's datasheet. So from a technical perspective, there would be no reason to go beyond the 9 bit resolution due to accuracy limitations. You can still get accurate measurements beyond the ±0.5°C, but they are not guaranteed... From a user perspective, it is nicer to see 24.3ºC or 23.7ºC instead of only 24ºC - being conscious of the limitations above though.

     

    For the ESP8266, it looks quite powerful, 32 bits, 80Mhz, 16 GPIO pins, Wifi integreated, there is now even Arduino SDK support and all at a nice price. Looking forward to those videos.

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
element14 Community

element14 is the first online community specifically for engineers. Connect with your peers and get expert answers to your questions.

  • Members
  • Learn
  • Technologies
  • Challenges & Projects
  • Products
  • Store
  • About Us
  • Feedback & Support
  • FAQs
  • Terms of Use
  • Privacy Policy
  • Legal and Copyright Notices
  • Sitemap
  • Cookies

An Avnet Company © 2025 Premier Farnell Limited. All Rights Reserved.

Premier Farnell Ltd, registered in England and Wales (no 00876412), registered office: Farnell House, Forge Lane, Leeds LS12 2NE.

ICP 备案号 10220084.

Follow element14

  • X
  • Facebook
  • linkedin
  • YouTube