Arduino: Controlling The Paludarium | “Edens.Bow” | Ch.3

Are you ready for the fun part? Arduino magic! Now that chapter 2 has covered how to build the paludarium, we can start adding some level of intelligence to it.

This chapter might be a bit complex if you’re looking at code or tinkering with electronics for the first time. If it gives you any relief, I had no real experience with Arduino before attempting to do this project.. Understanding Arduino really is that easy if you’re driven enough!




What Is Arduino?

Arduino is an open-source platform that make hardware and software based products. Their boards like the Mega2560 board we will be using today.. Are micro-controllers you can program to do virtually anything. Arduino makes a programming software called Arduino IDE, which allows you to have programming control over Arduino’s micro-controller boards.

Arduino Terrarium/Aquarium Controller

I looked at a number of terrarium controllers on the market.. Besides the hefty price tags, I wasn’t really impressed with what I found. Nothing against the hard work available on today’s market. I just wanted specific capabilities with a more modern look.

I wanted something user-friendly, yet feature stuffed.. I’m a gamer (maybe not as much anymore), a joystick powered HUD with responsive sound effects would make the simplest task, feel entertaining to interact with.

Features:

With this chapter of the BEHINDtheBUILD I will cover a number of features you will be able to demonstrate from Edens.Bow:

  • Operate Arduino vivarium controller via joystick and LCD screen
  • Monitor air & water temperatures as well as humidity
  • Control externally powered items like mist-maker, fan & rain irrigation system
  • Toggle through a series of menu options

Constructing A Smart Paludarium controller

Let’s gather the items needed for this part of the build. I tried my hardest to keep a reasonable budget with these parts.

Materials:

Mega2560 Ultimate Starter Kit – I would highly recommend getting this kit. If you are considering going with a clone Mega2560 board, this kit will come with most of the parts used to build this paludarium. I will put (**) next to the items this kit includes so you know what you will still need to consider getting if you choose to order this kit. I will put (*) to indicate the kit comes with an alternative item that isn’t that part but could be substituted to work. https://amzn.to/2LqQ43e

  • Arduino Mega2560 board* – This is the micro-controller board I decided to go with. A clone should work as well but shop at your own risk. https://amzn.to/2Lm0hha
  • 20×4 LCD Module Shield* – This LCD monitor comes with a shield making it capable of using less pins on the Arduino than if you used the LCD by itself. https://amzn.to/2LowOn3
  • Joystick Module** – This is the joystick I used to control the features from the side of the tank. https://amzn.to/2OdWEHY
  • Power Supply Module** – It’s best to power everything separate from the board. Just make sure it is all connected through the ground wire on the breadboard. https://amzn.to/2uOEp3X
  • Power Adapter – You will need two of these, one for the board and one for the breadboard if your Arduino doesn’t come with one already. https://amzn.to/2LrVdYY
  • DHT11 Temperature and Humidity Module** – We will use this sensor to monitor the air temperature and humidity. https://amzn.to/2uRTz8l
  • Waterproof 10k Thermistor Probe* – This will be used to monitor under water temperature. The starter kit does come with a thermistor, it isn’t waterproof though. https://amzn.to/2LRzcP8
  • Breadboard Kit** – If you decide not to go with the starter kit, I’d recommend getting at least this kit. It comes with a breadboard, jumper wires and resistors.. Plus a couple of other little odds and ends that will come in handy later. https://amzn.to/2OgwFjf
  • Solder-able Breadboard – Once you are ready to install the electronics into the tank, It would be highly recommended you solder everything down into one of these boards.. I learned this the hard way and had to do it later when I started having faulty wire issues with the LED’s. https://amzn.to/2mMk0b3
  • 8 Channel Relay Module – This is an amazing way to power devices the Arduino board doesn’t have the energy to push out. https://amzn.to/2JVowgL

Tools:

  • Arduino IDE Software – This is the software you will use to write and send code to the Arduino board. You can download it for free here
  • Soldering Iron Kit – This is going to be extremely handy when it’s time to lock down the wires and keep them from moving later. https://amzn.to/2Lr3bS9

Setting Up The LCD Screen

This is the very basics to understanding Arduino. We will start with displaying a simple message:

“Bantam.Earth”

  1. Start by connecting your LCD module shield to the Mega2560 like demonstrated in the diagram:
  2. With everything connected in its right place we can plug it up to a laptop and upload some code. Open a new IDE sketch and you should have something like this:
    void setup() {
      // put your setup code here, to run once:
    }
    
    void loop() {
      // put your main code here, to run repeatedly:
    
    }
  3. You will notice you have two voids already written, a “setup” and a “loop”. The “setup” void is everything are board does when it first loads up. The “loop” void is the code your micro-controller cycles through repeatedly until the board is powered off. Now that we know how the code is laid out, the first thing we need to do is include the library for the proper LCD monitor. This is the first line of code we add to the top above what is already there:
    #include <LiquidCrystal_I2C.h>
    LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);
  4. Next, we will need to tell the LCD screen to display a message for three seconds.. Afterwards clear the screen. The code should go into the “setup” void like this:
    void setup() {
      // put your setup code here, to run once:
      lcd.begin(20,4);
      lcd.backlight(); // finish with backlight on
      lcd.setCursor(4,1);
      lcd.print("Bantam.Earth");
      delay(3000);
      lcd.clear(); 
    }
  5. We can now verify that the code works by pressing the check symbol. Once verified, upload it to the board. Make sure you are using the correct Board and Processor under the “tools” tab. The code you are uploading should look like this so far:
    #include <LiquidCrystal_I2C.h>
    LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);
    
    void setup() {
      // put your setup code here, to run once:
      lcd.begin(20,4);
      lcd.backlight(); // finish with backlight on
      lcd.setCursor(4,1);
      lcd.print("Bantam.Earth");
      delay(3000);
      lcd.clear(); 
    }
    
    void loop() {
      // put your main code here, to run repeatedly:
    
    }
  6. If all is correct you should see the message on your LCD monitor. Remember to adjust the screen brightness if you are having trouble viewing the text on the monitor.

Monitoring Humidity & Temperature

Let’s now create a main menu screen that displays the current temperature and humidity of the tank. We will set up are code so that the intro message is display when the Arduino board is first powered on.. After a few seconds, we get the main menu.

  1. Before updating the code for temperature and humidity monitoring, let’s add the sensors to the Mega2560 board first:
  2. We will add a new library so that the humidity sensor(DHT) is compatible on our board. Lets add this line of code above the LCD library:
    #include <dht.h>
  3. Next we have to define the pin humidity Sensor is placed on the Arduino board. Add this code somewhere under the libraries:
    dht DHT; 
    #define DHT11_PIN 11
  4. A couple of variables will need to be added in order for the board to understand what it needs to do with the signals it receives from the sensors. we will also define the water thermostat pin as well here:
    int ThermistorPin = 0;
    int Vo;
    float R1 = 10000;
    float logR2, R2, WaterTemp, AirTemp;
    float c1 = 1.009249522e-03, c2 = 2.378405444e-04, c3 = 2.019202697e-07;
    int tCount1;
    bool refresh;//lcd clear On/Off
    unsigned long readTime;
  5. With the variables established, we can now create two new voids. These voids will check the sensors every second and a half, than update the display on the LCD screen. Add this code after the “loop” void:
    void updatestatistics(){
      readTime = millis();
      //read air temperature & humidity
      int chk = DHT.read11(DHT11_PIN);
      AirTemp = DHT.temperature;
      AirTemp *= 1.8;
      AirTemp += 30;
      //read water temperature
      Vo = analogRead(ThermistorPin);
      R2 = R1 * (1023.0 / (float)Vo - 1.0);
      logR2 = log(R2);
      WaterTemp = (1.0 / (c1 + c2*logR2 + c3*logR2*logR2*logR2));
      WaterTemp = WaterTemp - 273.15;
      WaterTemp = (WaterTemp * 9.0)/ 5.0 + 32.0;
    writestatistics();
    }
    
    void writestatistics(){
      //write air temperature to LCD
      lcd.setCursor(1,1); 
      lcd.print("Temp:  A:");
      lcd.print(AirTemp, 0);
      lcd.print((char)223);
      lcd.print(" W:");
      // write water temperature to LCD
      lcd.print(WaterTemp, 0);
      lcd.print((char)223);   
      //lcd.print("F");
      // write humidity to LCD
      lcd.setCursor(4,2);
      lcd.print("Humidity ");
      lcd.print(DHT.humidity, 0);
      lcd.print("%");  
    }
    
  6. We can now initiate these voids every 1.5 seconds by adding this code to the loop:
    void loop() {
      // put your main code here, to run repeatedly:
    if(millis() > readTime+1500){
        updatestatistics();
      }
    }
  7. Before I forget, Lets set the “readTime” variable to 0 in the “setup” void so it always starts from scratch:
    readTime = 0;
  8. Everything should look like this before verifying and uploading our new code:
    #include <dht.h>
    #include <LiquidCrystal_I2C.h>
    LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);dht DHT;
    #define DHT11_PIN 11//Assign thermistor to pin
    int ThermistorPin = 0;
    int Vo;
    float R1 = 10000;
    float logR2, R2, WaterTemp, AirTemp;
    float c1 = 1.009249522e-03, c2 = 2.378405444e-04, c3 = 2.019202697e-07;
    int tCount1;
    bool refresh;//lcd clear On/Off
    unsigned long readTime;void setup() {
    // put your setup code here, to run once:
    lcd.begin(20,4);
    lcd.backlight(); // finish with backlight on
    lcd.setCursor(4,1);
    lcd.print("Bantam.Earth");
    delay(3000);
    lcd.clear();
    readTime = 0;
    }void loop() {
    // put your main code here, to run repeatedly:
    if(millis() > readTime+1500){
    updatestatistics();
    }
    }void updatestatistics(){
    readTime = millis();
    //read air temperature & humidity
    int chk = DHT.read11(DHT11_PIN);
    AirTemp = DHT.temperature;
    AirTemp *= 1.8;
    AirTemp += 30;
    //read water temperature
    Vo = analogRead(ThermistorPin);
    R2 = R1 * (1023.0 / (float)Vo - 1.0);
    logR2 = log(R2);
    WaterTemp = (1.0 / (c1 + c2*logR2 + c3*logR2*logR2*logR2));
    WaterTemp = WaterTemp - 273.15;
    WaterTemp = (WaterTemp * 9.0)/ 5.0 + 32.0;
    writestatistics();
    }void writestatistics(){
    //write air temperature to LCD
    lcd.setCursor(1,1);
    lcd.print("Temp: A:");
    lcd.print(AirTemp, 0);
    lcd.print((char)223);
    lcd.print(" W:");
    // write water temperature to LCD
    lcd.print(WaterTemp, 0);
    lcd.print((char)223);
    //lcd.print("F");
    // write humidity to LCD
    lcd.setCursor(4,2);
    lcd.print("Humidity ");
    lcd.print(DHT.humidity, 0);
    lcd.print("%");
    }
  9. When the board updates, You should see a welcome message and than stats. Don’t be alarmed if the air and water temperature is off by a few degrees.. The thermistor is a bit more accurate than the DHT sensor.
  1. Using A Joystick To Navigate

    This was a very challenging task to figure out. There wasn’t much information out there about operating menus with joystick modules. I had to sort of figure this one out and fine tune it a bit to get it working accurately.

  1. Here’s a diagram of the joystick module connected to the Mega2560 board:
  2. Just like before, We need to define the pins the joystick will use on the Arduino board:
    #define xPin A1 
    #define yPin A2 
    #define kPin 12
  3. Now we need to assign those pins as output signals in the “setup” void with this code:
    //assign Joystick pins as output signal
    pinMode(xPin, INPUT);
    pinMode(yPin, INPUT);
    pinMode(kPin, INPUT_PULLUP);
  4. These are the variables will need to list for the joystick module:
    //ReadJoystick
    int joyRead;
    int joyPos; 
    int lastJoyPos;
    int joyButtonPushed;
    long lastDebounceTime = 0; 
    long debounceDelay = 70; //user define
    int pushedTime = 0;
    //---------Storage states of buttons for debounce function-----// 
    boolean current_sel = LOW;
    boolean last_sel = LOW;
    //-------ALT button Pins we wont need, so lets stash them on pin 4 for now-----//
    int up = 4; //Up/Yes button 
    int sel = 4; //Select button
    int down = 4; //Down/No button
  5. We will be creating two new functions similar to a void. The first is an integer, this will define the functionality of the joystick. The second is a Boolean, this helps the board understand the difference between an intentional press and an unintentional press. here is the codes for that:
    int readJoystick(){
    int x = analogRead(xPin);
    int y = analogRead(yPin);
    int k = digitalRead(kPin);
    if(x>600){joyRead=1; //x+
    }else if(x<100){joyRead=2; //x-
    }else if(y>600){joyRead=3; //y+
    }else if(y<100){joyRead=4; //y-
    }else if(!k){joyRead=5;
    }else{joyRead=0;}if (joyRead != lastJoyPos){lastDebounceTime = millis();}
    if(((millis() - lastDebounceTime) > debounceDelay)&&(joyRead!=joyPos)){
    joyPos=joyRead;
    }if (joyPos != lastJoyPos && pushedTime > 3){
    joyButtonPushed =joyPos;
    }
    if (joyRead != 0){
    pushedTime++;
    }
    if (pushedTime >= 50){
    pushedTime = 0;
    }
    lastJoyPos=joyRead;
    current_sel = debounce(last_sel, sel); //Debounce for Select button
    }boolean debounce(boolean last, int pin){
    boolean current = digitalRead(pin);
    if (last != current){
    delay(5);
    current = digitalRead(pin);
    }
    return current;
    }
  6. Lastly we can add the joystick functionality to the “loop” void so it is always available:
     readJoystick();
  7. Everything all together should now look like this:
    //libraries
    #include <dht.h>
    #include <LiquidCrystal_I2C.h>
    LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);//define DHT sensor pin
    dht DHT;
    #define DHT11_PIN 11
    //define joystick pin
    #define xPin A1
    #define yPin A2
    #define kPin 12
    
    //Assign thermistor to pin
    int ThermistorPin = 0;
    int Vo;
    float R1 = 10000;
    float logR2, R2, WaterTemp, AirTemp;
    float c1 = 1.009249522e-03, c2 = 2.378405444e-04, c3 = 2.019202697e-07;
    int tCount1;
    bool refresh;//lcd clear On/Off
    unsigned long readTime;
    
    //ReadJoystick
    int joyRead;
    int joyPos;
    int lastJoyPos;
    int joyButtonPushed;
    long lastDebounceTime = 0;
    long debounceDelay = 70; //user define
    int pushedTime = 0;
    //---------Storage states of buttons for debounce function-----//
    boolean current_sel = LOW;
    boolean last_sel = LOW;
    //-------ALT button Pins-----//
    int up = 4; //Up/Yes button
    int sel = 4; //Select button
    int down = 4; //Down/No button
    
    void setup() {
    //assign Joystick pins as output signal
    pinMode(xPin, INPUT);
    pinMode(yPin, INPUT);
    pinMode(kPin, INPUT_PULLUP);
    // put your setup code here, to run once:
    lcd.begin(20,4);
    lcd.backlight(); // finish with backlight on
    lcd.setCursor(4,1);
    lcd.print("Bantam.Earth");
    delay(3000);
    lcd.clear();
    readTime = 0;
    }
    
    void loop() {
    // put your main code here, to run repeatedly:
    if(millis() > readTime+1500){
    updatestatistics();
    }
    readJoystick();
    }
    
    void updatestatistics(){
    readTime = millis();
    //read air temperature & humidity
    int chk = DHT.read11(DHT11_PIN);
    AirTemp = DHT.temperature;
    AirTemp *= 1.8;
    AirTemp += 30;
    //read water temperature
    Vo = analogRead(ThermistorPin);
    R2 = R1 * (1023.0 / (float)Vo - 1.0);
    logR2 = log(R2);
    WaterTemp = (1.0 / (c1 + c2*logR2 + c3*logR2*logR2*logR2));
    WaterTemp = WaterTemp - 273.15;
    WaterTemp = (WaterTemp * 9.0)/ 5.0 + 32.0;
    writestatistics();
    }
    
    void writestatistics(){
    //write air temperature to LCD
    lcd.setCursor(1,1);
    lcd.print("Temp: A:");
    lcd.print(AirTemp, 0);
    lcd.print((char)223);
    lcd.print(" W:");
    // write water temperature to LCD
    lcd.print(WaterTemp, 0);
    lcd.print((char)223);
    //lcd.print("F");
    // write humidity to LCD
    lcd.setCursor(4,2);
    lcd.print("Humidity ");
    lcd.print(DHT.humidity, 0);
    lcd.print("%");
    }
    
    int readJoystick(){
    int x = analogRead(xPin);
    int y = analogRead(yPin);
    int k = digitalRead(kPin);
    if(x>600){joyRead=1; //x+
    }else if(x<100){joyRead=2; //x-
    }else if(y>600){joyRead=3; //y+
    }else if(y<100){joyRead=4; //y-
    }else if(!k){joyRead=5;
    }else{joyRead=0;}
    
    if (joyRead != lastJoyPos){lastDebounceTime = millis();}
    if(((millis() - lastDebounceTime) > debounceDelay)&&(joyRead!=joyPos)){
    joyPos=joyRead;
    }
    
    if (joyPos != lastJoyPos && pushedTime > 3){
    joyButtonPushed =joyPos;
    }
    if (joyRead != 0){
    pushedTime++;
    }
    if (pushedTime >= 50){
    pushedTime = 0;
    }
    lastJoyPos=joyRead;
    current_sel = debounce(last_sel, sel); //Debounce for Select button
    }
    
    boolean debounce(boolean last, int pin){
    boolean current = digitalRead(pin);
    if (last != current){
    delay(5);
    current = digitalRead(pin);
    }
    return current;
    }
  8. We will now have to update our code with menu functionality before being able to test the joystick out.
  9. Creating The Menu Functionality

    Things are getting pretty serious now. We almost have the ground work laid out for a fully functioning terrarium controller!

  1. Let’s define some new variables before we start, add this code at the top with the other variables:
    //Counters to change positions of pages and sub-menus
    int pages=7 ;       //number of pages
    int page_counter=1 ;       //To move between pages
    int subpage_counter=0;    //To move submenu 1 
    int subpage2_counter=0;   //To move submenu 2
    
    //Bluetooth
    //byte dataFromBT;
    //byte State;
    int FanState = 0;
    int FogState = 0;
    int RainState = 0;
    int SunState = 0;
    int BaskState = 0;
    int SkyState = 0;
  2. we need to create two custom symbols that will be used for the “back” button and the “select” icon, lets add those variables as well with this code:
    //Custom return char
    byte back[8] = {
      0b00100,
      0b01000,
      0b11111,
      0b01001,
      0b00101,
      0b00001,
      0b00001,
      0b11111
    };
    //Custom arrow char
    byte arrow[8] = {
      0b01000,
      0b00100,
      0b00010,
      0b11111,
      0b00010,
      0b00100,
      0b01000,
      0b00000
    };
  3. Now that we’ve define the custom characters, we need to assign them properly in the “setup” void with this code:
    lcd.createChar(1, back);
      lcd.createChar(2, arrow);
  4. Next we are going to create a void for the menu system and another one for navigation functionality so that the joystick properly controls the menu system.  Here is the code for those two functions:
    void homeScreen(){
      if (refresh){lcd.clear();refresh=0;}
      writestatistics();
    }
    ---------------------------------------
    MENU OPTION ONE
    -----------------------------------------
    void fanControl(){
      //Static objects 
         lcd.setCursor(4,0); 
         lcd.print("Fan Control:");
    //----------------------------------------
    // page controls
    //----------------------------------------     
         last_sel=current_sel;                      //Save last state of select button
         
          if (last_sel== LOW && joyButtonPushed==1 || last_sel== LOW && joyButtonPushed==2){ //Up or Down
         if(subpage_counter <2){                    // subpage counter never higher than 2(total of items)
         subpage_counter ++;                         //subcounter to move beetwen submenu
         lcd.setCursor(15,3);
         lcd.write(byte(1));     //Return custom char 
         }
         else{                                       //If subpage higher than 2 (total of items) return to first item
          subpage_counter=1;
          lcd.setCursor(15,3);
         lcd.write(byte(1));     //Return custom char 
         }
          joyButtonPushed = 0; pushedTime = 0;  
         }
         // Sub menu counter control
         if (last_sel== LOW && joyButtonPushed==5){ //select button pressed
          if(subpage_counter <2){                    // subpage counter never higher than 4 (total of items)
         subpage_counter ++;                         //subcounter to move between submenu
         lcd.setCursor(15,3);
         lcd.write(byte(1));     //Return custom char 
         }
         else if(subpage_counter ==2){                                       //If subpage higher than 2 (total of items) return to first item
          subpage_counter=0;
          lcd.setCursor(14,3);
          lcd.print(" ");
          lcd.setCursor(15,3);
          lcd.print(" ");
         }
         joyButtonPushed = 0; pushedTime = 0;  
         }
         last_sel=current_sel;                      //Save last state of select button
    //----------------------------------------
    // item control 1
    //----------------------------------------  
         
         //First item control(subpage_counter =1) Fan Power
         if(subpage_counter==1){        
         lcd.setCursor(14,3);
         lcd.print(" ");                           //Delete last arrow position 
         lcd.setCursor(7,1);
         lcd.write(byte(2));
         
         if (last_sel== LOW && joyButtonPushed==3){  //Right, Fan on
         relay8_state=LOW;
         joyButtonPushed = 0; pushedTime = 0; 
         }
         last_sel=current_sel;
         if(last_sel== LOW && joyButtonPushed==4){//Left, Fan off
         relay8_state=HIGH;
          joyButtonPushed = 0; pushedTime = 0;
         }
         last_sel=current_sel;
         }
    
         if(relay8_state==HIGH){
         lcd.setCursor(8,1);
         lcd.print("OFF");
         if(FanState !=0){
          FanState = 0;
        }  
         }
         else if(relay8_state==LOW){                    
         lcd.setCursor(8,1);
         lcd.print("ON ");
         if(FanState !=1){
          FanState = 1;
        } 
         }
    //----------------------------------------
    // BACK controls
    //----------------------------------------  
         //Second item control (subpage_counter=2) back
         if(subpage_counter==2){
         lcd.setCursor(7,1);
         lcd.print(" ");                           //Delete last arrow position                    
         lcd.setCursor(14,3);                      //Place the arrow
         lcd.write(byte(2));                      
         }
    }
    -------------------------------------------------
    MENU OPTION TWO
    -----------------------------------------------
    void fogControl(){
      //Static objects 
         lcd.setCursor(4,0); 
         lcd.print("Fog Control:");
    //----------------------------------------
    // page controls
    //----------------------------------------     
         last_sel=current_sel;                      //Save last state of select button
         
          if (last_sel== LOW && joyButtonPushed==1 || last_sel== LOW && joyButtonPushed==2){ //Up or Down
         if(subpage_counter <2){                    // subpage counter never higher than 2(total of items)
         subpage_counter ++;                         //subcounter to move beetwen submenu
         lcd.setCursor(15,3);
         lcd.write(byte(1));     //Return custom char 
         }
         else{                                       //If subpage higher than 2 (total of items) return to first item
          subpage_counter=1;
          lcd.setCursor(15,3);
         lcd.write(byte(1));     //Return custom char 
         }
          joyButtonPushed = 0; pushedTime = 0;  
         }
         // Sub menu counter control
         if (last_sel== LOW && joyButtonPushed==5){ //select button pressed
          if(subpage_counter <2){                    // subpage counter never higher than 4 (total of items)
         subpage_counter ++;                         //subcounter to move between submenu
         lcd.setCursor(15,3);
         lcd.write(byte(1));     //Return custom char 
         }
         else if(subpage_counter ==2){                                       //If subpage higher than 2 (total of items) return to first item
          subpage_counter=0;
          lcd.setCursor(14,3);
          lcd.print(" ");
          lcd.setCursor(15,3);
          lcd.print(" ");
         }
         joyButtonPushed = 0; pushedTime = 0; 
         }
         last_sel=current_sel;                      //Save last state of select button
    //----------------------------------------
    // item control 1
    //----------------------------------------  
         
         //First item control(subpage_counter =1) Fog Power
         if(subpage_counter==1){        
         lcd.setCursor(14,3);
         lcd.print(" ");                           //Delete last arrow position 
         lcd.setCursor(7,1);
         lcd.write(byte(2));
         
         if (last_sel== LOW && joyButtonPushed==3){  //Right, Fog on
         relay2_state=LOW;
         joyButtonPushed = 0; pushedTime = 0; 
         }
         last_sel=current_sel;
         if(last_sel== LOW && joyButtonPushed==4){//Left, Fog off
         relay2_state=HIGH;
          joyButtonPushed = 0; pushedTime = 0; 
         }
         last_sel=current_sel;
         }
    
         if(relay2_state==HIGH){
         lcd.setCursor(8,1);
         lcd.print("OFF"); 
         if(FogState !=0){
          FogState = 0;
        } 
         }
         else if(relay2_state==LOW){                    
         lcd.setCursor(8,1);
         lcd.print("ON ");
         if(FogState !=1){
          FogState = 1;
        } 
         }
    //----------------------------------------
    // BACK controls
    //----------------------------------------  
         //Second item control (subpage_counter=2) back
         if(subpage_counter==2){
         lcd.setCursor(7,1);
         lcd.print(" ");                           //Delete last arrow position                    
         lcd.setCursor(14,3);                      //Place the arrow
         lcd.write(byte(2));                      
         }
    }
    -----------------------------------------------
     MENU OPTION THREE
    -----------------------------------------------
    void rainControl(){
      //Static objects 
         lcd.setCursor(4,0); 
         lcd.print("Rain Control:");
    //----------------------------------------
    // page controls
    //----------------------------------------     
         last_sel=current_sel;                      //Save last state of select button
         
          if (last_sel== LOW && joyButtonPushed==1 || last_sel== LOW && joyButtonPushed==2){ //Up or Down
         if(subpage_counter <2){                    // subpage counter never higher than 2(total of items)
         subpage_counter ++;                         //subcounter to move beetwen submenu
         lcd.setCursor(15,3);
         lcd.write(byte(1));     //Return custom char 
         }
         else{                                       //If subpage higher than 2 (total of items) return to first item
          subpage_counter=1;
          lcd.setCursor(15,3);
         lcd.write(byte(1));     //Return custom char 
         }
          joyButtonPushed = 0; pushedTime = 0;  
         }
         // Sub menu counter control
         if (last_sel== LOW && joyButtonPushed==5){ //select button pressed
          if(subpage_counter <2){                    // subpage counter never higher than 4 (total of items)
         subpage_counter ++;                         //subcounter to move between submenu
         lcd.setCursor(15,3);
         lcd.write(byte(1));     //Return custom char 
         }
         else if(subpage_counter ==2){                                       //If subpage higher than 2 (total of items) return to first item
          subpage_counter=0;
          lcd.setCursor(14,3);
          lcd.print(" ");
          lcd.setCursor(15,3);
          lcd.print(" ");
         }
         joyButtonPushed = 0; pushedTime = 0;  
         }
         last_sel=current_sel;                      //Save last state of select button
    //----------------------------------------
    // item control 1
    //----------------------------------------  
         
         //First item control(subpage_counter =1) Fog Power
         if(subpage_counter==1){        
         lcd.setCursor(14,3);
         lcd.print(" ");                           //Delete last arrow position 
         lcd.setCursor(7,1);
         lcd.write(byte(2));
         
         if (last_sel== LOW && joyButtonPushed==3){  //Right, Rain on
         relay3_state=LOW;
         joyButtonPushed = 0; pushedTime = 0; 
         }
         last_sel=current_sel;
         if(last_sel== LOW && joyButtonPushed==4){//Left, Rain off
         relay3_state=HIGH;
          joyButtonPushed = 0; pushedTime = 0; 
         }
         last_sel=current_sel;
         }
    
         if(relay3_state==HIGH){
         lcd.setCursor(8,1);
         lcd.print("OFF"); 
         if(RainState !=0){
          RainState = 0;
        } 
         }
         else if(relay3_state==LOW){                    
         lcd.setCursor(8,1);
         lcd.print("ON ");
         if(RainState !=1){
          RainState = 1;
        } 
         }
    //----------------------------------------
    // BACK controls
    //----------------------------------------  
         //Second item control (subpage_counter=2) back
         if(subpage_counter==2){
         lcd.setCursor(7,1);
         lcd.print(" ");                           //Delete last arrow position                    
         lcd.setCursor(14,3);                      //Place the arrow
         lcd.write(byte(2));                      
         }
    }
    ---------------------------------
        MENU OPTION FOUR
    ----------------------------------
    void sunControl(){
      //Static objects 
         lcd.setCursor(4,0); 
         lcd.print("Sun Control:");
    //----------------------------------------
    // page controls
    //----------------------------------------     
         last_sel=current_sel;                      //Save last state of select button
         
          if (last_sel== LOW && joyButtonPushed==1 || last_sel== LOW && joyButtonPushed==2){ //Up or Down
         if(subpage_counter <2){                    // subpage counter never higher than 2(total of items)
         subpage_counter ++;                         //subcounter to move beetwen submenu
         lcd.setCursor(15,3);
         lcd.write(byte(1));     //Return custom char 
         }
         else{                                       //If subpage higher than 2 (total of items) return to first item
          subpage_counter=1;
          lcd.setCursor(15,3);
         lcd.write(byte(1));     //Return custom char 
         }
          joyButtonPushed = 0; pushedTime = 0;  
         }
         // Sub menu counter control
         if (last_sel== LOW && joyButtonPushed==5){ //select button pressed
          if(subpage_counter <2){                    // subpage counter never higher than 4 (total of items)
         subpage_counter ++;                         //subcounter to move between submenu
         lcd.setCursor(15,3);
         lcd.write(byte(1));     //Return custom char 
         }
         else if(subpage_counter ==2){                                       //If subpage higher than 2 (total of items) return to first item
          subpage_counter=0;
          lcd.setCursor(14,3);
          lcd.print(" ");
          lcd.setCursor(15,3);
          lcd.print(" ");
         }
         joyButtonPushed = 0; pushedTime = 0;  
         }
         last_sel=current_sel;                      //Save last state of select button
    //----------------------------------------
    // item control 1
    //----------------------------------------  
         
         //First item control(subpage_counter =1) Sun Power
         if(subpage_counter==1){        
         lcd.setCursor(14,3);
         lcd.print(" ");                           //Delete last arrow position 
         lcd.setCursor(7,1);
         lcd.write(byte(2));
         
         if (last_sel== LOW && joyButtonPushed==3){  //Right, Sun on
         relay4_state=LOW;
         joyButtonPushed = 0; pushedTime = 0; 
         }
         last_sel=current_sel;
         if(last_sel== LOW && joyButtonPushed==4){//Left, Sun off
         relay4_state=HIGH;
          joyButtonPushed = 0; pushedTime = 0; 
         }
         last_sel=current_sel;
         }
    
         if(relay4_state==HIGH){
         lcd.setCursor(8,1);
         lcd.print("OFF");
          if(SunState !=0){
          SunState = 0;
        } 
         }
         else if(relay4_state==LOW){                    
         lcd.setCursor(8,1);
         lcd.print("ON ");
         if(SunState !=1){
          SunState = 1;
        } 
         }
    //----------------------------------------
    // BACK controls
    //----------------------------------------  
         //Second item control (subpage_counter=2) back
         if(subpage_counter==2){
         lcd.setCursor(7,1);
         lcd.print(" ");                           //Delete last arrow position                    
         lcd.setCursor(14,3);                      //Place the arrow
         lcd.write(byte(2));                      
         }
    }
    ------------------------------------------------------------
     MENU OPTION FIVE
    -----------------------------------------------------------
    void BaskingControl(){
      //Static objects 
         lcd.setCursor(4,0); 
         lcd.print("Bask Control:");
    //----------------------------------------
    // page controls
    //----------------------------------------     
         last_sel=current_sel;                      //Save last state of select button
         
          if (last_sel== LOW && joyButtonPushed==1 || last_sel== LOW && joyButtonPushed==2){ //Up or Down
         if(subpage_counter <2){                    // subpage counter never higher than 2(total of items)
         subpage_counter ++;                         //subcounter to move beetwen submenu
         lcd.setCursor(15,3);
         lcd.write(byte(1));     //Return custom char 
         }
         else{                                       //If subpage higher than 2 (total of items) return to first item
          subpage_counter=1;
          lcd.setCursor(15,3);
         lcd.write(byte(1));     //Return custom char 
         }
          joyButtonPushed = 0; pushedTime = 0;  
         }
         // Sub menu counter control
         if (last_sel== LOW && joyButtonPushed==5){ //select button pressed
          if(subpage_counter <2){                    // subpage counter never higher than 4 (total of items)
         subpage_counter ++;                         //subcounter to move between submenu
         lcd.setCursor(15,3);
         lcd.write(byte(1));     //Return custom char 
         }
         else if(subpage_counter ==2){                                       //If subpage higher than 2 (total of items) return to first item
          subpage_counter=0;
          lcd.setCursor(14,3);
          lcd.print(" ");
          lcd.setCursor(15,3);
          lcd.print(" ");
         }
         joyButtonPushed = 0; pushedTime = 0;  
         }
         last_sel=current_sel;                      //Save last state of select button
    //----------------------------------------
    // item control 1
    //----------------------------------------  
         
         //First item control(subpage_counter =1) Basking Power
         if(subpage_counter==1){        
         lcd.setCursor(14,3);
         lcd.print(" ");                           //Delete last arrow position 
         lcd.setCursor(7,1);
         lcd.write(byte(2));
         
         if (last_sel== LOW && joyButtonPushed==3){  //Right, Basking on
         relay5_state=LOW;
         joyButtonPushed = 0; pushedTime = 0;
         }
         last_sel=current_sel;
         if(last_sel== LOW && joyButtonPushed==4){//Left, Basking off
         relay5_state=HIGH;
          joyButtonPushed = 0; pushedTime = 0; 
         }
         last_sel=current_sel;
         }
    
         if(relay5_state==HIGH){
         lcd.setCursor(8,1);
         lcd.print("OFF");
          if(BaskState !=0){
          BaskState = 0;
        } 
         }
         else if(relay5_state==LOW){                    
         lcd.setCursor(8,1);
         lcd.print("ON ");
          if(BaskState !=1){
          BaskState = 1;
        }
         }
    //----------------------------------------
    // BACK controls
    //----------------------------------------  
         //Second item control (subpage_counter=2) back
         if(subpage_counter==2){
         lcd.setCursor(7,1);
         lcd.print(" ");                           //Delete last arrow position                    
         lcd.setCursor(14,3);                      //Place the arrow
         lcd.write(byte(2));                      
         }
    }
    ------------------------------------------------------
      MENU OPTION SIX
    -------------------------------------------------------
    void skyControl(){
      //Static objects 
         lcd.setCursor(4,0); 
         lcd.print("Sky Control:");
         
    //----------------------------------------
    // page controls
    //----------------------------------------     
         last_sel=current_sel;                      //Save last state of select button
         
          if (last_sel== LOW && joyButtonPushed==2){ //Down
         if(subpage_counter ==1 && relay6_state==HIGH || subpage_counter ==2 && relay6_state==LOW){                    // subpage counter never higher than 2(total of items)
         subpage_counter=3;                         //subcounter to move beetwen submenu
         lcd.setCursor(15,3);
         lcd.write(byte(1));     //Return custom char 
         }
          else if(subpage_counter ==1 && relay6_state==LOW){                    // subpage counter never higher than 2(total of items)
         subpage_counter =2;                         //subcounter to move beetwen submenu
         lcd.setCursor(15,3);
         lcd.write(byte(1));     //Return custom char 
         }
         else{                                       //If subpage higher than 2 (total of items) return to first item
          subpage_counter=1;
          lcd.setCursor(15,3);
          lcd.write(byte(1));     //Return custom char 
         }
          joyButtonPushed = 0; pushedTime = 0;  
         }
         
          else if (last_sel== LOW && joyButtonPushed==1){ //Up
         if(subpage_counter ==1 && relay6_state==HIGH || subpage_counter ==1 && relay6_state==LOW){                    // subpage counter never higher than 2(total of items)
         subpage_counter=3;                         //subcounter to move beetwen submenu
         lcd.setCursor(15,3);
         lcd.write(byte(1));     //Return custom char 
         }
         else if(subpage_counter ==2){                                       //If subpage higher than 2 (total of items) return to first item
          subpage_counter=1;
          lcd.setCursor(15,3);
          lcd.write(byte(1));     //Return custom char 
         }
          else if(subpage_counter ==3 && relay6_state==LOW){                    // subpage counter never higher than 2(total of items)
         subpage_counter =2;                         //subcounter to move beetwen submenu
         lcd.setCursor(15,3);
         lcd.write(byte(1));     //Return custom char 
         }
          else if(subpage_counter ==3 && relay6_state==HIGH){                    // subpage counter never higher than 2(total of items)
         subpage_counter =1;                         //subcounter to move beetwen submenu
         lcd.setCursor(15,3);
         lcd.write(byte(1));     //Return custom char 
         }
          joyButtonPushed = 0; pushedTime = 0;  
         }
         // Sub menu counter control
    
         last_sel=current_sel;                      //Save last state of select button
         if (last_sel== LOW && joyButtonPushed==5){ //select button pressed
          if(subpage_counter ==0){                    // subpage counter never higher than 4 (total of items)
         subpage_counter =1;                         //subcounter to move between submenu
         lcd.setCursor(15,3);
         lcd.write(byte(1));     //Return custom char 
         }
          else if(subpage_counter ==1 && relay6_state==HIGH || subpage_counter ==2){                    // subpage counter never higher than 4 (total of items)
         subpage_counter =3;                         //subcounter to move between submenu
         lcd.setCursor(15,3);
         lcd.write(byte(1));     //Return custom char 
         }
         else if(subpage_counter ==1 && relay6_state==LOW){                    // subpage counter never higher than 4 (total of items)
         subpage_counter =2;                         //subcounter to move between submenu
         lcd.setCursor(15,3);
         lcd.write(byte(1));     //Return custom char 
         }
         else if(subpage_counter ==3){                                       //If subpage higher than 2 (total of items) return to first item
          subpage_counter=0;
          lcd.setCursor(14,3);
          lcd.print(" ");
          lcd.setCursor(15,3);
          lcd.print(" ");
         }
         joyButtonPushed = 0; pushedTime = 0;   
         }
    //----------------------------------------
    // item control 1
    //----------------------------------------  
         
         //First item control(subpage_counter =1) Sky Power
         if(subpage_counter==1){        
           lcd.setCursor(14,3);
           lcd.print(" ");                           //Delete last arrow position
           lcd.setCursor(4,2);
           lcd.print(" ");    
           lcd.setCursor(7,1);
           lcd.write(byte(2));
         
         if (last_sel== LOW && joyButtonPushed==3){  //Right, Sky on
         relay6_state=LOW;
         subpage2_counter =1;
         joyButtonPushed = 0; pushedTime = 0;
         }
         last_sel=current_sel;
         if(last_sel== LOW && joyButtonPushed==4){//Left, Sky off
         relay6_state=HIGH;
         subpage2_counter =0;
          joyButtonPushed = 0; pushedTime = 0; 
         }
         last_sel=current_sel;
         }
    
         if(relay6_state==HIGH){
         lcd.setCursor(8,1);
         lcd.print("OFF");
        // SkyMenu();
          }
         
         else if(relay6_state==LOW){                    
         lcd.setCursor(8,1);
         lcd.print("ON ");
       //  SkyMenu();
         }
    //----------------------------------------
    // item control 2
    //----------------------------------------  
         
         //Second item control(subpage_counter =2) Sky Setting
         if(subpage_counter==2 && relay6_state==LOW){        
         lcd.setCursor(7,1);
         lcd.print(" ");                           //Delete last arrow position 
         lcd.setCursor(14,3);
         lcd.print(" ");                           //Delete last arrow position 
         lcd.setCursor(4,2);
         lcd.write(byte(2));
         
         if (last_sel== LOW && joyButtonPushed==3){  //Right, Sky on
         if(subpage2_counter <5){ subpage2_counter ++; } else if(subpage2_counter ==5){ subpage2_counter =1; } joyButtonPushed = 0; pushedTime = 0;  } last_sel=current_sel; if(last_sel== LOW && joyButtonPushed==4){//Left, Sky off if(subpage2_counter >1){
           subpage2_counter --;
          }
          else if(subpage2_counter ==1){
            subpage2_counter =5;
            }
          joyButtonPushed = 0; pushedTime = 0;
         }
         last_sel=current_sel;
         }
      
    //----------------------------------------
    // BACK controls
    //----------------------------------------  
         //Second item control (subpage_counter=2) back
         if(subpage_counter==3){
         lcd.setCursor(7,1);
         lcd.print(" ");                           //Delete last arrow position 
         lcd.setCursor(4,2);
         lcd.print(" ");                   
         lcd.setCursor(14,3);                      //Place the arrow
         lcd.write(byte(2));                      
         }
    }
    
    void navigationFunctioner(){
    //----Page counter function to move pages----//
    
    if(subpage_counter==0){ // right/left buttons enabled if subcounters are 0,Disabled if 1,2..etc to work on submenus
    
    //Page Right
    if (last_sel== LOW && joyButtonPushed==3){ //Right button pressed
    lcd.clear(); //Clear lcd if page is changed to print new one
    if(page_counter <pages){ //Page counter never higher than 3(total of pages)
    page_counter= page_counter +1; //Page up
    
    }
    else{
    page_counter= 2; //If higher than 3 (last page) stay on page 3(change to 1 if you want to rotate)
    }
    current_sel = HIGH;
    joyButtonPushed = 0; pushedTime = 0;  
    }
    
    
    last_sel = current_sel; //Save up button last state 
    
    //Page Left
    if (last_sel== LOW && joyButtonPushed==4){//Left button pressed
    lcd.clear(); //Clear lcd if page is changed to print new one
    if(page_counter >2){ //Page counter never lower than 1 (total of pages)
    page_counter= page_counter -1; //Page down
    
    }
    else{
    page_counter= pages; //If lower than 1(first page) stay on page 1(change to 3 if you want to rotate)
    }
    current_sel = HIGH;
    joyButtonPushed = 0; pushedTime = 0; 
    
    }
    
    last_sel = current_sel; //Save down button last state
    }
    //Show sub counters status,delete or comment if not needed
    if(page_counter !=1){
    lcd.setCursor(0,0);
    lcd.print(page_counter-1);
    
    lcd.setCursor(1,0);
    lcd.print(".");
    }
    if(page_counter >9){
    lcd.setCursor(0,0);
    lcd.print(page_counter-1);
    
    lcd.setCursor(2,0);
    lcd.print(".");
    }
    }
  5. Let’s add these functions to the “loop” void so they work:
     navigationFunctioner();
      myMenu();
  6. With our menu system complete, we need to go back and remove code from the “updatestatistics” void. Remove this line of code:
    writestatistics();
  7. You might noticed I went ahead and left the relay variables in that menu. I didn’t want to confuse you later with where they went so lets go ahead and add the variables to the top so we don’t get any errors when we verify the code:
    #define RelayPin1 52
    #define RelayPin2 50
    #define RelayPin3 48
    #define RelayPin4 46
    #define RelayPin5 44
    #define RelayPin6 42
    #define RelayPin7 40
    #define RelayPin8 38
    
    //--------Relay states-----------//
    boolean relay1_state = HIGH;
    boolean relay2_state = HIGH;
    boolean relay3_state = HIGH;
    boolean relay4_state = HIGH;
    boolean relay5_state = HIGH;
    boolean relay6_state = HIGH;
    boolean relay7_state = HIGH;
    boolean relay8_state = HIGH;
  8. Uploading this to the Arduino board should now allow you to toggle between a series of options.

 

Auto “Back To Main Menu” Bonus Feature

 I wanted the display to automatically go back to the main menu after ten seconds of not touching the joystick.

  1. Adding this void to the end of the code will enable that:
    void autoGoBackToHomePage(){
      unsigned long currentMillis = millis();            //call current millis
         
      if (currentMillis - previousMillis > interval) {  //If interval is reached :
         lcd.clear();
         previousMillis = currentMillis;                   //replace previous millis by current millis as new start point
         subpage_counter=0;
         page_counter=1;                                    //Go back to home page
         joyButtonPushed = 0; pushedTime = 0;
         }       
      if (joyRead != 0){ // Reset millis counter If any button is pressed
          previousMillis = currentMillis;
          }
    }
  2. Don’t forget to define those new variables as well with this code somewhere at the top:
    //Variables for auto scroll
    unsigned long previousMillis = 0;
    unsigned long interval = 10000; //Desired wait time 10 seconds
  3. Lastly, we need to add this code to the “loop” void so that it can work:
     if(page_counter !=1){
        autoGoBackToHomePage();
        }
  4. Now no matter where you are in the menu, it will always go back to displaying the Temperature and humidity after a couple of seconds!

 

Constructing Relays To Control Power Outlets

With our menu set up, let’s send signal to the relays to power outlets!

  1. I decided to run a separate power supply for the relay module since it consumes so much energy when more than two channels are on. Adding the relay bar to the Mega2560 board should look like this:
  2. Once the relay module is installed, run the individual channels to a power outlet source. It will be as easy as plugging what ever selected devices you choose to those outlets. OR you can do like I did and splice into one of the two lines on a surge protector and seal it back.. Do that at your own risk though lol..
  3. We have already taken care of some of the relay coding in the menu, lets assign the relay pins as output signals in the “setup” void:
    //assign Relay pins as outgoing signal
      pinMode(RelayPin1, OUTPUT);
      pinMode(RelayPin2, OUTPUT);
      pinMode(RelayPin3, OUTPUT);
      pinMode(RelayPin4, OUTPUT);
      pinMode(RelayPin5, OUTPUT);
      pinMode(RelayPin6, OUTPUT);
      pinMode(RelayPin7, OUTPUT);
      pinMode(RelayPin8, OUTPUT);
  4. Lets add two more void functions. One will keep track of relay states and the other will reset all the relays whenever called:
    void relayStates(){  
      //------------Change Relay states---------//     
      if(relay1_state==HIGH && RelayPin1 != HIGH){
        digitalWrite(RelayPin1,HIGH);
      }
      if(relay1_state==LOW && RelayPin1 != LOW){
        digitalWrite(RelayPin1,LOW);
      }
      if(relay2_state==HIGH && RelayPin2 != HIGH){
        digitalWrite(RelayPin2,HIGH);
      }
      if(relay2_state==LOW && RelayPin2 != LOW){
        digitalWrite(RelayPin2,LOW);
      }
      if(relay3_state==HIGH && RelayPin3 != HIGH){
        digitalWrite(RelayPin3,HIGH);
      }
      if(relay3_state==LOW && RelayPin3 != LOW){
        digitalWrite(RelayPin3,LOW);
      }
      if(relay4_state==HIGH && RelayPin4 != HIGH){
        digitalWrite(RelayPin4,HIGH);
      }
      if(relay4_state==LOW && RelayPin4 != LOW){
        digitalWrite(RelayPin4,LOW);
      }
      if(relay5_state==HIGH && RelayPin5 != HIGH){
        digitalWrite(RelayPin5,HIGH);
      }
      if(relay5_state==LOW && RelayPin5 != LOW){
        digitalWrite(RelayPin5,LOW);
      }
      if(relay6_state==HIGH && RelayPin6 != HIGH){
        digitalWrite(RelayPin6,HIGH);
      }
      if(relay6_state==LOW && RelayPin6 != LOW){
        digitalWrite(RelayPin6,LOW);
      }
      if(relay7_state==HIGH && RelayPin7 != HIGH){
        digitalWrite(RelayPin7,HIGH);
      }
      if(relay7_state==LOW && RelayPin7 != LOW){
        digitalWrite(RelayPin7,LOW);
      }
      if(relay8_state==HIGH && RelayPin8 != HIGH){
        digitalWrite(RelayPin8,HIGH);
      }
      if(relay8_state==LOW && RelayPin8 != LOW){
        digitalWrite(RelayPin8,LOW);
      }
    }
    
    void resetallrelays(){
      digitalWrite(RelayPin1, HIGH);
      digitalWrite(RelayPin2, HIGH);
      digitalWrite(RelayPin3, HIGH);
      digitalWrite(RelayPin4, HIGH);
      digitalWrite(RelayPin5, HIGH);
      digitalWrite(RelayPin6, HIGH);
      digitalWrite(RelayPin7, HIGH);
      digitalWrite(RelayPin8, HIGH);
    }
  5. Add this line to the “setup” void so that the relays reset on start up:
     resetallrelays();
  6. Add this line of code to the “loop” and we can test:
    relayStates();

Conclusion

Now that we have the basic Arduino functionality complete, you might notice some of the more autonomous features aren’t there. Don’t worry, Will get to that mile stone soon.. Things like controlling the temperature wont be possible until we install the lighting. Controlling things like the humidity or automatic scheduling, are options that would be easier to manage through the app.. Which we will come to later as well!



  1. We can now proceed to the next chapter of this series, Lighting a Paludarium!

Related Post: