8.6 Writing the Sketch

The sketch for this project borrows from ideas we encoded in two other projects. The sensor readings come from the Tweeting Bird Feeder, and the state machine for the open or closed status was copied from the Water Level Notifier. As such, here is the complete sketch.

CurtainAutomation/CurtainAutomation.pde
  ​#include <AFMotor.h> ​
  ​ #define LIGHT_PIN 0 ​
  ​ #define LIGHT_THRESHOLD 800​
  ​ #define TEMP_PIN 5​
  ​ #define TEMP_THRESHOLD 72​
  ​ #define TEMP_VOLTAGE 5.0​
  ​ #define ONBOARD_LED 13​
  ​​
  int curtain_state = 1; ​
  int light_status = 0;​
  double temp_status = 0;​
  ​​
  ​boolean daylight = true;​
  ​boolean warm = false;​
  ​​
  ​AF_Stepper motor(100, 2);​
  ​​
  void setup() { ​
  ​ Serial.begin(9600);​
  ​ Serial.println("Setting up Curtain Automation...");​
  // Set stepper motor rotation speed to 100 RPMs
  ​ motor.setSpeed(100);​
  // Initialize motor
  // motor.step(100, FORWARD, SINGLE);
  // motor.release();
  ​ delay(1000);​
  ​}​
  ​​
  void Curtain(boolean curtain_state) { ​
  ​ digitalWrite(ONBOARD_LED, curtain_state ? HIGH : LOW);​
  if (curtain_state) {​
  ​ Serial.println("Opening curtain...");​
  // Try SINGLE, DOUBLE, INTERLEAVE or MICROSTOP
  ​ motor.step(800, FORWARD, SINGLE);​
  ​ } else {​
  ​ Serial.println("Closing curtain...");​
  ​ motor.step(800, BACKWARD, SINGLE);​
  ​ }​
  ​}​
  ​​
  void loop() { ​
  ​​
  // poll photocell value
  ​ light_status = analogRead(LIGHT_PIN);​
  ​ delay(500);​
  ​​
  // print light_status value to the serial port
  ​ Serial.print("Photocell value = ");​
  ​ Serial.println(light_status);​
  ​ Serial.println("");​
  ​​
  // poll temperature
  int temp_reading = analogRead(TEMP_PIN);​
  ​ delay(500);​
  ​​
  // convert voltage to temp in Celsius and Fahrenheit
  float voltage = temp_reading * TEMP_VOLTAGE / 1024.0;​
  float temp_Celsius = (voltage - 0.5) * 100 ;​
  float temp_Fahrenheit = (temp_Celsius * 9 / 5) + 32;​
  // print temp_status value to the serial port
  ​ Serial.print("Temperature value (Celsius) = ");​
  ​ Serial.println(temp_Celsius);​
  ​ Serial.print("Temperature value (Fahrenheit) = ");​
  ​ Serial.println(temp_Fahrenheit);​
  ​ Serial.println("");​
  ​​
  if (light_status > LIGHT_THRESHOLD)​
  ​ daylight = true;​
  else
  ​ daylight = false;​
  ​​
  if (temp_Fahrenheit > TEMP_THRESHOLD)​
  ​ warm = true;​
  else
  ​ warm = false;​
  ​​
  switch (curtain_state)​
  ​ {​
  case 0:​
  if (daylight && !warm)​
  // open curtain
  ​ {​
  ​ curtain_state = 1;​
  ​ Curtain(curtain_state);​
  ​ }​
  break;​
  ​​
  case 1:​
  if (!daylight || warm)​
  // close curtain
  ​ {​
  ​ curtain_state = 0;​
  ​ Curtain(curtain_state);​
  ​ }​
  break;​
  ​ }​
  ​}​

Reference the AFMotor library that will be used to drive the stepper motor attached to the Adafruit motor shield.

We will define several values up front. This will make it easier to change the LIGHT_THRESHOLD and TEMP_THRESHOLD values as we refine the stepper motor trigger points.

Variables for storing curtain state—as well as the analog values of the photocell and temperature sensors two boolean variables, daylight and warm—are used in the main loop’s conditional statements to identify the status of daylight and the indoor room temperature. We also assign the number of steps per revolution (in this case, 100) and the motor shield port that the stepper motor is attached to (in this case, the second port per the wiring diagram) by creating an AF_Stepper object called motor.

Here’s where we initialize the serial port to output the light and temperature readings to the Arduino IDE serial window, as well as initialize the speed of the motor (in this case, 100 revolutions per minute).

The Curtain function will be called when the light or temperature thresholds are exceeded. The state of the curtains (open or closed) is maintained so that the motor doesn’t keep running every second the threshold is exceeded. After all, once the curtains are opened, there’s no need to open them again. In fact, doing so might even damage the stepper motor, grooved pulley, or curtain drawstring.

If the Curtain function receives a curtain_state of true, the stepper motor will spin counterclockwise to open the curtains. A curtain_state value of false will spin clockwise to close the curtains.

We will also use the Arduino’s onboard LED to indicate the status of the curtains. If the curtains are open, the LED will remain lit. Otherwise, the LED will be off. Since the motor shield will be covering the top of the Arduino, the onboard LED won’t be easily visible, but it will still serve as a good visual aid for debugging purposes.

The main loop of the sketch is where all the action happens. We poll the analog values of the photocell and temperature every second, convert the electrical value of the temperature sensor both to Celsius and—for those who have yet to convert to the metric system—Fahrenheit. If the light sensor exceeds the LIGHT_THRESHOLD value we assigned in the #define section of the sketch, then it must be daytime (i.e., daytime = true). However, we don’t want to open the curtains if it’s already warm in the room, since the incoming sunlight would make the room even warmer. Thus, if the temperature status exceeds the TEMP_THRESHOLD, we will keep the curtains closed until the room cools down. After checking the status of the curtain_state, we will pass a new state to the Curtain routine and open or close the curtains accordingly.

Verify, download, and execute the sketch on the Arduino. Leave the Arduino tethered to your computer and open the Arduino IDE’s serial window to see the light and temperature values being captured by the sensors. Now we can verify whether exceeding the threshold values produces the desired effect of activating the stepper motor (see Figure 34, Test the Curtain Automation sketch).

Test the Curtain Automation sketch first by covering the photocell with your finger to verify that the stepper motor rotates the shaft in the counterclockwise direction. Remove your finger, and the shaft should rotate clockwise the same number of times. Blow warm air or use a blow dryer to warm up the air around the temperature sensor. When the threshold is exceeded, the stepper motor shaft should spin clockwise. This translates to open curtains being closed. Before removing the heat source, cover the photocell with your finger again. Then remove the heat source. The stepper motor shaft should remain motionless.

/epubstore/R/M-Riley/Programming-your-home//images/curtainautomation-actualwiring.jpg

Figure 34. Test the Curtain Automation sketch

Remove your finger from the photocell. If the air surrounding the temperature sensor has cooled, the shaft should rotate counterclockwise. If it doesn’t rotate, blow air on the temperature sensor to cool it down; it should react once the temperature drops below the target threshold. Verify that your sketch reacted when the designated threshold values for light and temperature were exceeded. You may need to tweak these threshold values to ensure that the stepper motor reacts when the desired light densities and room temperature are attained. You may also need to consider omitting a light or temperature sensor range for the assigned threshold values. Otherwise, the stepper motor may act a bit jittery as the light or temperature wavers back and forth between triggered threshold values.

After you have confirmed that the sensors are properly reporting their values and the stepper motor shaft moves when the threshold values are exceeded, we can seat the sensors on a windowsill and mount the stepper motor on the wall next to the curtains. Once the system is working, you can also choose to reposition the sensors anywhere inside the room as long as the wire attaching the sensors to the Arduino board is long enough. If you do so, make sure that the wire and sensors are not in an area where they might accidentally be stepped on or where the connecting wire could be tripped over.

Programming Your Home
cover.xhtml
f_0000.html
f_0001.html
f_0002.html
f_0003.html
f_0004.html
f_0005.html
f_0006.html
f_0007.html
f_0008.html
f_0009.html
f_0010.html
f_0011.html
f_0012.html
f_0013.html
f_0014.html
f_0015.html
f_0016.html
f_0017.html
f_0018.html
f_0019.html
f_0020.html
f_0021.html
f_0022.html
f_0023.html
f_0024.html
f_0025.html
f_0026.html
f_0027.html
f_0028.html
f_0029.html
f_0030.html
f_0031.html
f_0032.html
f_0033.html
f_0034.html
f_0035.html
f_0036.html
f_0037.html
f_0038.html
f_0039.html
f_0040.html
f_0041.html
f_0042.html
f_0043.html
f_0044.html
f_0045.html
f_0046.html
f_0047.html
f_0048.html
f_0049.html
f_0050.html
f_0051.html
f_0052.html
f_0053.html
f_0054.html
f_0055.html
f_0056.html
f_0057.html
f_0058.html
f_0059.html
f_0060.html
f_0061.html
f_0062.html
f_0063.html
f_0064.html
f_0065.html
f_0066.html
f_0067.html
f_0068.html
f_0069.html
f_0070.html
f_0071.html
f_0072.html
f_0073.html
f_0074.html
f_0075.html
f_0076.html
f_0077.html
f_0078.html
f_0079.html
f_0080.html
f_0081.html
f_0082.html
f_0083.html
f_0084.html
f_0085.html
f_0086.html
f_0087.html
f_0088.html
f_0089.html
f_0090.html
f_0091.html
f_0092.html
f_0093.html
f_0094.html
f_0095.html
f_0096.html
f_0097.html
f_0098.html
f_0099.html
f_0100.html
f_0101.html
f_0102.html
f_0103.html
f_0104.html
f_0105.html
f_0106.html
f_0107.html
f_0108.html
f_0109.html
f_0110.html
f_0111.html
f_0112.html
f_0113.html
f_0114.html
f_0115.html
f_0116.html
f_0117.html
f_0118.html
f_0119.html
f_0120.html
f_0121.html
f_0122.html
f_0123.html
f_0124.html
f_0125.html