Go Down

Topic: Functions? Creating a Radio Hub  (Read 337 times) previous topic - next topic

Watson221

Apr 16, 2018, 06:23 am Last Edit: Apr 16, 2018, 06:24 am by Watson221
All of my previous posts on this forum have been my Automatic Chicken Coop Door project. This is a continuation of that project. Essentially I wanted to have the chicken coop send a message to a hub in the house, telling the hub if it's closed or open. Originally I used a led that would turn on and off and that worked fine, but I decided to do something a little fancier.

For this project I want to display the Chicken Coop Door's state on an LCD, but I also want to create a security system. I have the Arduino Uno attached to a keypad (1,2,3,4,5,6,7,8.9.0,#,*), 2x16 LCD, and a nRF module.   Right now I'm struggling to create a menu or some type of layout for the LCD. I want to have different screens. For the time being I'd like to focus on displaying the signal I'm getting from the Chicken Coop, since my motion senor is en route.

 So on screen 1 I'd like to have a menu that allows you to go to either the security system's screen or the chicken coop door's screen. ( press * for Coop, and press # for security). After you press * we would go to screen 2, where I'd like to display "the door is" on the top line of the LCD and on the bottom line I'd like to display the Door's state, open/closed.

I don't know how to make the door's state dynamic. I tried to create a function that would spew out a result and include that under screen 2. The problem I'm having is that when I click * I only get to the screen 2 if the Coop Door's Arduino isn't transmitting. When I turn off the chicken coop door and click * I can get to screen 2 and it displays the coop's door as being open. So I have no idea how to display the coop's state live.

Thanks for the Help!

The Keypad  pins-

Row pins = 3, 5, 6, 7

Column pins = 8, 9, 10


LCD pins-
A0 - RS
A1 - E
A2 - D4
A3 - D5
A4 - D6
A5 - D7

The rest of the pins are attached to the the 5v pins and ground as necessary.

nRF Pins-
13 - SCK
12 - MISC
11 - MOSI
2   - CE
4   - CS


Code: [Select]


#include <Keypad.h>
#include <LiquidCrystal.h>
#include <nRF24L01.h>
#include <RF24.h>
#include <RF24_config.h>
#include <SPI.h>


boolean doorState = LOW;

//Radio
int msg[1];
RF24 radio(2, 4);
const uint64_t pipes[2] = {
  0xF0F0F0F000LL, 0xF0F0F0F0FFLL
};

//LCD
LiquidCrystal lcd(A0, A1, A2, A3, A4, A5);

//Keypad
const byte ROWS = 4;
const byte COLS = 3;

char hexaKeys[ROWS][COLS] = {
  {'1', '2', '3'},
  {'4', '5', '6'},
  {'7', '8', '9'},
  {'*', '0', '#'}
};
byte rowPins[ROWS] = {3, 5, 6, 7};
byte colPins[COLS] = {8, 9, 10};

Keypad customKeypad = Keypad( makeKeymap(hexaKeys), rowPins, colPins, ROWS, COLS);

void setup() {
 
  radio.begin();
  radio.setDataRate(RF24_250KBPS);
  radio.setChannel(100);
  radio.setRetries(15, 15);
  radio.openWritingPipe(pipes[0]);
  radio.openReadingPipe(1, pipes[1]);
  radio.setPALevel(RF24_PA_MAX);
  radio.startListening();

  //Screen 1
  lcd.begin(16, 2);
  lcd.setCursor(0, 0);
  lcd.print("* for Coop");
  lcd.setCursor(0, 1);
  lcd.print("# for Security");

  Serial.begin(9600);
}

void loop() {
  char customKey = customKeypad.getKey();

  if (customKey) {
    Serial.println(customKey);
  }

  //Radio Code
  if (radio.available()) {
    bool done = false;
    while (!done) {
      done = radio.read(msg, 1);

      if (msg[0] == 111) {
        delay(10);
        doorState = HIGH;
      }

      else if (msg[0] == 112) {
        doorState = HIGH;
      }
    }
  }

  // Screen 2
  if (customKey == '*' ) {
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print(" Door Is ");
    CoopDoor();

  }
}

void CoopDoor() {
  if (doorState = HIGH) {
    lcd.setCursor(0, 1);
    lcd.print("Open");
    delay(1000);
  }
  else if (doorState = LOW) {
    lcd.setCursor(0, 1);
    lcd.print("Closed");
    delay(1000);
  }
}




 



Coding Badly


Start with the low hanging fruit...

Code: [Select]

...
  if (doorState = HIGH) {
...
  else if (doorState = LOW) {
...



Watson221

I'm sorry. I don't understand what you mean. Does that mean that the function is written correctly or did i implement it wrong?

UKHeliBob

= for assignment
== for comparison
Please do not send me PMs asking for help.  Post in the forum then everyone will benefit from seeing the questions and answers.

Watson221

Oh! Ok, thank you! I'll fix that as soon as I get home. Thanks for the help!

Watson221

Hi, that worked beautifully! The only way I can get the door's state to update is if I click the rest button. Is there some way I can get the door's state to update automatically on screen 2?

UKHeliBob

Please do not send me PMs asking for help.  Post in the forum then everyone will benefit from seeing the questions and answers.

Watson221

There you go! The only thing I changed was how many equal signs I had in the CoopDoor function.

Code: [Select]

#include <Keypad.h>
#include <LiquidCrystal.h>
#include <nRF24L01.h>
#include <RF24.h>
#include <RF24_config.h>
#include <SPI.h>


boolean doorState = LOW;

//Radio
int msg[1];
RF24 radio(2, 4);
const uint64_t pipes[2] = {
  0xF0F0F0F000LL, 0xF0F0F0F0FFLL
};

//LCD
LiquidCrystal lcd(A0, A1, A2, A3, A4, A5);

//Keypad
const byte ROWS = 4;
const byte COLS = 3;

char hexaKeys[ROWS][COLS] = {
  {'1', '2', '3'},
  {'4', '5', '6'},
  {'7', '8', '9'},
  {'*', '0', '#'}
};
byte rowPins[ROWS] = {3, 5, 6, 7};
byte colPins[COLS] = {8, 9, 10};

Keypad customKeypad = Keypad( makeKeymap(hexaKeys), rowPins, colPins, ROWS, COLS);

void setup() {
  radio.begin();
  radio.setDataRate(RF24_250KBPS);
  radio.setChannel(100);
  radio.setRetries(15, 15);
  radio.openWritingPipe(pipes[0]);
  radio.openReadingPipe(1, pipes[1]);
  radio.setPALevel(RF24_PA_MAX);
  radio.startListening();

  //Screen 1
  lcd.begin(16, 2);
  lcd.setCursor(0, 0);
  lcd.print("* for Coop");
  lcd.setCursor(0, 1);
  lcd.print("# for Security");

  Serial.begin(9600);
}

void loop() {
  char customKey = customKeypad.getKey();

  if (customKey) {
    Serial.println(customKey);
  }

  //Radio Code
  if (radio.available()) {
    bool done = false;
    while (!done) {
      done = radio.read(msg, 1);

      if (msg[0] == 111) {
        delay(10);
        doorState = HIGH;
      }

      else if (msg[0] == 112) {
        doorState = HIGH;
      }
    }
  }

  // Screen 2
  if (customKey == '*' ) {
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("The Coop Door Is");
    CoopDoor();
  }
}

void CoopDoor() {
  if (doorState == HIGH) {
    lcd.setCursor(0, 1);
    lcd.print("Open");
    delay(1000);
  }
  else if (doorState == LOW) {
    lcd.setCursor(0, 1);
    lcd.print("Closed");
    delay(1000);
  }
}




UKHeliBob

If you want the door state to update on the screen in real time then you need to write to it periodically, perhaps by calling the CoopDoor() function but only when screen 2 is being displayed.

Suggestions:

Add a variable that holds an indication of the screen currently on display
At the end of loop() use millis() to determine whether the required screen refresh period has passed since the previous iteration of loop() and, if screen 2 is currently being displayed call the CoopDoor function to update the screen.

If you are not familiar with using millis() for timing then read Using millis() for timing.  A beginners guide,   Several things at the same time and look at the BlinkWithoutDelay example in the IDE.
Please do not send me PMs asking for help.  Post in the forum then everyone will benefit from seeing the questions and answers.

Watson221

#9
Apr 17, 2018, 06:59 pm Last Edit: Apr 17, 2018, 07:19 pm by Watson221
Ok the way I understand it is under my screen 2 if statement I should have a variable that tells me that screen 2 is displaying.

So
Code: [Select]

  if (customKey == '*' ) {
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("The Coop Door Is");
    ScreenTwo=HIGH;
  }
}


Then have an other if statement
Code: [Select]

  if (ScreenTwo == HIGH){
     CoopDoor();
     delay(1000);
}


I think I used the delay correctly here. I want to check the CoopDoor function every second and display that change.
 

UKHeliBob

Quote
I think I used the delay correctly here. I want to check the CoopDoor function every second and display that change.
 
The idea sounds OK but personally I would use millis() for non blocking timing as I described in my previous post but try it with delay() first.
Please do not send me PMs asking for help.  Post in the forum then everyone will benefit from seeing the questions and answers.

Watson221

Right! I'm just not as familiar with millis(), so until I can mess around with it I'm going to use delay it's place. Thanks though for that really in depth forum post you gave me the link to. That really helped!

Watson221

#12
Apr 18, 2018, 02:54 am Last Edit: Apr 18, 2018, 03:20 am by Watson221
I'm confused by the whole millis thing. I understand why it's better than delay in this instance, but I don't know how to implement it.

I tried doing this, but It would switch over to the next screen and back to the 1st before I could react, so something is definitely wrong with the millis function.

Code: [Select]


#include <Keypad.h>
#include <LiquidCrystal.h>
#include <nRF24L01.h>
#include <RF24.h>
#include <RF24_config.h>
#include <SPI.h>

//Timer
unsigned long startMillis;
unsigned long currentMillis;
const unsigned long ScreenRefresh = 5000;


boolean ScreenTwo = LOW;
boolean doorState = LOW;

//Radio
int msg[1];
RF24 radio(2, 4);
const uint64_t pipes[2] = {
  0xF0F0F0F000LL, 0xF0F0F0F0FFLL
};

//LCD
LiquidCrystal lcd(A0, A1, A2, A3, A4, A5);

//Keypad
const byte ROWS = 4;
const byte COLS = 3;

char hexaKeys[ROWS][COLS] = {
  {'1', '2', '3'},
  {'4', '5', '6'},
  {'7', '8', '9'},
  {'*', '0', '#'}
};
byte rowPins[ROWS] = {3, 5, 6, 7};
byte colPins[COLS] = {8, 9, 10};

Keypad customKeypad = Keypad( makeKeymap(hexaKeys), rowPins, colPins, ROWS, COLS);

void setup() {
  radio.begin();
  radio.setDataRate(RF24_250KBPS);
  radio.setChannel(100);
  radio.setRetries(15, 15);
  radio.openWritingPipe(pipes[0]);
  radio.openReadingPipe(1, pipes[1]);
  radio.setPALevel(RF24_PA_MAX);
  radio.startListening();

  //Timer
  startMillis = millis();

  //Screen 1
  lcd.begin(16, 2);
  MainMenu();

  Serial.begin(9600);
}

void loop() {
  //Timing
  currentMillis = millis();

  //Keypad
  char customKey = customKeypad.getKey();
  if (customKey) {
    Serial.println(customKey);
  }

  //Radio Code
  if (radio.available()) {
    bool done = false;
    while (!done) {
      done = radio.read(msg, 1);

      if (msg[0] == 111) {
        delay(10);
        doorState = HIGH;
      }

      else if (msg[0] == 112) {
        doorState = HIGH;
      }
    }
  }

  // Screen 2
  if (customKey == '*' ) {
    currentMillis = millis();
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("The Coop Door Is");
    ScreenTwo = HIGH;
   
    if (startMillis - currentMillis >= ScreenRefresh) {
      ScreenTwo = LOW;
      lcd.clear();
      MainMenu();
    }
  }

  if (ScreenTwo == HIGH) {
    CoopDoor();
  }
}

void CoopDoor() {
  if (doorState == HIGH) {
    lcd.setCursor(0, 1);
    lcd.print("Open");

  }
  else if (doorState == LOW) {
    lcd.setCursor(0, 1);
    lcd.print("Closed");

  }
}

void MainMenu () {
  lcd.setCursor(0, 0);
  lcd.print("* for Coop");
  lcd.setCursor(0, 1);
  lcd.print("# for Security");
}



Just FYI I also decide to make screen 1 a function called MainMenu, so I could go back to the start without having to press reset.

UKHeliBob

I have not had time to look at your code in detail but this line stood out

Code: [Select]
   if (startMillis - currentMillis >= ScreenRefresh)
If the variable names reflect the data they hold then the logic is wrong.

When using millis() for timing you need to check whether a certain period of time has passed since something occurred.  This is best done by subtracting the start time from the current time and comparing it with the required timing period.  You seem to have done this the wrong way round based on the variable names.

Replacing delay() with millis() timing in your code to refresh screen 2 when it is being displayed should be simple.  When you first move to screen 2 set ScreenTwo HIGH and save the start time and call CoopDoor().  Then, independent on any other code do this in loop()

Code: [Select]

if (ScreenTwo == HIGH)  //if we are showing screen 2
{
  if (currentMillis - startMillis >= ScreenRefresh)  //if the period has passed
  {
    ShowCoop();  //refresh the screen
    startMillis() = currentMillis();  //save the time that it was refreshed
  }
}

 
Please do not send me PMs asking for help.  Post in the forum then everyone will benefit from seeing the questions and answers.

Watson221

I tried to simplify my code. I think I need to do a few things. Maybe make the ScreenTwo= LOW and lcd.clear() stuff a function to clean up everything, or put the millis stuff into the ~~ I feel like something should go here ~~ part.

Helping a novice like me, probably seems grueling, but I really appreciate all your help!


Code: [Select]


unsigned long startMillis;
unsigned long currentMillis;
const unsigned long ScreenRefresh = 5000;

void loop(){

  currentMillis = millis();


  if (customKey == '*' ) {
    currentMillis = millis();
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("The Coop Door Is");
    ScreenTwo = HIGH;
 
~~Feel Like Something Should Go Here ~~
   
    ScreenTwo = LOW;
    lcd.clear();
    MainMenu();
    }
  }
 
 if (ScreenTwo == HIGH)  //if we are showing screen 2
  {
    if (currentMillis - startMillis >= ScreenRefresh)  //if the period has passed
    {
      CoopDoor();  //refresh the screen
      startMillis = currentMillis;  //save the time that it was refreshed
    }
  }
}



So maybe something like this?

Code: [Select]

unsigned long startMillis;
unsigned long currentMillis;
const unsigned long ScreenRefresh = 5000;

void loop(){

  currentMillis = millis();


  if (customKey == '*' ) {
    currentMillis = millis();
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("The Coop Door Is");
    ScreenTwo = HIGH;
 
    }
  }
 
 if (ScreenTwo == HIGH)  //if we are showing screen 2
  {
    if (currentMillis - startMillis >= ScreenRefresh)  //if the period has passed
    {
      CoopDoor();  //refresh the screen
      startMillis = currentMillis;  //save the time that it was refreshed
      ClearScreen();
    }
  }
}

void ClearScreen (){

    ScreenTwo = LOW;
    lcd.clear();
    MainMenu();


Go Up