This 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 makes 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 microcontroller boards.
Terrarium Controllers
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 in 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:
Today, 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 The 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 fewer 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 the underwater 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 LEDs. 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 the 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 of understanding Arduino. We will start by displaying a simple message:
“Bantam.Earth”
- Start by connecting your LCD module shield to the Mega2560 like demonstrated in the diagram:
- 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: }
- You will notice you have two voids already written, a “setup” and a “loop”. The “setup” void is everything is the 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);
- Next, we will need to tell the LCD screen to display a message for three seconds… Afterward, 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(); }
- 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: }
- 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 the main menu screen that displays the current temperature and humidity of the tank. We will set up is code so that the intro message is displayed when the Arduino board is first powered on… After a few seconds, we get the main menu.
- Before updating the code for temperature and humidity monitoring, let’s add the sensors to the Mega2560 board first:
- We will add a new library so that the humidity sensor(DHT) is compatible on our board. Let’s add this line of code above the LCD library:
#include <dht.h>
- Next, we have to define the pin humidity Sensor that is placed on the Arduino board. Add this code somewhere under the libraries:
dht DHT; #define DHT11_PIN 11
- 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;
- With the variables established, we can now create two new voids. These voids will check the sensors every second and a half, then 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("%"); }
- 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(); } }
- Before I forget, Lets set the “readTime” variable to 0 in the “setup” void so it always starts from scratch:
readTime = 0;
- 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("%"); }
- When the board updates, You should see a welcome message and then 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.
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.
- Here’s a diagram of the joystick module connected to the Mega2560 board:
- 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
- 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);
- These are the variables that 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
- We will be creating two new functions similar to avoid. 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 are 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; }
- Lastly, we can add the joystick functionality to the “loop” void so it is always available:
readJoystick();
- 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; }
- We will now have to update our code with menu functionality before being able to test the joystick out.
Creating The Menu Functionality
Things are getting pretty serious now. We almost have the groundwork laid out for a fully functioning terrarium controller!
- 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;
- we need to create two custom symbols that will be used for the “back” button and the “select” icon, let’s 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 };
- Now that we’ve defined the custom characters, we need to assign them properly in the “setup” void with this code:
lcd.createChar(1, back); lcd.createChar(2, arrow);
- 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("."); } }
- Let’s add these functions to the “loop” void so they work:
navigationFunctioner(); myMenu();
- With our menu system complete, we need to go back and remove the code from the “updatestatistics” void. Remove this line of code:
writestatistics();
- You might notice I went ahead and left the relay variables in that menu. I didn’t want to confuse you later with where they went so let’s 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;
- 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.
- 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; } }
- 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
- Lastly, we need to add this code to the “loop” void so that it can work:
if(page_counter !=1){ autoGoBackToHomePage(); }
- 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 a signal to the relays to power outlets!
- 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:
- Once the relay module is installed, run the individual channels to a power outlet source. It will be as easy as plugging whatever 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…
- We have already taken care of some of the relay coding in the menu, let’s 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);
- Let’s 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); }
- Add this line to the “setup” void so that the relays reset on startup:
resetallrelays();
- 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, I Will get to that milestone soon.. Things like controlling the temperature won’t be possible until we install the lighting. Controlling things like humidity or automatic scheduling, are options that would be easier to manage through the app… Which we will come to later as well!