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 11: Ready to use. Garage node (master & slave)
  • 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: 11 Aug 2016 3:43 PM Date Created
  • Views 1550 views
  • Likes 2 likes
  • Comments 7 comments
  • piiot
  • piiot challenge
Related
Recommended

PiIoT - DomPi 11: Ready to use. Garage node (master & slave)

mg.sergio
mg.sergio
11 Aug 2016

Hi there! Following the previous post and the issues I found with the TFT and the RF24, I´m happy to share that I managed to solve it. Rather than solving it, I have had to apply the workaround briefly introduced earlier this week: add a master node to control the RF while delegating the rest of the functions to the slave one. Let´s go into the details after the project status.

 

Previous Posts

PiIoT - DomPi: ApplicationPiIoT - DomPi: IntroPiIoT - DomPi 02: Project Dashboard and first steps in the Living room
PiIoT - DomPi 03: Living room, light control via TV remotePiIoT - DomPi 04: Movement detection and RF2.4Ghz commsPiIoT - DomPi 05: Ready for use Living Room and parents and kids´ bedrooms
PiIoT - DomPi 06: Setting up the Command Center - RPI3PiIoT - DomPi 07: Setting up the Command Center (2)PiIoT - DomPi 08: Setting up the Command Center (3) openHAB, mosquitto, RF24
PiIoT - DomPi 09: Presence EmulatorPiIoT - DomPi 10: Garage node (slave)

 

Project Status

image

 

Architecture

Due to some incompatibilities of my TFT LCD with the SPI protocol - very probably it is not releasing the bus when the device is not addressed by the chip -, the RF24 board was not working properly. Both devices, the screen and the RF, are key components of the Garage node and hence, I could not sacrifice any. I have not found any potential solution rather than the work around to separate the TFT and the RF24, connecting them to two different Arduinos and interconnecting the Arduinos with I2C:

  • Arduino Mega, Slave node: temperature, humidity, luminosity, movement, distance measurement, TFT display and I2C slave node
  • Arduino Nano, Master node: 2.4Ghz radio frequency communications and I2C master node

 

Slave node - sensor blocksGarage node: master + slave + TFT + RF24
imageimage

 

I2C Implementation

To enable the I2C comms between both Arduinos, the first step is to make sure that the Nano has the pins A4 and A5 soldered on it. You can see some notes on soldering the headers in PiIoT - DomPi 05: Ready for use Living Room and parents and kids´ bedrooms and the valid comments from ravi_butani about avoiding soldering on the breadboard. As a quick note, the A4 and A5 on the Nano are on the upper part, separated from the rest of the pins.

 

I2C implementation in the master

To add an I2C communication is fairly easy. From the HW perspective the only requirement is to connect the Nano pins A4 (SDA) and A5 (SCL) to the Mega pins 20 (SDA), 21 (SCL) respectively. Additionally and as a must in these architectures, you need to connect the GND of both Arduinos together, so that both share a common ground. You should not connect the 5V source together if you have two power supplies, one for each board. The reason is that they very probably will provide different voltage (f.e. 5.05V and 5.19V) and you would be short-circuiting them, which is bad and can destroy the power supplies...

 

From the SW perspective, I am using the Wire library from Arduino. This library includes nice examples (the Master writer and the Master reader) that have helped me to understand the protocol and leverage it in my Arduino sketches. Re the bit-rate, the I2C speed is not that high and can be increased by 4 by tweaking the Wire.h library. For the DomPi, the original speed is more than sufficient as both Arduinos will be just sharing few bytes per second the most. In any case, more information on increasing the bit rate in the I2C can be found here.

 

The protocol I have created is fairly simple. Via the "write_I2C_cmd" function below, the Master can ask the slave to send:

  • all of the data: temperature, humidity, luminosity, motion and the distances of the three ultrasonic sensors. So far I am just using 1 sensor that I will install on the rear wall of the garage. However,  implementing 3 will allow me grow easily - as a future development, I will install two more ultrasonic sensors to measure the distance with the walls on the left and on the right.
  • confirm if the car is present. This request in return makes the slave to send all of the data as above, but for the distances, it calls another function (finetune) that calculates the distance more precisely by spending more time. This should not be required, but thought of really avoiding false positives.

After the master it has sent one of the above commands with the "write_I2C_cmd" function, it awaits the message from the slave with the function "requestI2C_data", see code below.

 

You can find later the whole code, but let me add here the I2C related code:

// Initialize I2C - in the loop function
Wire.begin();
...

void write_I2C_cmd(uint8_t cmd_slave, uint8_t info_slave) {
    Wire.beginTransmission(id_I2Cslave);    // transmit to device #8
    Wire.write(cmd_slave);                  // sends cmd byte
    Wire.write(info_slave);                 // sends info byte
    Wire.endTransmission();                 // stop transmitting
}

void requestI2C_data() {
  if (debug) Serial.println("Requesting I2C data...");
  Wire.requestFrom(8, 11);    //Request from I2C node 8 the data: 11 bytes
  if (debug) Serial.println("I2C after requestFrom...");
  uint8_t rxBytes=0;          //We count the number of bytes received, to ensure data is ok
  uint8_t buffer_rx[11];

  while (Wire.available()) {  
    if (rxBytes<11) buffer_rx[rxBytes] = Wire.read();
    rxBytes++;
  }
  if (rxBytes!=11) {
    if (debug) {
      Serial.print("Error in I2C data transmission. Received bytes: ");
      Serial.println(rxBytes);
    }
  } else {
    t = 0;                    //reset global variable for the temperature
    t= (buffer_rx[0]<<8);     //First 2 bytes are for the temperature. First byte received is the most significant one, moving it to the highest 8 bits
    t += buffer_rx[1];       //Second byte: low byte of the temperature. No need to divde by 100, we wil just resend it via RF
    h = buffer_rx[2];
    l = buffer_rx[3];
    motionPIR = buffer_rx[4];
    dist_sensor1 = 0;         //reset global variable for the distance sensor 1 - same as with temperature
    dist_sensor1 = (buffer_rx[5]<<8);
    dist_sensor1 += buffer_rx[6];
    dist_sensor2=0;
    dist_sensor3=0;
    if (debug) {
      send_data_serial();
      uint8_t i;
      for (i=0;i<11;i++) {
        Serial.print(buffer_rx[i]);
        Serial.print("-");
      }
      Serial.println("");
    }
  }
}

 

I2C implementation in the slave

From the HW perspective, it is straightforward as well, and I used the pins 20 and 21 in the Mega as per above. From the SW perspective, the "protocol" I created waits for the Master to confirm which data it is expecting (if just the normal data or the data with the more accurate distance). This is done by the line and function Wire.onReceive(receive_I2C_data). Then the slave waits to be asked by the master when to send the data by the line and function Wire.onRequest(send_I2C_sensors_data)

 

  //Starting I2C communication with the Arduino master - in the loop function
  Wire.begin(id_I2Cslave);                          // join i2c bus with address #8
  Wire.onRequest(send_I2C_sensors_data);  // register I2C event, allows Master to request information - 11 bytes
  Wire.onReceive(receive_I2C_data);       // 2 bytes
...

void send_I2C_sensors_data() {
  // If we enter here, the I2C master has requested data to be sent. 
  // Sends data to the Master. Master expects 11 bytes
  uint8_t buffer_tx[11]; //The wire.write function sends data per bytes
      //The function will send an array of data as follows:
      //    2xbytes for the int16_t of the temperature
      //    1xbyte for the humidity
      //    1xbyte for the light
      //    1xbyte for the motion
      //    2xbytes for the uint16_t of sensor distance 1
      //    2xbytes for the uint16_t of sensor distance 2
      //    2xbytes for the uint16_t of sensor distance 3
   
  tempo_watchdog.reiniciar_t0();    //Reinitialize the watchdog timer, since we have received an I2C message

  float t1 = t*100;             //multiplies by 100 to get rid of float and transmit only uint16
  int16_t t_tx = (int16_t) t1; 
  uint16_t dist_cm_1_tx = 0;    //Initialize the variable, ensuring all bits are set to 0
  
  if (get_iscar) {
    //if get_iscar -> the master requested previously to send get_iscar data
    //the distance to tx will be the _iscar one
    get_iscar = false;
    dist_cm_1_tx = dist_cm_iscar;
  } else dist_cm_1_tx = dist_cm_1;
  
  buffer_tx[0] = t_tx >> 8;             //first byte of temperature uint16_t
  buffer_tx[1] = t_tx & 0xFF;           //second byte
  buffer_tx[2] = h;                     //humidity
  buffer_tx[3] = l;                     //luminosity
  buffer_tx[4] = is_motionPIR;          //motion
  buffer_tx[5] = dist_cm_1_tx >>8;      //first byte of distance sensor 1
  buffer_tx[6] = dist_cm_1_tx & 0xFF;   //second byte of distance sensor 1
  buffer_tx[7] = 0;                     //first byte of distance sensor 2
  buffer_tx[8] = 0;                     //second byte of distance sensor 2
  buffer_tx[9] = 0;                     //first byte of distance sensor 3
  buffer_tx[10] = 0;                    //second byte of distance sensor 3
  
  Wire.write(buffer_tx, 11); // respond with message with sensors data
  if (debug) {
    send_data_serial();
    uint8_t i;
    for (i=0;i<11;i++) {
      Serial.print(buffer_tx[i]);
    }
    Serial.println("");
  }
}


void receive_I2C_data(int rxBytes){
  //If we enter here is because the master has sent I2C message
  tempo_watchdog.reiniciar_t0();   //message received, it means there is communication
                                   // with the master, reinitiates the watchdog timer
  //We only expect 2 bytes: cmd and info
  if (rxBytes==2){
    cmd = Wire.read();
    info = Wire.read();
    if (debug) { 
      Serial.print("Received I2C: cmd = ");
      Serial.print(cmd); Serial.print(" info = "); Serial.println(info);
    }
  } else {
    Serial.println("Unknown message received from I2C...");
    cmd = NO_ACTION;
    info = NO_ACTION;
  }
  switch (cmd) {
    case MODIFICAR_T_SLEEP: //Note translation: modificar = modify
      interval = info * 1000;
      //send ACK to I2C??
      break;
    case FORCE_SEND_MSG:
      force_send_msg = true; 
      //send ACK to I2C??
      break;
    case GET_ISCAR_GARAGE:
      force_send_msg = true; 
      get_iscar = true;
      //send ACK to I2C??
      dist_cm_iscar = getDistance_finetune(); //If asked to check if car is in garage, 
                                      //we call the finetune function for better accuracy
      break;
    default:
      //Do nothing
      break;
  } 
}

 

The protocol is quite simple and allows future growth if I need to implement more commands to the slave node. A mistake I made is mixing least significant byte with the most significant byte... The point is that when the slave was sending the 11 bytes, the master interpreted them in the reverse order, so the information shared was just wrong... I lost some hours to figure the error (checking sensors, debugging, print-outs, etc).

 

RF24 Module

To implement the RF24 I have just followed the same steps as in my post 4. Despite being straightforward, I did two mistakes. The first one is that I connecrted the base module of the RF24 to a 3.3V source instead of the 5V. It took me more than an hour to figure it out... The second one is that I prepared the sketch to test the RF by removing some previous code and hehe by mistake I removed the command to actually send any data... more time lost...

 

Regarding the radio range, I´m just in the limit and I plan to replace the current modules by new RF24 modules with an antenna. I have started to test it and looks better. So I will keep testing it and then just probably replace them.

 

Summary of the Garage Node

In the end, the garage node has become a bit more complex than expected, but at least working. As you can see from the pictures below, the last minute changes have transformed two arduinos and some sensors into a mess of cables prone for errors and disconnections... I promise to arrange them much better! Will do it when I try to put them into a plastic box (like those you can by in the supermarkets to store your food) - as discussed in a forum discussion, I have not found any better enclosure for this node... image

 

imageimage

Yet another issue that I have found was with the Serial.print and the Mega. In one of the lines I wanted to inform via the serial port of a warning like this "Bla, bla bla !!!". I also made some changes to the code and it stopped compiling. It did not compile regardless my efforts to change the additional code and so until I found in google that the Mega goes into monitoring status when the uploader sends a string with the "!!!" and the upload never takes place... more time lost...

 

You can find the Arduino codes for the master and slave parts of the Garage node below.

 

Arduino Master

#include <RF24Network.h>
#include <RF24.h>
#include <SPI.h>
#include <Wire.h>
#include <Defs_y_Clases.h>

/*
 * Code for the Slave node at the garage 
 * It includes:
 *  RF 2.4Ghz communication with the master RF node
 *  I2C communications with the Arduino Slave
 * 
*/

const bool debug = true;  //If true, enables debug dump via Serial port

// 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 - RFwise
const uint16_t this_node = 5;
const uint16_t parent_node = 0;

//Constant for the id of the slave node on the I2C
const int id_I2Cslave = 8;

// 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

// Variables for the sensors
uint8_t h, l, motionPIR; //humidity and luminosity
int16_t t;   //temperature
uint16_t dist_sensor1=0, dist_sensor2=0, dist_sensor3=0;  //distance for ultrasound sensors 1,2,3

bool force_send_msg = false;  //Forces the transmission of the RF 2.4Ghz message to the node
bool get_iscar=false;

struct message_3 { // 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
  uint8_t humidity;
  uint8_t light;
  uint8_t motion;
  uint16_t distance_sensor1;  //variable for the ultrasound sensor 1
  uint16_t distance_sensor2;  //variable for the ultrasound sensor 2
  uint16_t distance_sensor3;  //variable for the ultrasound sensor 3
};
message_3 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_HIGH);  //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 I2C
  Wire.begin();
  
  if (debug) {
    Serial.print("Starting Master Node Garage. Node number: "); Serial.println(this_node);
  } 
}

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 (get_iscar){
    //the RF main module requested to get_iscar
    get_iscar = false;  
    force_send_msg = true;
    uint8_t cmd_slave = GET_ISCAR_GARAGE;         //Tell slave to get ISCAR, implies fine tune in the ultrasound
    uint8_t info_slave = 0;
    write_I2C_cmd(cmd_slave, info_slave); //We write into the I2C slave the cmd to get iscar
    delay(500);                           //We just requested get_iscar, allowing some time for the slave to respond
  }
  //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) {
    requestI2C_data();
    send_sensors_data();
    force_send_msg = false;
  }

  //force_send_msg = true;  //For testing purposes I can force the data to be sent
  //if (debug) Serial.println("Forcing send message every loop for tests.");
  delay(1000);  //Send data every 1000ms, maximum period, typically it will take longer
  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);
    }
    // Check value and turn the LED on or off
    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: //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 GET_ISCAR_GARAGE:
        force_send_msg = true; 
        get_iscar = 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 ");
    if (debug) Serial.println(header.from_node);
  }
}

void write_I2C_cmd(uint8_t cmd_slave, uint8_t info_slave) {
    Wire.beginTransmission(id_I2Cslave);    // transmit to device #8
    Wire.write(cmd_slave);                  // sends cmd byte
    Wire.write(info_slave);                 // sends info byte
    Wire.endTransmission();                 // stop transmitting
}
  
void send_sensors_data(){
  header.type = '3'; // Header 3 for this node type of info
  // 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

  if (abs(t - 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) {   //Note: PIR change already covered in loop
      //set distance variables

      // Construct the message we'll send
      message_tx = (message_3){ t, h, l, motionPIR, dist_sensor1, dist_sensor2, dist_sensor3 }; 
      // Writing the message to the network means sending it
      if (network.write(header, &message_tx, sizeof(message_tx))) {
        //The RF transmission was successful
        tempo_tx.reiniciar_t0();            //Reinitialize the timers
        tempo_watchdog.reiniciar_t0();      //reinitiates the watchdog timer
        tempo_tx_maxdelay.reiniciar_t0();   //Reinitialize the timer for maxdelay
        if (debug) Serial.print("Message sent RF with data\n"); 
      } else {
        if (debug) Serial.print("Could not send message RF with data\n"); 
      }
  
      if (debug) {
        Serial.print("Temp_tx: "); Serial.print((message_tx.temperature)); Serial.print(" Temp_detected: "); Serial.println((t)); 
        Serial.print("Hum_tx: "); Serial.print(message_tx.humidity); Serial.print(" Hum_detected: "); Serial.println((h)); 
        Serial.print("Luminosity: "); Serial.print(message_tx.light); Serial.print(" Motion PIR: "); Serial.print(message_tx.motion);
        Serial.print(" Distance 1: "); Serial.println(message_tx.distance_sensor1);
      }
    }
}


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"); 
  }

}

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();
  }
}

void requestI2C_data() {
  if (debug) Serial.println("Requesting I2C data...");
  Wire.requestFrom(8, 11);    //Request from I2C node 8 the data: 11 bytes
  if (debug) Serial.println("I2C after requestFrom...");
  uint8_t rxBytes=0;          //We count the number of bytes received, to ensure data is ok
  uint8_t buffer_rx[11];

  while (Wire.available()) {  
    if (rxBytes<11) buffer_rx[rxBytes] = Wire.read();
    rxBytes++;
  }
  if (rxBytes!=11) {
    if (debug) {
      Serial.print("Error in I2C data transmission. Received bytes: ");
      Serial.println(rxBytes);
    }
  } else {
    t = 0;                    //reset global variable for the temperature
    t= (buffer_rx[0]<<8);     //First 2 bytes are for the temperature. First byte received is the most significant one, moving it to the highest 8 bits
    t += buffer_rx[1];       //Second byte: low byte of the temperature. No need to divde by 100, we wil just resend it via RF
    h = buffer_rx[2];
    l = buffer_rx[3];
    motionPIR = buffer_rx[4];
    dist_sensor1 = 0;         //reset global variable for the distance sensor 1 - same as with temperature
    dist_sensor1 = (buffer_rx[5]<<8);
    dist_sensor1 += buffer_rx[6];
    dist_sensor2=0;
    dist_sensor3=0;
    if (debug) {
      send_data_serial();
      uint8_t i;
      for (i=0;i<11;i++) {
        Serial.print(buffer_rx[i]);
        Serial.print("-");
      }
      Serial.println("");
    }
  }
}

void send_data_serial() {
  if (debug) {
    Serial.print("Temp_detected: "); Serial.println((t)); 
    Serial.print("Hum_detected: "); Serial.println((h)); 
    Serial.print("Luminosity: "); Serial.println(l);
    Serial.print("Motion PIR: "); Serial.println(motionPIR);
    Serial.print("Distance 1: "); Serial.println(dist_sensor1);
    Serial.print("Received I2C packet from slave: "); 
  }
}

 

Arduino Slave

#include <Wire.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <DHT.h>
#include <UTFT.h>
//#include <UTouch.h>
//#include <UTFT_Buttons.h>
#include <NewPing.h>
#include <Defs_y_Clases.h>

/*
 * Code for the Slave Node at the garage 
 * It includes:
 *  Temperature sensor
 *  Humidity sensor
 *  Luminosity sensor
 *      -----   Removed due to SPI incompatibility ----- RF card at 2.4GHz
 *  PIR motion detector
 *  TFT Screen 5 inches
 *  I2C communications with Arduino master
 * 
 */

const bool debug = false;  //If true, enables debug dump via Serial port
// One wire pin - Temperature sensor
#define ONE_WIRE_BUS 8 

// The DHT humidity sensor pin
#define DHTPIN 9
#define DHTTYPE DHT11

#define PIR_PIN_GARAGE  10
#define TRIGGER_PIN     12  // Arduino pin tied to trigger pin on the ultrasonic sensor.
#define ECHO_PIN        13  // Arduino pin tied to echo pin on the ultrasonic sensor.
#define MAX_DISTANCE    500 // Maximum distance we want to ping for (in centimeters). Maximum sensor distance is rated at 400-500cm.
#define COLOR_VERDE     VGA_LIME
#define COLOR_ROJO      VGA_RED
#define COLOR_AMARILLO  VGA_YELLOW
#define COLOR_AMBAR     255,127,0
#define COLOR_AZUL      VGA_BLUE

// Initialize display
UTFT myGLCD(TFT01_50,38,39,40,41);          //For the 5 inch screen //UTFT    myGLCD(TFT01_24_8,19,18,17,16);   //For the 2.4 inches

// Initialize touchscreen
//UTouch  myTouch( 15, 8, 14, 9, 8);
// Initialize Buttons
//UTFT_Buttons  myButtons(&myGLCD, &myTouch);

// Declare which fonts we will be using
extern uint8_t SevenSegNumFont[];
//extern uint8_t BigFont[];
//extern uint8_t SmallFont[];

NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE); // NewPing setup of pins and maximum distance.
Temporizador tempo_ultrasonido(50, false);  //Allows at min 50ms before restarting the ultrasound sensor
Temporizador tempo_motion(120000, false);   //If no movement in 120s, it turns off the LCD screen
uint16_t dist_cm_1=0, prev_dist_cm_1, v=0;
uint16_t dist_cm_iscar=0;
unsigned long prev_t=0;
int Xpos, Ypos;                             //LCD position to display the distance in the display
bool is_actDisplay = false;                 //States if we need to update the display. Variable gets reset
                                            //if there was movement in the last 120s
bool prev_PIR_status = false;               //Stores previous status of the PIR, for debug purposes only

//Humidity sensor DHT object
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, 0xAB, 0x9D, 0x54, 0x15, 0x03, 0x63}; 
//Temp address of the sensor: 28FFAB9D54150363 

// Photocell variable
byte photocellPin = A8;

uint8_t cmd=0, info=0;    //Variables to store I2C message sent by the Master

// Timers
unsigned long interval = 60000;  // rep milisecs to activate the itmers with
// 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 sensors reading, every 60s it will force reading
Temporizador tempo_watchdog(3600000, false);    //Watchdog, if no update in 1h, activates watchdog

// 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
bool force_send_msg = false;  //Forces the transmission of the RF 2.4Ghz message to the node
bool get_iscar = false;       //Not yet fully implemented

//Constant for the id of this slave node on the I2C 
const int id_I2Cslave = 8;

void setup(void)
{
  if (debug) Serial.begin(9600);

  //Starting I2C communication with the Arduino master
  Wire.begin(id_I2Cslave);                          // join i2c bus with address #8
  Wire.onRequest(send_I2C_sensors_data);  // register I2C event, allows Master to request information - 11 bytes
  Wire.onReceive(receive_I2C_data);       // 2 bytes
  
  // Initialize the DHT library
  dht.begin();

  //Initialize pins
  pinMode(TRIGGER_PIN, OUTPUT);     //Ultrasound sensor
  pinMode(ECHO_PIN, INPUT);         //Ultrasound sensor
  pinMode(PIR_PIN_GARAGE, INPUT);   //PIR sensor

  // Initial setup LCD
  myGLCD.InitLCD();
  myGLCD.setFont(SevenSegNumFont);  
  //myTouch.InitTouch();
  //myTouch.setPrecision(PREC_MEDIUM);
  //myGLCD.setFont(SmallFont);
  //myGLCD.setBackColor(0, 0, 255); 
  //myGLCD.setBackColor(COLOR_AZUL); 
  //myButtons.setTextFont(BigFont);
  //myButtons.setTextFont(SmallFont);
  Xpos = myGLCD.getDisplayXSize()/2 - 50;
  Ypos = myGLCD.getDisplayYSize()/2 - 16;
  
  //Get oneWire devices on bus
  sensors.begin();
  sensors.setResolution(insideThermometer, 12);

  //Initial distance of the ultrasound
  dist_cm_1 = getDistance_finetune();
  v = getSpeed(dist_cm_1);      //Not yet fully implemented
  
  delay(1000);                  //Allows some sensor to set up: PIR, DHT, etc 
  myGLCD.fillScr(COLOR_AZUL);

  if (debug) {
    Serial.println("Starting Garage Node TFT Screen 5 inches, slave node.");
    Serial.print("Ultrasound Echo pin: "); Serial.println(ECHO_PIN);
    Serial.print("Ultrasound Trigger pin: "); Serial.println(TRIGGER_PIN);
    Serial.print("PIR Sensor pin: "); Serial.println(PIR_PIN_GARAGE);
    Serial.print("Dallas Temperature sensor: "); Serial.println(ONE_WIRE_BUS);
    Serial.print("DHT humidity sensor: "); Serial.println(DHTPIN);
  }
}

void loop() {
  if (change_inPIRsensor()){
    //If there is change in PIR, forces sensors reading
    force_send_msg = true;
  }
  //Check if it is time to read sensors data
  if (tempo_tx.is_Time() || force_send_msg) {
    force_send_msg = false;
    read_sensors_data();
  }
  
  reportDistance(); //if appropriate, reports the Distance from ultrasound to the TFT screen
  check_watchdog();

  if (debug) send_data_serial();
}

void receive_I2C_data(int rxBytes){
  //If we enter here is because the master has sent I2C message
  tempo_watchdog.reiniciar_t0();   //message received, it means there is communication
                                   // with the master, reinitiates the watchdog timer
  //We only expect 2 bytes: cmd and info
  if (rxBytes==2){
    cmd = Wire.read();
    info = Wire.read();
    if (debug) { 
      Serial.print("Received I2C: cmd = ");
      Serial.print(cmd); Serial.print(" info = "); Serial.println(info);
    }
  } else {
    Serial.println("Unknown message received from I2C...");
    cmd = NO_ACTION;
    info = NO_ACTION;
  }
  switch (cmd) {
    case MODIFICAR_T_SLEEP: //Note translation: modificar = modify
      interval = info * 1000;
      //send ACK to I2C??
      break;
    case FORCE_SEND_MSG:
      force_send_msg = true; 
      //send ACK to I2C??
      break;
    case GET_ISCAR_GARAGE:
      force_send_msg = true; 
      get_iscar = true;
      //send ACK to I2C??
      dist_cm_iscar = getDistance_finetune(); //If asked to check if car is in garage, 
                                      //we call the finetune function for better accuracy
      break;
    default:
      //Do nothing
      break;
  } 
}

void read_sensors_data() {
  sensors.requestTemperatures();
  h = dht.readHumidity();
  t = sensors.getTempC(insideThermometer);
  //Sensor for PIR is read separately. No need to re-read it here
  //Sensor for distance (ultrasound) is read separately. 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 reportDistance() {
  //If there was movement or the display is active, it gets the distance and reports it via TFT screen
  //Once there is no movement detected, waits 2 mins and turns off screen if no movement
  bool motion_detected = is_motion();
  if (motion_detected || is_actDisplay) {
    //if movement or is_actDisplay, we update the TFT display
    dist_cm_1 = getDistance();
    //v = getSpeed(dist_cm_1);
    displayDistance(dist_cm_1, true);
    //displaySpeed(v);  
    if (motion_detected) {
      tempo_motion.reiniciar_t0();   //it reinitialize the timer only if there was movement
      is_actDisplay = true; 
    }
  }  
  if (tempo_motion.is_Time()){
    //If 2 mins have gone without PIR movement, we turn off the display
    is_actDisplay = false;
    displayDistance(0, false);
  }
}

bool is_motion() {
  //returns value of the PIR sensor, which directly implies or not movement
  bool b = (digitalRead(PIR_PIN_GARAGE)==HIGH);
  if (debug) {
    //For debug purposes, we notify of changes on the PIR status
    if (b && !prev_PIR_status) {
      //Motion detected and there was no motion before, 
      //hence inform via Serial and update variable
      Serial.println("PIR: Motion started");
      prev_PIR_status = true;
    } else if (!b && prev_PIR_status) {
      //There is no motion detected and there was motion before, 
      //we inform not it stopped and update variable
      Serial.println("PIR: Motion stopped");
      prev_PIR_status = false;
    }
  }
  if (debug) {
    Serial.print("PIR Motion status:");
    Serial.print(b); delay(500);
  }
  return b;
}

bool change_inPIRsensor() {
  //True if there is a change in the PIR sensor: LOW to HIGH or HIGH to LOW
  bool b;
  is_motionPIR = (digitalRead(PIR_PIN_GARAGE)==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;  
}

unsigned int getDistance_finetune() {
  //we called, gets the ultrasound distnace, but tries to get more accurate measurements
  //this function is mainly uses for the feature: is the car in the garage
  //for this feature we dont need much speed but yes high accuracy to avoid false positives
  unsigned int meassure[7]={0,0,0,0,0,0,0};         //Variable to contain array of distances
  unsigned int max_val=0, min_val=MAX_DISTANCE * 2; //Variables to store max and min values
  unsigned int val=0;                               //Value to return
  int i;
  //We will make the 7 measurements, and then remove the max and min values and perform an average
  for (i=0;i<7;i++) {
    meassure[i] = getDistance();
    val += meassure[i];                               //We add all of the meassurements
    if (max_val < meassure[i]) max_val = meassure[i]; //store max value
    if (min_val > meassure[i]) min_val = meassure[i]; //store min value
  }
  //To remove the max and min, we just substract them from the sum
  val = val - max_val - min_val;
  //Now we just take the average by dividing by 5
  val = val / 5;
  if (debug) {
    Serial.print("Valor medio de Val para fine tune: ");
    Serial.println(val);
  }
  return val;
}

unsigned int getDistance() {
  //gets the ultrasound distance, it will obtain a median of the measuremnts
  //this function is mainly used for park assistance: we dont want to delay too much
  //the display refresh, hence not much finetuning in the value
  while (!tempo_ultrasonido.is_Time() ) {
    //Allow 50ms before next ultrasound check, to ensure no false echos come in
  }
  //unsigned int uS = sonar.ping(); // Send ping, get ping time in microseconds (uS).
  unsigned int uS = sonar.ping_median(); // Send ping, get ping time in microseconds (uS).
  return (sonar.convert_cm(uS)); // Convert ping time to distance and print result (0 = outside set distance range, no ping echo)
}

void displayDistance(uint16_t dist_cm_1a, bool showDist){
  //display distance in screen, if showDist=false clears screen
  if (showDist) {
    if (debug) Serial.print("Dist: "); 
    if (debug) Serial.print(dist_cm_1a);
    if (debug) Serial.println (" cm");
    //myGLCD.lcdOn();
    if (dist_cm_1a > 200) myGLCD.fillScr(COLOR_VERDE);
    else if (dist_cm_1a > 20) myGLCD.fillScr(COLOR_AMARILLO);
    else myGLCD.fillScr(COLOR_ROJO);
    String s = String(dist_cm_1a);
    if (dist_cm_1a < 100) s = "0"+s;  //If 2 digits, adds 0s to the left
    if (dist_cm_1a <  10) s = "0"+s;
    myGLCD.print(s, Xpos, Ypos);  //If 1 digit, adds 0 to the left
  } else {
    //we just clear screen and turn it off
    myGLCD.clrScr();
    if (debug) Serial.println ("No activity. Screen to off");
    //myGLCD.lcdOff();
  }
}

unsigned int getSpeed(unsigned int dist_cm_1a){
  unsigned int v1=0;
  
  unsigned long t1 = (micros() - prev_t)/1000;  //Calculo incremento de t 
  unsigned long dist = abs(dist_cm_1a - prev_dist_cm_1)*1000; //Calculo incremento de distancia
  v1 = (unsigned int) dist/t1;
  prev_dist_cm_1 = dist_cm_1a; //reinicio distancia inicial
  prev_t = micros(); //reinicio t0
  return v1;
}

void displaySpeed(unsigned int v1) {
  if (debug) Serial.print("Velo: "); 
  if (debug) Serial.print(v1);
  if (debug) Serial.println (" cm/s");
}

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()) {
    //To be implemented
    //At this point in time, this does nothing special, just print error via Serial
    //Improvement: to ensure lights to off, etc
    if (debug) Serial.println("Alarm! Watchdog timer. No successfull communication with the center");    
  }
}

void send_data_serial() {
  if (debug) {
    Serial.print("Temp_detected: "); Serial.println((t)); 
    Serial.print("Hum_detected: "); Serial.println((h)); 
    Serial.print("Luminosity: "); Serial.println(l);
    Serial.print("Motion PIR: "); Serial.println(is_motionPIR);
    Serial.print("Distance 1: "); Serial.println(dist_cm_1);
    Serial.print("Sending I2C to master: "); 
  }
}

void send_I2C_sensors_data() {
  // If we enter here, the I2C master has requested data to be sent. 
  // Sends data to the Master. Master expects 11 bytes
  uint8_t buffer_tx[11]; //The wire.write function sends data per bytes
      //The function will send an array of data as follows:
      //    2xbytes for the int16_t of the temperature
      //    1xbyte for the humidity
      //    1xbyte for the light
      //    1xbyte for the motion
      //    2xbytes for the uint16_t of sensor distance 1
      //    2xbytes for the uint16_t of sensor distance 2
      //    2xbytes for the uint16_t of sensor distance 3
   
  tempo_watchdog.reiniciar_t0();    //Reinitialize the watchdog timer, since we have received an I2C message

  float t1 = t*100;             //multiplies by 100 to get rid of float and transmit only uint16
  int16_t t_tx = (int16_t) t1; 
  uint16_t dist_cm_1_tx = 0;    //Initialize the variable, ensuring all bits are set to 0
  
  if (get_iscar) {
    //if get_iscar -> the master requested previously to send get_iscar data
    //the distance to tx will be the _iscar one
    get_iscar = false;
    dist_cm_1_tx = dist_cm_iscar;
  } else dist_cm_1_tx = dist_cm_1;
  
  buffer_tx[0] = t_tx >> 8;             //first byte of temperature uint16_t
  buffer_tx[1] = t_tx & 0xFF;           //second byte
  buffer_tx[2] = h;                     //humidity
  buffer_tx[3] = l;                     //luminosity
  buffer_tx[4] = is_motionPIR;          //motion
  buffer_tx[5] = dist_cm_1_tx >>8;      //first byte of distance sensor 1
  buffer_tx[6] = dist_cm_1_tx & 0xFF;   //second byte of distance sensor 1
  buffer_tx[7] = 0;                     //first byte of distance sensor 2
  buffer_tx[8] = 0;                     //second byte of distance sensor 2
  buffer_tx[9] = 0;                     //first byte of distance sensor 3
  buffer_tx[10] = 0;                    //second byte of distance sensor 3
  
  Wire.write(buffer_tx, 11); // respond with message with sensors data
  if (debug) {
    send_data_serial();
    uint8_t i;
    for (i=0;i<11;i++) {
      Serial.print(buffer_tx[i]);
    }
    Serial.println("");
  }
}

 

Nodes´ Dashboard

image

  • Sign in to reply

Top Comments

  • clem57
    clem57 over 9 years ago +1
    That wiring is very artsy not to be imitated. I love it! Clem
  • fvan
    fvan over 9 years ago +1
    Impressive wiring indeed
  • clem57
    clem57 over 9 years ago in reply to mg.sergio +1
    It takes a sharp eye to make and check those leads. Don't' kid yourself. I have done a few over time and amaze myself how I ever wired it up. I also believe a clean desk is a sign of a empty mind. Cle…
Parents
  • clem57
    clem57 over 9 years ago

    That wiring is very artsy not to be imitated. I love it! image

    Clem

    • Cancel
    • Vote Up +1 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • mg.sergio
    mg.sergio over 9 years ago in reply to clem57

    I should feel embarrassed... and I could say "it is a prototype", "it was in favour of speed", "just to check if it worked"... all excuses image

     

    Thanks for the "very artsy" image and yes, not to be imitated or repeated, hehe

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

    I should feel embarrassed... and I could say "it is a prototype", "it was in favour of speed", "just to check if it worked"... all excuses image

     

    Thanks for the "very artsy" image and yes, not to be imitated or repeated, hehe

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

    It takes a sharp eye to make and check those leads. Don't' kid yourself. I have done a few over time and amaze myself how I ever wired it up. I also believe a clean desk is a sign of a empty mind.image

    Clem

    • Cancel
    • Vote Up +1 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