The greenhouse controller code

Since the code of this project is perhaps a little longer than the previous examples, I will present it first by sections without comments and include the complete code for reference at the end of this chapter.

Libraries and constant definitions

We begin our code by including the servo library that we will use to deal with the servomotor that will operate the retractable roof. We define constants that will be used all along the code as follows:

#include <Servo.h>

#define redLedPin 13
#define yellowLedPin 12
#define greenLedPin 11
#define relayPin 8
#define motorPin 6
#define buzzerPin 4
#define servoPin 3
#define buttonPin 2
#define potentiometerPin 2
#define thermistorPin 1
#define photocellPin 0
#define NOTE_A4 440
#define NOTE_A3 220
#define TEMPMIN 820
#define TEMPMAX 850
#define MEDIUMPOWER 128
#define FULLPOWER 255
#define BEGINWATERING 700
#define ENDWATERING 550
#define DAYLIGHT 400

Here is a breakdown of the 20 constants used in the preceding code:

  • The first 11 constants are simply to reference the pins that we will use to connect the different devices. They are all lowercase with every word's first letter in uppercase and they all finish in Pin.
  • From there on, there are some constants that will be used mainly to compare their values in conditional statements all along the code.
  • NOTE_A3 and NOTE_A4 define the frequency of the notes that will be produced in the acoustic alarm.
  • TEMPMAX and TEMPMIN represent the range of temperatures in which the controller can operate. You will have to adapt them to the readings your thermistor gives.
  • MEDIUMPOWER and FULLPOWER are the values at which the motor will spin in the medium and high temperatures.
  • BEGINWATERING and ENDWATERING are the values between which the watering will occur. We will activate the water pump when the soil humidity sensor or the potentiometer gives a value of 700 and end the watering when the humidity raises and makes the sensor lower its internal resistance to give a value of 550.
  • Finally, DAYLIGHT is the photocell value that indicates that the night is off and that we can open the retractable roof.

Global variables

There aren't really too many variables in this example, just those needed to read the analog sensors and three more:

volatile boolean buttonPressed = false;
int tempValue;
int humidityValue;
int lightValue;
Servo myServo;
int state;

Here is a breakdown of the variables used in the preceding code:

  • The volatile Boolean buttonPressed is, as you may have guessed, the one we will be using inside the Interrupt Service Routine (ISR) for the button press. It is simply a Boolean that we will use to flag up the button pressing.
  • tempValue, humidityValue, and lightValue are used, as I said before, to store the values read from every one of the analog sensors.
  • The myServo variable is an instance of the Servo class that will allow us to operate the servomotor.
  • Finally, state is a variable used in a switch control structure to activate each subsystem depending on its value. We will see it later in action when we study the main loop code.

The interrupt ISR

As mentioned before, the button pressing will be managed via an interrupt and thus an ISR has to be written. We saw in Chapter 9, Dealing with Interrupts, that the code inside the ISR should be as concise as possible avoiding big processing inside this critical function.

In our case, we simply set the buttonPressed Boolean variable to true, which will execute some code inside the main loop once this is detected.

The complete code of this function is as follows:

void buttonPress(){
  buttonPressed = true;
}

The alarm routine

We talked before about all the things that should be unchained once an alarm situation is detected.

Since this alarm situation may occur in more than one place in the code, I have decided to include all these actions in a function and call it whenever it is needed.

The alarmRoutine() function code is as follows:

void alarmRoutine(){
  myServo.write(0);
  digitalWrite(relayPin,HIGH);
  tone(buzzerPin,NOTE_A4);
  digitalWrite(greenLedPin,HIGH);
  digitalWrite(yellowLedPin,HIGH);
  digitalWrite(redLedPin,HIGH);
  delay(1000);
  tone(buzzerPin,NOTE_A3);
  digitalWrite(greenLedPin,LOW);
  digitalWrite(yellowLedPin,LOW);
  digitalWrite(redLedPin,LOW);
  delay(1000);
  noTone(buzzerPin);
}

As you may have understood at this point, we simply execute sequentially all the actions of the alarm routine, that is:

  1. Close the roof by setting the servo at 0 degrees.
  2. Activate the watering system by setting a HIGH value at the relay pin, or to be precise at the pin that connects to the base of the transistor that acts as a driver to the relay.
  3. Produce a tone.
  4. Turn all the three LEDs on.
  5. Wait for a second.
  6. Produce a different tone in a similar way to a real alarm.
  7. Turn all the three LEDs off.
  8. Wait for another second.
  9. Turn the sound off.

Initialization and board configuration

The next step in our code is to configure the used pins and other necessary setup routines for all used devices. Here is the complete setup() function of our project sketch:

void setup() {
  pinMode(redLedPin, OUTPUT);
  pinMode(yellowLedPin, OUTPUT);
  pinMode(greenLedPin, OUTPUT);
  pinMode(relayPin,OUTPUT);
  pinMode(motorPin, OUTPUT); 
  pinMode(buzzerPin, OUTPUT);
  myServo.attach(servoPin);
  attachInterrupt(0, buttonPress, RISING);
  Serial.begin(9600);
}

The pinMode() functions call apart, we simply initialize our servo instance, set the ISR for the interrupt 0, and begin a serial communication that will help us calibrate our sensors.

There is nothing new here.

The main execution loop

Finally, here is the main loop of our sketch where it all happens. You will immediately notice that I simply check every subsystem in sequence and trigger every action needed, so let's analyze every one of the four subsystems in the code.

Temperature subsystem

The code corresponding to the temperature control is as follows:

tempValue = analogRead(thermistorPin);
  state =map(tempValue, TEMPMIN, TEMPMAX, 1, 4);
  switch (state){
    case 1: 
     digitalWrite(greenLedPin,HIGH);
     break;
    case 2:
      digitalWrite(yellowLedPin,HIGH);
      analogWrite(motorPin, MEDIUMPOWER);
      break;
    case 3:
      digitalWrite(redLedPin, HIGH);
      analogWrite(motorPin,FULLPOWER);
      myServo.write(180);
      break;
    case 4:
      alarmRoutine();
      break;
  }

We begin by reading the sensor and storing its value in the tempValue variable.

From now on, we map the read value into the state variable. The mapping will convert the read value, valid between the TEMPMIN and TEMPMAX constants that we presented earlier, into a new range of 1 to 4 that will ease the process of selecting what to do depending on one of the four possible states.

We use this variable to select a case up of four in the switch control structure that will represent the four possible states of our system and which will trigger different actions:

  • State 1: It is all under control, and we simply set the green LED on.
  • State 2: The temperature is rising, so we turn yellow LED on and the motor at MEDIUMPOWER.
  • State 3: The temperature is getting too hot, so we indicate it by turning the red LED on, power the motor at FULLPOWER, and open the retractable roof to try to lower the temperature by setting the servo at 180 degrees.
  • State 4: This state represents an emergency, so, we simply call the already presented alarmRoutine() function.

Humidity subsystem

After the temperature control has been made, we are going to see how the humidity control is made. This case is quite simple because it only has two possible situations: if the reading is above the BEGINWATERING value, we open the watering pump and we close it when the reading is below the ENDWATERING value:

humidityValue=analogRead(potentiometerPin);
  if (humidityValue>BEGINWATERING){
    digitalWrite(relayPin,HIGH);
  }
  if (humidityValue<ENDWATERING){
    digitalWrite(relayPin,LOW);
  }

Notice how these two conditionals are independent, meaning that the one that activates the watering will be true first and quite a good number of loops later the other will be true.

This is so because most resistive humidity sensors offer a lower resistance when the humidity increases. This way, when the soil is really dry, we will trigger the water pump and will leave it on for as much time as needed until a new reading of the sensor confirms that the humidity has increased and so the sensor reading has decreased.

Lighting subsystem

The photocell control is very similar to the humidity one, but in this case both possible cases are exclusive, which means that either it is day or it is night; there is no range here, it is only a simple cutoff value we all have, and thus we either open the roof or we close it.

Here, there is only an if conditional statement with two exclusive branches, a totally different approach to the humidity control:

lightValue = analogRead(photocellPin);
  if (lightValue>DAYLIGHT){
    myServo.write(0);
  }
  else{
    myServo.write(180);
  }

Just as in the previous section, we read the sensor and compare it with the DAYLIGHT cutoff value to open or close the roof by setting the servomotor at 0 or 180 degrees.

Alarm subsystem

The alarm control is quite simple; it just checks the buttonPressed variable to see if it was changed in the ISR, in which case it resets it and calls the alarmRoutine() function:

if (buttonPressed){
    buttonPressed = false;
    alarmRoutine();  
  }

Serial feedback and calibration

To allow for calibrating the sensor, we finish the main loop by sending back the reading of every analog sensor and the mapped state to the Serial Monitor.

Once the project runs, this code can be simply commented out to avoid working on unneeded instructions:

  Serial.print(" S: ");
  Serial.print(state);
  Serial.print(" T: ");
  Serial.print(tempValue);
  Serial.print(" H: ");
  Serial.print(humidityValue);
  Serial.print(" L: ");
  Serial.println(lightValue);

The complete project code

You can download the complete code for this project from the Packt Publishing website. The name of the file is _17_greenhouse.ino inside the 8569_10_Code folder.

Final considerations

This was a relatively simple project based on a theoretical situation, but if you are really interested in working on a real gardening project, you should take a look on the Internet because there are plenty of similar projects and ideas that might help you.

In particular, I liked the initiative of the GardenBot project at http://gardenbot.org, an open source initiative to build a complete monitoring and automation gardening system.

I'm sure you will like to take a look at it; you will enjoy seeing how much you can understand of such a project after reading this book and it could even give you some ideas to improve the project in this chapter.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.224.62.105