Automatic Terrarium

by Stabby_Death in Living > Gardening

592 Views, 1 Favorites, 0 Comments

Automatic Terrarium

PXL_20210620_171004748.jpg

This guide is to build a terrarium with integrated lights, temperature, and humidity sensors. It is also connected to WiFi so that it can always know when it is time to turn the lights on or off!

Supplies

Wiring Diagram

Terrarium_bb_update.jpg

Here is the basic wiring diagram. You could change up what buttons are active or not. The Neopixels are currently on pin 12. Buttons A and C are on pins that can be used as interrupts.

Setup Your Arduino IDE Libraries

You're going to need to download some libraries into you Arduino IDE:

Check out THIS link to the Adafruit Feather HUZZAH ESP8266 Arduino guide to get your IDE set up for the board.

And use THIS link to set up the Neopixels and install the proper library.

For the OLED screen, use THIS link.

You will also need the Adafruit HTU31D Library which you can search for in the Arduino Library Manager.

Code for the Microcontroller

#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SH110X.h>
#include "Adafruit_HTU31D.h"
#include <Adafruit_NeoPixel.h>

Adafruit_SH1107 display = Adafruit_SH1107(64, 128, &Wire);
//Hum + temp setup
Adafruit_HTU31D htu = Adafruit_HTU31D();

float fTemp = 0.0;
float fHumid = 0.0;

unsigned long epoch = 0;

volatile unsigned long timestamp;
unsigned long overflowTime;

volatile bool showDisp = true;

bool growOn = false;
bool rainbowOn = false;
bool rainbowStart = false;
unsigned long rainbowTime;

int  randNumber;

int led[10][4] = {{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}};
int BRIGHTNESS =  0;


// OLED FeatherWing buttons map to different pins depending on board:

#define BUTTON_A  0
#define BUTTON_B 16
#define BUTTON_C  2


#ifndef STASSID
#define STASSID "PUT_YOUR_WIFI_NAME_HERE"
#define STAPSK  "PUT_YOUR_WIFI_PASSWORD_HERE"
#endif

//****************************************************************************************
//Neopixel setup
#define LED_PIN     12
#define LED_COUNT  10

Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRBW + NEO_KHZ800);
//*****************************************************************************************


/* Don't hardwire the IP address or we won't get the benefits of the pool.
    Lookup the IP address for the host name instead */
const char * ssid = STASSID; // your network SSID (name)
const char * pass = STAPSK;  // your network password
unsigned int localPort = 2390;      // local port to listen for UDP packets
//IPAddress timeServer(129, 6, 15, 28); // time.nist.gov NTP server
IPAddress timeServerIP; // time.nist.gov NTP server address
const char* ntpServerName = "time.nist.gov";
const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message
byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets
// A UDP instance to let us send and receive packets over UDP
WiFiUDP udp;



// send an NTP request to the time server at the given address &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
void sendNTPpacket(IPAddress& address) {
  //Serial.println("sending NTP packet...")
  // set all bytes in the buffer to 0
  memset(packetBuffer, 0, NTP_PACKET_SIZE);
  // Initialize values needed to form NTP request
  // (see URL above for details on the packets)
  packetBuffer[0] = 0b11100011;   // LI, Version, Mode
  packetBuffer[1] = 0;     // Stratum, or type of clock
  packetBuffer[2] = 6;     // Polling Interval
  packetBuffer[3] = 0xEC;  // Peer Clock Precision
  // 8 bytes of zero for Root Delay & Root Dispersion
  packetBuffer[12]  = 49;
  packetBuffer[13]  = 0x4E;
  packetBuffer[14]  = 49;
  packetBuffer[15]  = 52;

  // all NTP fields have been given values, now
  // you can send a packet requesting a timestamp:
  udp.beginPacket(address, 123); //NTP requests are to port 123
  udp.write(packetBuffer, NTP_PACKET_SIZE);
  udp.endPacket();
}

//Time query function &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
void getTime(){
  int cb = 0;


    WiFi.hostByName(ntpServerName, timeServerIP);
    sendNTPpacket(timeServerIP); // send an NTP packet to a time server
    delay(1000);
    cb = udp.parsePacket();

    if(cb){
      udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer
  
      //the timestamp starts at byte 40 of the received packet and is four bytes,
      // or two words, long. First, esxtract the two words:
  
      unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
      unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
      // combine the four bytes (two words) into a long integer
      // this is NTP time (seconds since Jan 1 1900):
      unsigned long secsSince1900 = highWord << 16 | lowWord;
      //Serial.print("Seconds since Jan 1 1900 = ");
      //Serial.println(secsSince1900);
  
      // now convert NTP time into everyday time:
      //Serial.print("Unix time = ");
      // Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
      const unsigned long seventyYears = 2208988800UL;
      // subtract seventy years:
      //unsigned long epoch = secsSince1900 - seventyYears;
      epoch = secsSince1900 - seventyYears;
    }
    
}
//Used at initilization to make sure we have a time. Takes a few tries sometimes.
void mustGetTime(){
  int cb = 0;

    while(!cb){
    WiFi.hostByName(ntpServerName, timeServerIP);
    sendNTPpacket(timeServerIP); // send an NTP packet to a time server
    delay(1000);
    cb = udp.parsePacket();

    }

      udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer
  
      //the timestamp starts at byte 40 of the received packet and is four bytes,
      // or two words, long. First, esxtract the two words:
  
      unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
      unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
      // combine the four bytes (two words) into a long integer
      // this is NTP time (seconds since Jan 1 1900):
      unsigned long secsSince1900 = highWord << 16 | lowWord;
      //Serial.print("Seconds since Jan 1 1900 = ");
      //Serial.println(secsSince1900);
  
      // now convert NTP time into everyday time:
      //Serial.print("Unix time = ");
      // Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
      const unsigned long seventyYears = 2208988800UL;
      // subtract seventy years:
      //unsigned long epoch = secsSince1900 - seventyYears;
      epoch = secsSince1900 - seventyYears;
    
}

//Screen update &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
void refreshScreen(){
  display.clearDisplay();
  //display.display();
  display.setCursor(0,0);

  display.print(F("Temp: "));
  display.print(((fTemp * (9.0/5.0)) + 32.0), 0);
  display.println(F(" F"));

  display.print(F("Hum: "));
  display.print(fHumid, 0);
  display.println(F(" \%"));

  display.print(F("UT: "));
  //display.print(F("  "));

  display.print((epoch  % 86400L) / 3600);
  display.print(':');
    
  if (((epoch % 3600) / 60) < 10) {
    // In the first 10 minutes of each hour, we'll want a leading '0'
    display.print('0');
  }
  display.println((epoch  % 3600) / 60); // print the minute (3600 equals secs per minute)
  //display.print(':');
  //if ((epoch % 60) < 10) {
  //  // In the first 10 seconds of each minute, we'll want a leading '0'
  //  display.print('0');
  //}
  //display.println(epoch % 60);
  display.display();
}

//Update Temp and Humidity variables &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
void tempHumid(){
  sensors_event_t humidity, temp;
  htu.getEvent(&humidity, &temp);// populate temp and humidity objects with fresh data
  fTemp = temp.temperature;
  htu.enableHeater(true);

  bool dipState = showDisp;

//So as not to block too much while waiting on the heater to warm up
  for(int i=0; i<500; i++) {
    if (showDisp != dipState) {
      refreshScreen();
      timestamp = millis();}
    delay(10);
  }
  
  htu.getEvent(&humidity, &temp);// populate temp and humidity objects with fresh data
  htu.enableHeater(false);
  fHumid = humidity.relative_humidity;
}

//Initilization function &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
void setup() {


  Serial.begin(115200);
  //Start OLED screen
  display.begin(0x3C, true); // Address 0x3C default
  display.clearDisplay();
  display.display();
  display.setRotation(1);
  display.setTextSize(2);
  display.setTextColor(SH110X_WHITE);
  display.setCursor(0,0);
  display.print(F("Warming Up"));
  display.display(); // actually display all of the above

  //Interrupt setup
  pinMode(BUTTON_A, INPUT_PULLUP);
  pinMode(BUTTON_B, INPUT_PULLUP);
  pinMode(BUTTON_C, INPUT_PULLUP);
  
  attachInterrupt(digitalPinToInterrupt(BUTTON_C), displayOn, FALLING);
  attachInterrupt(digitalPinToInterrupt(BUTTON_A), rainbowInt, FALLING);


  //Startup temp/humid sensor
  htu.begin(0x40);

  //Start NEOpixels
  strip.begin();           // INITIALIZE NeoPixel strip object (REQUIRED)
  strip.show();            // Turn OFF all pixels ASAP
  strip.setBrightness(BRIGHTNESS);



  // Start WiFi network
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, pass);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
  }
  udp.begin(localPort);

  //Setup random function
  randomSeed(analogRead(A0));

  //First run of functions
  
  //Get the time
  mustGetTime();
  //Hum + temp get
  tempHumid();
  //Initalize screen timeout
  timestamp = millis();
  overflowTime = millis();
  

}

//Interrupt function to turn screen back on and restart timer &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
 IRAM_ATTR void displayOn(){
  //refreshScreen();
  timestamp = millis();
  //display.print(F("Interrupt!"));
  //display.display();
  //Serial.println("Interrupt!");
  showDisp = true;
}

IRAM_ATTR void rainbowInt(){
  rainbowOn = true;
}



//Grow light function &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
void grow(){
  //Random number of pixels to change (up to 5 each pass)
   randNumber = random(6);
   
  for(int i=0; i<randNumber; i++) { // For each pixel in strip...
    int startPixel = random(10);
    //int startPixel = 0;
    int fadeRange = random(80);
    int fadeDirection = random(3) -1;
    for(int j = 0; j<fadeRange; j++){
      led[startPixel][0] = led[startPixel][0] - (fadeDirection*3);
      if (led[startPixel][0] > 255){
        fadeDirection = -1*fadeDirection;
        led[startPixel][0] = led[startPixel][0] - (fadeDirection*3);
        break;}
      if (led[startPixel][0] < 50){
        fadeDirection = -1*fadeDirection;
        led[startPixel][0] = led[startPixel][0] - (fadeDirection*3);
        break;}
        
      led[startPixel][1] = led[startPixel][1] - (fadeDirection*3);
      if (led[startPixel][1] > 200){
        fadeDirection = -1*fadeDirection;
        led[startPixel][1] = led[startPixel][1] - (fadeDirection*3);
        break;}
      if (led[startPixel][1] < 20){
        fadeDirection = -1*fadeDirection;
        led[startPixel][1] = led[startPixel][1] - (fadeDirection*3);
        break;}
      
      led[startPixel][3] = led[startPixel][3] - (fadeDirection*3);
      if (led[startPixel][3] > 255){
        fadeDirection = -1*fadeDirection;
        led[startPixel][3] = led[startPixel][3] - (fadeDirection*3);
        break;}
      if (led[startPixel][3] < 20){
        fadeDirection = -1*fadeDirection;
        led[startPixel][3] = led[startPixel][3] - (fadeDirection*3);
        break;}

    strip.setPixelColor(startPixel, led[startPixel][0], led[startPixel][1], 0, led[startPixel][3]);
    strip.show();
    delay(10);
    }
    Serial.print("Grow ");
    Serial.print(i);
    Serial.print(" ");
    Serial.println(led[0][3]);
  }   
}

//Bring lights up to grow() level &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
void lightUp(){
  display.clearDisplay();
  //display.display();
  display.setCursor(0,0);
  display.print(F("G'morning!"));
  display.display();
  
  for(int i=0; i<255; i++) {
    BRIGHTNESS = BRIGHTNESS + 1;
    if (BRIGHTNESS > 128){
      BRIGHTNESS = 128;
    }
    strip.setBrightness(BRIGHTNESS);
    for(int j=0; j<10; j++){
      led[j][0] = led[j][0] + 1;
      led[j][1] = led[j][1] + 1;
      led[j][2] = led[j][2] - 1;
      led[j][3] = led[j][3] + 1;
      if (led[j][0] > 255){
        led[j][1] = 255;
      }
      if (led[j][1] > 120){
        led[j][1] = 120;
      }
      if (led[j][2] < 0){
        led[j][1] = 0;
      }
      if (led[j][3] > 255){
        led[j][3] = 255;
      }
      strip.setPixelColor(j, led[j][0], led[j][1], led[j][2], led[j][3]);
      
    }
    strip.show();
    delay(500);
  }
  getTime();
  timestamp = millis();
  refreshScreen();
}

// bring lights down &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
void lightDown(){
  display.clearDisplay();
  //display.display();
  display.setCursor(0,0);
  display.print(F("Goodnight!"));
  display.display();
  
  for(int i=0; i<255; i++) {
    BRIGHTNESS = BRIGHTNESS - 1;
    if (BRIGHTNESS < 0){
      BRIGHTNESS = 0;
    }
    strip.setBrightness(BRIGHTNESS);
    for(int j=0; j<10; j++){
      led[j][0] = led[j][0] - 1;
      led[j][1] = led[j][1] - 1;
      led[j][1] = led[j][2] - 1;
      led[j][3] = led[j][3] - 1;
      if (led[j][0] < 0){
        led[j][1] = 0;
      }
      if (led[j][1] < 0){
        led[j][1] = 0;
      }
      if (led[j][2] < 0){
        led[j][1] = 0;
      }
      if (led[j][3] < 0){
        led[j][3] = 0;
      }
      strip.setPixelColor(j, led[j][0], led[j][1], led[j][2], led[j][3]);
      
    }
    strip.show();
    delay(500);
  }
  display.clearDisplay();
  display.display();
}

//Rainbow function &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
void rainbow(){
  //init rainbow color values
  int rbow[10][3];
  uint16_t hue = random(6) *10922;
  for(int i = 0; i<10; i++){
    uint32_t rgbcolor = strip.ColorHSV(((hue + i*10922) % 65536), 255, 255);
    rbow[i][0] = (rgbcolor >> 16) & 0xFF;
    rbow[i][1] = (rgbcolor >> 8) & 0xFF;
    rbow[i][2] = rgbcolor & 0xFF;
  }
  
  for(int i=0; i<255; i++) {
    BRIGHTNESS = BRIGHTNESS + 1;
    if (BRIGHTNESS > 255){
      BRIGHTNESS = 255;
    }
    strip.setBrightness(BRIGHTNESS);

    for(int j=0; j<10; j++){
      if (led[j][0] != rbow[j][0]){
        if (led[j][0] < rbow[j][0]){
          led[j][0] = led[j][0] + 1;
        }else{
          led[j][0] = led[j][0] - 1;
        }
      }
      if (led[j][1] != rbow[j][1]){
        if (led[j][1] < rbow[j][1]){
          led[j][1] = led[j][1] + 1;
        }else{
          led[j][1] = led[j][1] - 1;
        }
      }
      if (led[j][2] != rbow[j][2]){
        if (led[j][2] < rbow[j][2]){
          led[j][2] = led[j][2] + 1;
        }else{
          led[j][2] = led[j][2] - 1;
        }
      }
      if (led[j][3] > 0){
        led[j][3] = led[j][3] - 1;
      }
      strip.setPixelColor(j, led[j][0], led[j][1], led[j][2], led[j][3]); 
    }
 
    strip.show();
    delay(2);
  }


}

//Bring lights up to rainbow random level &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
void rainbowUp(){

  //init rainbow color values
  int rbow[10][3];
  uint16_t hue = random(6) *10922;
  for(int i = 0; i<10; i++){
    uint32_t rgbcolor = strip.ColorHSV(((hue + i*10922) % 65536), 255, 255);
    rbow[i][0] = (rgbcolor >> 16) & 0xFF;
    rbow[i][1] = (rgbcolor >> 8) & 0xFF;
    rbow[i][2] = rgbcolor & 0xFF;
  }
  
  for(int i=0; i<255; i++) {
    BRIGHTNESS = BRIGHTNESS + 1;
    if (BRIGHTNESS > 255){
      BRIGHTNESS = 255;
    }
    strip.setBrightness(BRIGHTNESS);

    for(int j=0; j<10; j++){
      if (led[j][0] != rbow[j][0]){
        if (led[j][0] < rbow[j][0]){
          led[j][0] = led[j][0] + 1;
        }else{
          led[j][0] = led[j][0] - 1;
        }
      }
      if (led[j][1] != rbow[j][1]){
        if (led[j][1] < rbow[j][1]){
          led[j][1] = led[j][1] + 1;
        }else{
          led[j][1] = led[j][1] - 1;
        }
      }
      if (led[j][2] != rbow[j][2]){
        if (led[j][2] < rbow[j][2]){
          led[j][2] = led[j][2] + 1;
        }else{
          led[j][2] = led[j][2] - 1;
        }
      }
      if (led[j][3] > 0){
        led[j][3] = led[j][3] - 1;
      }
      strip.setPixelColor(j, led[j][0], led[j][1], led[j][2], led[j][3]); 
    }



    
    strip.show();
    delay(25);
  }
}
// bring rainbow lights down (or to grow() level) &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
void rainbowDown(){

  if (growOn){
    for(int i=0; i<255; i++) {
    BRIGHTNESS = BRIGHTNESS + 1;
    if (BRIGHTNESS > 128){
      BRIGHTNESS = 128;
    }
    strip.setBrightness(BRIGHTNESS);
    for(int j=0; j<10; j++){
      led[j][0] = led[j][0] + 1;
      if(led[j][1] > 120){
        led[j][1] = led[j][1] - 1;
      }else{
        led[j][1] = led[j][1] + 1;
      }
      led[j][2] = led[j][2] - 1;
      led[j][3] = led[j][3] + 1;
      if (led[j][0] > 255){
        led[j][1] = 255;
      }
//      if (led[j][1] > 120){
//        led[j][1] = 120;
//      }
      if (led[j][2] < 0){
        led[j][1] = 0;
      }
      if (led[j][3] > 255){
        led[j][3] = 255;
      }
      strip.setPixelColor(j, led[j][0], led[j][1], led[j][2], led[j][3]);
      
    }
    strip.show();
    delay(50);
    Serial.print("Rainbow down ");
    Serial.print(i);
    Serial.print(" ");
    Serial.println(led[0][3]);
  }
  }else{
  for(int i=0; i<255; i++) {
    BRIGHTNESS = BRIGHTNESS - 1;
    if (BRIGHTNESS < 0){
      BRIGHTNESS = 0;
    }
    strip.setBrightness(BRIGHTNESS);
    for(int j=0; j<10; j++){
      led[j][0] = led[j][0] - 1;
      led[j][1] = led[j][1] - 1;
      led[j][1] = led[j][2] - 1;
      led[j][3] = led[j][3] - 1;
      if (led[j][0] < 0){
        led[j][1] = 0;
      }
      if (led[j][1] < 0){
        led[j][1] = 0;
      }
      if (led[j][2] < 0){
        led[j][1] = 0;
      }
      if (led[j][3] < 0){
        led[j][3] = 0;
      }
      strip.setPixelColor(j, led[j][0], led[j][1], led[j][2], led[j][3]);
      
    }
    strip.show();
    delay(50);
  }
}
}


// Main repeated loop &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
void loop() {

  //Catch the millis overflow and fix timers
  if (overflowTime > millis()){
    timestamp = 0;
    rainbowTime = 0;
  }
  
  //Get the time
  getTime();
  //Hum + temp get
  tempHumid();

  //Turn off display after 1 minute
  if ((millis() - timestamp) > 60000) {
    showDisp = false;
  }
  if (showDisp) {
      refreshScreen();
  }else{
    display.clearDisplay();
    display.display();
  }

  //Set grow lights or rainbow light
  if (!rainbowOn){
  int hour = (epoch  % 86400L) / 3600;
  Serial.print("Hour: ");
  Serial.println(hour);

//HERE IS WHERE YOU ADJUST THE UT TIME FOR YOUR TIME ZOME. CURRENTLY IT IS SET TO ARIZONA TIME
  if ((hour > 16) || (hour < 2)){
    if(!growOn){
      lightUp();
    }
    growOn = true;
    grow();
  }else{
    Serial.println("In night loop");
    if(growOn){
      lightDown();
    }
    growOn = false;
  }
  //Run rainbow lights
  }else{
    //Lights up
    if (!rainbowStart){
      rainbowUp();
      rainbowTime = millis();
      rainbowStart = true;
    }
    //Normal run
    rainbow();

    //When timer runs out
    if ((millis() - rainbowTime) > 180000){
      rainbowDown();
      rainbowStart = false;
      rainbowOn = false;
    }
    
  }
  
  delay(1000);
  }

Editing the Code

Make sure to put in your WiFi info on these two lines:

#define STASSID "PUT_YOUR_WIFI_NAME_HERE"

#define STAPSK  "PUT_YOUR_WIFI_PASSWORD_HERE"

Also adjust the hours that the lights will be on in this line towards the bottom:

if ((hour > 16) || (hour < 2)){

And that's it! You should be ready to grow!