Maker and IoT Ideas
Published © CC BY-NC-SA

Automating a Mushroom house

This is a collaboration with a local High School to create a project with real-world applications, as well as teach the students new skills.

IntermediateWork in progress4 hours166
Automating a Mushroom house

Things used in this project

Hardware components

PCBWay Custom PCB
PCBWay Custom PCB
×1

Story

Read more

Schematics

Schematic - Page 1

Schematic - Page 2

Schematic - Page 3

Code

Initial Code

Arduino
This is the basic code to be used with the controller. It does not include code to control the temperature in the Mushroom house, which will be added later by a different group of students. The code is specific to the 5v peripheral version of the hardware, but can be used on the 3v version by changing a few of the conditional compiler options at the top of the file...
 
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_Sensor.h>
#include <DHT.h>
#include <DHT_U.h>


/*
 * Conditional Defines
 * 
 * These are switches to allow for different versions of the Smart Farm Controller
 * to be used with the same firware, as well as to enable/disable serial debugging
 * NOTE that deep sleep does NOT FUNCTION CORRECTLY when serial debugging is active.
 * THIS IS AN ISSUE WITH THE ESP32S3 AND NOT THE FIRMWARE.
 * 
 * 
*/

//#define DEBUG-SERIAL
// Enable below for the 3.3v version of the hardware, IF it has a lipo battery installed
//#define HAS_LIPO
// Enable only is NO lipo battery installed, DONT USE together with HAS_LIPO
#define MAINS_PWR
// ENABLE if the DHT11 Sensor's power is controlled with a transistor.
// This is standard on the 3.3v version, where power saving is important.
#define DHT_SW
// THIS IS THE TIME THAT THE "FOG" relay will allow water to be released
#define FOG_DELAY 10000
// TOP Value of the Humidity Window
#define FOG_HIGH 80
// BOTTOM  Value of the Humidity Window
#define FOG_LOW 70
// HOW LONG to Sleep During DEEP SLEEP MODE - 10 Minutes is Maximum, Value is in seconds
#define TIME_TO_SLEEP  600 
// HOW LONG TO STAY AWAKE (RUN) - Value is in seconds                            
#define TIME_AWAKE 300

/*
 * PLEASE NOTE THAT THE DEVICE SHALL NOT ENTER DEEP SLEEP IN THE EVENT THAT HUMIDITY LEVELS
 * HAVE DROPPED OUTSIDE OF THE ALLOWED WINDOW. 
 * NORMAL DEEP SLEEP SHALL RESUME WHEN THE HUMIDITY VALUE IS BACK IN THE CORRECT WINDOW
 */

/*
 * ===================================================================================================
 * ============ DO NOT EDIT BELOW THIS BLOCK - UNLESS YOU KNOW WHAT YOU ARE DOING ====================
 * ===================================================================================================
 */


// Variables and Constants
#define DHTPIN 9
#define DHTTYPE    DHT11     // DHT 11
#define Relay1 8
#define Relay2 7
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define OLED_RESET     -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
#ifdef DHT_SW
#define DHT_Pin 3 // THIS IS NOT THE DATA PIN, IT IS A CONTROL TRANSISTOR, PRESENT ONLY ON THE 3.3v Version of the controller
#endif
#define uS_TO_S_FACTOR 1000000ULL  /* Conversion factor for micro seconds to seconds */
const int ledPin = LED_BUILTIN;
int ledState = LOW;
unsigned long previousMillis = 0;
unsigned long previousFog = 0;
uint32_t delayMS;
float temperature = 0;
float humidity = 0;
#ifdef HAS_LIPO
  int rawBattVoltage = 0;
  float BattVoltage = 0.00;
#endif
bool MAY_FOG = 0;
bool RUN_FOG = 0;
bool DELAY_FOG = 0;
int FOG_LOOP = 0;
RTC_DATA_ATTR int LoopCount = 0;
bool MaySleep = 1;
bool SensorActive = 0;

/*
 * CLASS DEFINITIONS FOR EXTERNAL COMPONENTS
 */

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
DHT_Unified dht(DHTPIN, DHTTYPE);

/*
 * FUNCTIONS AND PROCEDURES
 */


void print_wakeup_reason(){
  #ifdef DEBUG-SERIAL
      esp_sleep_wakeup_cause_t wakeup_reason;
      wakeup_reason = esp_sleep_get_wakeup_cause();
      switch(wakeup_reason)
      {
        case ESP_SLEEP_WAKEUP_EXT0 : Serial.println("Wakeup caused by external signal using RTC_IO"); break;
        case ESP_SLEEP_WAKEUP_EXT1 : Serial.println("Wakeup caused by external signal using RTC_CNTL"); break;
        case ESP_SLEEP_WAKEUP_TIMER : Serial.println("Wakeup caused by timer"); break;
        case ESP_SLEEP_WAKEUP_TOUCHPAD : Serial.println("Wakeup caused by touchpad"); break;
        case ESP_SLEEP_WAKEUP_ULP : Serial.println("Wakeup caused by ULP program"); break;
        default : Serial.printf("Wakeup was not caused by deep sleep: %d\n",wakeup_reason); break;
      }    
  #endif
}

/*
 * SETUP FUNCTION - HARDWARE INITIALISATION
 */

void setup() {
   //Set relays to initialise as OFF (N/O)
  digitalWrite(Relay1,HIGH);
  digitalWrite(Relay2,HIGH);
  pinMode(Relay1,OUTPUT);
  pinMode(Relay2,OUTPUT);
  #ifdef DEBUG-SERIAL
    Serial.begin(115200);
  #endif
  LoopCount = 0;
  digitalWrite(ledPin,HIGH);
  pinMode(ledPin,OUTPUT);
  #ifdef DHT_SW
    digitalWrite(DHT_Pin,HIGH);
    pinMode(DHT_Pin,OUTPUT);
  #endif
  // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
  if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
    //Serial.println(F("SSD1306 allocation failed"));
    for(;;); // Don't proceed, loop forever
  }
  display.ssd1306_command(SSD1306_DISPLAYON);
  display.clearDisplay();
  display.display();
  delay(20);
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(10,10);
  display.print("Booting...");
  display.display();
  delay(2000);


  dht.begin();
  #ifdef DEBUG-SERIAL
   Serial.println(F("DHT11 Unified Sensor TEST"));
  #endif
  sensor_t sensor;
  dht.temperature().getSensor(&sensor);
  #ifdef DEBUG-SERIAL
    Serial.println(F("------------------------------------"));
    Serial.println(F("Temperature Sensor"));
    Serial.print  (F("Sensor Type: ")); Serial.println(sensor.name);
    Serial.print  (F("Driver Ver:  ")); Serial.println(sensor.version);
    Serial.print  (F("Unique ID:   ")); Serial.println(sensor.sensor_id);
    Serial.print  (F("Max Value:   ")); Serial.print(sensor.max_value); Serial.println(F("°C"));
    Serial.print  (F("Min Value:   ")); Serial.print(sensor.min_value); Serial.println(F("°C"));
    Serial.print  (F("Resolution:  ")); Serial.print(sensor.resolution); Serial.println(F("°C"));
    Serial.println(F("------------------------------------"));
  #endif
  display.clearDisplay();
  display.display();
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(10,1);
  display.print("Temperature Sensor");
  display.setCursor(10,10);
  display.print(F("Type: "));
  display.print(sensor.version);
  display.setCursor(10,20);
  display.print(F("Min: "));
  display.print(sensor.min_value);
  display.print(F(" Deg C"));
  display.setCursor(10,30);
  display.print(F("Max: "));
  display.print(sensor.max_value);
  display.print(F(" Deg C"));
  display.setCursor(10,40);
  display.print(F("Resolution: +/- "));
  display.setCursor(10,50);
  display.print(sensor.resolution);
  display.print(F(" Deg C"));
  display.display();
  delay(4000);
  // Print humidity sensor details.
  dht.humidity().getSensor(&sensor);
  #ifdef DEBUG-SERIAL
    Serial.println(F("Humidity Sensor"));
    Serial.print  (F("Sensor Type: ")); Serial.println(sensor.name);
    Serial.print  (F("Driver Ver:  ")); Serial.println(sensor.version);
    Serial.print  (F("Unique ID:   ")); Serial.println(sensor.sensor_id);
    Serial.print  (F("Max Value:   ")); Serial.print(sensor.max_value); Serial.println(F("%"));
    Serial.print  (F("Min Value:   ")); Serial.print(sensor.min_value); Serial.println(F("%"));
    Serial.print  (F("Resolution:  ")); Serial.print(sensor.resolution); Serial.println(F("%"));
    Serial.println(F("------------------------------------"));
  // Set delay between sensor readings based on sensor details.
  #endif
  display.clearDisplay();
  display.display();
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(10,1);
  display.print("Humidity Sensor");
  display.setCursor(10,10);
  display.print(F("Type: "));
  display.print(sensor.version);
  display.setCursor(10,20);
  display.print(F("Min: "));
  display.print(sensor.min_value);
  display.print(F(" % RH"));
  display.setCursor(10,30);
  display.print(F("Max: "));
  display.print(sensor.max_value);
  display.print(F(" % RH"));
  display.setCursor(10,40);
  display.print(F("Resolution: +/- "));
  display.setCursor(10,50);
  display.print(sensor.resolution);
  display.print(F(" % RH"));
  display.display();
  delay(4000);
  delayMS = sensor.min_delay / 1000;
  print_wakeup_reason();
  #ifdef HAS_LIPO
    rawBattVoltage = analogRead(A0);
    BattVoltage = ((rawBattVoltage * 2) * (3.34 / 4095.00));
    if (BattVoltage > 4.05) {
      esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
    } else if ((BattVoltage > 3.20) && (BattVoltage < 4.20)){
   
      esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
    } else {
     // Below 3.2 volt we consider the battary too low for operation.
     // We do not set a wakeup time for deepsleep.
     // This will cause the ESP32 to go to sleep and not wakeup until manual reset
    }
  #endif
  #ifdef MAINS_PWR
   esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
  #endif
}

/*
 * MAIN LOOP
 */

void loop() {
  #ifdef DHT_SW
    SensorActive = digitalRead(DHT_Pin);
  #else
    SensorActive = 1;
  #endif
  unsigned long currentMillis = millis();
  unsigned long FogMillis = millis();
  
  // 10 second "FOG" loop
  if (FogMillis - previousFog >= FOG_DELAY) {
    previousFog = FogMillis;
    if ((digitalRead(Relay1) == LOW) && (RUN_FOG == 1) && (DELAY_FOG == 0)) {
      digitalWrite(Relay1,HIGH);
      DELAY_FOG = 1;
      #ifdef DEBUG-SERIAL
        Serial.println(F("RELAY OFF AFTER 10 SEC"));
      #endif
    } else if (DELAY_FOG == 1) {
      DELAY_FOG = 0;
      if (RUN_FOG == 1) RUN_FOG = 0;
      #ifdef DEBUG-SERIAL
        Serial.println(F("RELAY DELAY OFF FOR 10 SEC"));
      #endif
    }  
  }
  
  // Main 1 second loop - Non blocking
  if (currentMillis - previousMillis >= delayMS) {
    // save the last time you blinked the LED
    previousMillis = currentMillis;
    ++LoopCount;
     if (ledState == LOW) {
      ledState = HIGH;
    } else {
      ledState = LOW;
    }
    // TEST RELAY2
    digitalWrite(Relay2,!digitalRead(Relay2));
    
    // set the LED with the ledState of the variable:
    digitalWrite(ledPin, ledState);
    #ifdef HAS_LIPO
      rawBattVoltage = analogRead(A0);
      BattVoltage = ((rawBattVoltage * 2) * (3.34 / 4095.00));
    #endif
    display.clearDisplay();
    display.setTextSize(2);
    display.setTextColor(SSD1306_WHITE);
    sensors_event_t event;
    dht.temperature().getEvent(&event);
    if (isnan(event.temperature)) {
      #ifdef DEBUG-SERIAL
        Serial.println(F("Error reading temperature!"));
      #endif
    }
    else {
      display.setCursor(10,1);
      display.setCursor(10,1);
      display.print(event.temperature);
      display.setCursor(80,1);
      display.println(F(" C"));
      temperature = event.temperature;
    }
    // Get humidity event and print its value.
    dht.humidity().getEvent(&event);
    if (isnan(event.relative_humidity)) {
      #ifdef DEBUG-SERIAL
        Serial.println(F("Error reading humidity!"));
      #endif
    }
    else {
       #ifdef DEBUG-SERIAL
        Serial.print(F("Humidity: "));
        Serial.print(event.relative_humidity);
        Serial.println(F("%"));
      #endif
      display.setCursor(10,20);
      display.print(event.relative_humidity);
      display.setCursor(80,20);
      display.println(F(" %"));
      humidity = event.relative_humidity;
    }
    #ifdef HAS_LIPO
      display.setTextSize(1);
      display.setCursor(10,40);
      display.print("LiPo ");
      display.print(String(BattVoltage));
      display.print("v ");
      if (BattVoltage >= 4.00)
      {
        display.println("Charging");
      } else if ((BattVoltage >= 3.30) and (BattVoltage <= 4.00)) {
        display.println("Batt PWR");
      } else {
        display.println("LOW BATT");
      }
    #endif
    
    #ifdef DEBUG-SERIAL
      Serial.println("=== TEST DATA ===");
      Serial.print("TEMP ");
      Serial.print(String(temperature));
      Serial.println(F("°C"));
      Serial.print("HUMIDITY ");
      Serial.print(String(humidity));
      Serial.println(F(" %"));
      #ifdef HAS_LIPO
        Serial.print("LiPo Battery Voltage : ");
        Serial.print(BattVoltage);
        Serial.println("v ");
        Serial.println();
      #endif
      Serial.print("Time to sleep :");
      Serial.print(String(TIME_AWAKE - LoopCount));
      Serial.println(" seconds");
      Serial.println("=== END ===");
    #endif
    
    display.setTextSize(1);
    if ((MaySleep == 1) && (RUN_FOG == 0)) {
      display.setCursor(10,50);
      display.print("DEEPSLEEP in ");
      display.print(String(TIME_AWAKE - LoopCount));
      display.print("sec ");
    } else if ((MaySleep == 0) && (RUN_FOG == 0)){
      display.setCursor(10,50);
      display.print("DISP OFF in ");
      display.print(String(TIME_AWAKE - LoopCount));
      display.print("sec ");
    }
    
  }
  
  display.display();
  if (LoopCount >= TIME_AWAKE)
  {
    display.clearDisplay();
    display.setTextSize(1);
    display.setTextColor(SSD1306_WHITE);
    if ((MaySleep == 1) && (RUN_FOG ==0)) {
      display.setCursor(10,10);
      display.print("Going to sleep...");
      display.display();
    } else {
      display.setCursor(10,10);
      display.print("Dimming Display...");
      display.display();
    }
    delay(2000);
    display.clearDisplay();
    display.display();
    #ifdef DEBUG-SERIAL
      Serial.flush();
    #endif
    #ifdef DHT_SW
      digitalWrite(DHT_Pin,LOW);
    #endif
    if (digitalRead(Relay1) == LOW) digitalWrite(Relay1,HIGH);
    if (digitalRead(Relay2) == LOW) digitalWrite(Relay2,HIGH);
    display.ssd1306_command(SSD1306_DISPLAYOFF);
    delay(2000);
     if ((MaySleep == 1) && (RUN_FOG ==0)){
     esp_deep_sleep_start();
    }
  }
  // we only care about readings if and when the sensor is active AND we have valid data...
  if (SensorActive == 1) { 
    if ((humidity < FOG_LOW) && (RUN_FOG == 0)) {
      RUN_FOG = 1;
      MAY_FOG = 1;
      #ifdef DEBUG-SERIAL
        Serial.println(F("HUMIDITY BELOW 70"));
      #endif
    } else if ((humidity >= FOG_LOW) && (humidity <= FOG_HIGH)) {
      RUN_FOG = 0;
      MAY_FOG = 0;
      if (digitalRead(Relay1) == LOW) digitalWrite(Relay1,HIGH);
      #ifdef DEBUG-SERIAL
        Serial.print("Humidity in Range ");
        Serial.print(String(FOG_LOW));
        Serial.print(" % to ");
        Serial.print(String(FOG_HIGH));
        Serial.println(" %");
      #endif
    }
 
  }
  if ((digitalRead(Relay1) == HIGH) && (RUN_FOG == 1) && DELAY_FOG == 0) digitalWrite(Relay1,LOW);
  if ((DELAY_FOG == 1) && (MAY_FOG ==1 )) digitalWrite(Relay1,HIGH);
  
}

Credits

Maker and IoT Ideas
97 projects • 25 followers
I design custom PCB solutions, usually with an IoT or Automation twist, to solve problems in my daily life. Sometimes also for other people.
Contact

Comments

Please log in or sign up to comment.