Introduction: Bird Cage LED Daylight Simulation System

About: A biomedical engineer working as a software designer in the medical devices sector.

Sometimes you get a chance to combine helping out your family with a nice hobby project..

In this case my father in law was looking for a lighting system for his bird breeding cages. A daylight simulation system to be exact. He asked me to take a look at a website which sells such systems, since he wanted to know what he needed to order. After a quick look at those websites and seeing their pricing.. I convinced my father in-law I would develop and install the system myself.. He would get his lighting, I would have a new hobby project!

Daylight-simulation systems

The requirements for a daylight simulation system are simple: lights should gradually (say in 20 minutes) increase to a set high intensity in the morning, stay at that intensity during the day, gradually decrease to a set lower intensity in the evening, and stay at the low intensity during the night.

Other 'non-functional' requirements of such systems: the light should not flicker (this is stressful for the birds), the dimming should be smooth (again related to stress), the system should be reliable..


This instructable describes how I built this system based on an Arduino, some N-channel NPN transistors, a real time clock, a character LCD and some cheap LEDs from eBay.

A sidenote: The birds are only in the cages for breeding, the remainder of the year they are in an outside bird sanctuary. So no animals were harmed for making this instructable!

I've submitted this project to the 
123D Circuits Contest and the 2014 makerlympics (both pending approval). So if you like this project: please vote for it! Thanks!

Step 1: Bill of Materials

The base of the system is an Arduino Duemillenova. I think a Uno should work just fine. If you are considering to use a Leonardo or Mega: the code sets special registers in the Arduino.. these might be different for these boards!

furthermore the system uses:

  • an Arduino experimenting cape
  • a 16x4 character LCD (HD44780)
  • an I2C serial display adapter (or just get a serial LCD)
  • BD135 transistors (any NPN N-channel will do. Power MosFets would be even better)
  • a 7809 9v voltage regulator (12v to 9v for the Arduino)
  • A ds1302 RTC module
  • some capacitors and resistors
  • Cool white LED modules (12v led strips will also do)
  • an auto-reset fuse ( or normal fuse if you like)
  • Some switches (if you want to switch off unused cages). I used: http://www.conrad.nl/ce/nl/product/701011/
  • a 12v power supply (over dimensions). I used a 3.5A supply for 18 x 4 LEDs (total +- 18 watts)
  • A case: I used a Fibox TAM201610 http://www.conrad.nl/ce/nl/product/533259/

I think everything combined including shipping cost about 85 euro's

All items were sourced online from dealextreme, eBay, Conrad etc.. but any electronics store would do.


A note on 'daylight' LEDs

According to bird breeder forums and stores: birds need special daylight LEDs. Maybe this is true (I do not want to start a religious discussion), but to me such definitions feel a bit like high-end audio discussions...

I've seen special birdkeeper websites showing off 'home developed' LED units for which the CE marking is of the 'China Export' type. A quick search on eBay revealed that the advertised LEDs (which are sold at special discount prices of 5 EUR per unit) very much resemble 'Cool White store lighting' LED chains sold for around 10USD / 20 units. Therefore I feel that at least some of the advise is a sneaky way to overprice the LEDs.

Step 2: Power the Arduino and LEDs

A schematic of supplying power to the arduino via a voltage regulator IC. Note that the same power supply can directly be used to power the LEDs without a voltage regulator.

Note that since the number of available components is still a bit limited, the 9v battery represents a power supply ( > 9v) and the PNP transistor represents a 7809 voltage regulator IC. The schematic view gives the clearest representation.

Be sure to use a power supply which can easily supply the the current you need to drive all your LEDs. I started out with a 12V / 2A power supply from some equipment that I no longer had. This seemed OK in the beginning, but resulted in a lower brightness and an unreadable LCD after about a day of testing!

Step 3: LED Dimming

Dimming/fading of leds is the key part of the daylight simulation system. Leds can be faded by supplying a PWM signal instead of a DC signal. An example of this is known to most arduino owners: The 'fade' example. The only thing you need for this is a simple LED and a resistor to limit the current through the circuit.

The same code can be used for fading a LED strip instead of a single LED. When powering or fading a LED strip, the voltage or the current that can be supplied by an Arduino pin are no longer sufficient. Therefore an n-channel NPN transistor is introduced. A very common transistor for this is the BD135. The current that can be supplied by this LED is limited (the TO220 type I used for this can supply a maximum of 1A). If more power is needed, a Power MOSFET can be used.

An example circuit is shown below:

Here a 9V battery is used to power a led which is controlled by an Arduino. Here, the 9v battery represents any type of external power supply. Note that a 10k external puldown resistor is used to keep the signal low until the arduino supplies a signal on Pin 9. Also, in case you are powering a LED strip, the current limiting resistors are already integrated in the strip itself and can be left out.

The above example does not take into account two things:

  1. The relation between the pulse width and the (perceived) LED brightness is not linear, but logarithmic (or at least: logarithmic is a good enough approximation)
  2. The default PWM frequency is rather low, this might disturb the birds if they're in this light all day

Google is your friend, and there are a lot of topics covering fast PWM and logarithmic LUTs for arduino, so you don't have to re-invent the wheel. My main source of inspiration: http://forum.arduino.cc/index.php/topic,130736.0.h...

So here's some example code which uses a logarithmic LUT and fast PWM on pin 9:

Note that this works on my duemillanova clone, and might not work on Arduino mega, due, leonardo etc.

<p>/* BirdLight</p><p>Example of a 12bit logarithmic LUT & Fast PWM for gently increasing
/ decreasing brightness during day to night and night to day transitions. </p><p>Code mostly retrieved from: <a href="http://forum.arduino.cc/index.php/topic,130736.0.html"> http://forum.arduino.cc/index.php/topic,130736.0....</a> </p><p>Copyright 2013 - Patrick Bronneberg 
*/
/*-----( Configuration )-----*/
const double Night_Level = 5.0;
const double Day_Level = 99.0;
const int LED_PWM_PIN = 9;     // the pin that the fading LEDs are attached to</p><p>/*-----( Declare Constants )-----*/
const int PWMMax = 4095;     // Set maximum brightness for PWM
// Define a logarithmic LUT for PWM dimming
const int PWMLut[] = {0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,9,9,9,10,11,
  13,15,16,17,19,21,23,25,26,27,29,31,32,33,35,37,39,41,43,45,47,49,51,53,55,57,
  60,63,66,69,71,74,77,80,84,88,91,94,98,102,106,110,114,118,123,128,133,138,143,
  148,154,160,166,172,179,185,192,199,207,214,222,230,239,248,257,266,276,286,296,
  306,317,329,341,353,366,379,392,406,421,436,451,466,483,500,518,536,555,574,595,
  616,638,661,684,707,732,757,784,811,840,869,900,931,964,997,1032,1067,1105,1144,
  1184,1224,1267,1311,1357,1404,1453,1503,1555,1609,1665,1722,1782,1843,1907,1973,
  2042,2112,2185,2260,2339,2419,2503,2589,2679,2771,2867,2965,3069,3174,3284,3397,
  3514,3629,3761,3905,4024,4079,4094};
//Define the size of the lut: note that integers are 16bits so 2 bytes
const int lutSize = sizeof(PWMLut)/2;</p><p>/*-----( Declare global variables )-----*/  
int ledBrightnessLevel = 0;
int dayLevel = 0;
int nightLevel = 0;
boolean isDay = true;</p><p>void setup()
{  
  //Initialize the LED PWM
  pinMode(LED_PWM_PIN,OUTPUT); 
  TCCR1A = (1 << COM1A1) | (1 << WGM11);                // Enable Fast PWM on OC1A (Pin 9)
  TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS10);   // Mode 14 Fast PWM/ (TOP = ICR1), pre-scale = 1
  ICR1 = PWMMax;     //Set the TOP value for 12-bit PWM
  
  // Calculate levels from configured percentages
  dayLevel = LutPercentageToLevel(Day_Level);
  nightLevel = LutPercentageToLevel(Night_Level);
}</p><p>void loop()
{
  if (isDay)
  {
    //Decrease led brightness until we reach night level
    if (ChangeLedBrightness(nightLevel, -1))
    {
      isDay = false;
    }
  }
  else
  {
    //Increase led brightness until we reach day level
    if (ChangeLedBrightness(dayLevel, -1))
    {
      isDay = true;
    }
  }
}</p><p>double LutLevelToPercentage(int lutLevel)
{
  return lutLevel*100.0/lutSize;
}</p><p>int LutPercentageToLevel(double lutPercentage)
{
  return (int)((lutSize/100.0)*lutPercentage);
}</p><p>boolean ChangeLedBrightness(int finishedLevel, int ledFadeAmount)
{
 
  // set the current brightness
  analogWrite(LED_PWM_PIN, PWMLut[ledBrightnessLevel]);    </p><p>  // change the brightness for next time through the loop:
  ledBrightnessLevel = ledBrightnessLevel + ledFadeAmount;</p><p>  //Check if dimming is finished
  if ((ledFadeAmount > 0 && ledBrightnessLevel >= finishedLevel) 
      || (ledFadeAmount < 0 && ledBrightnessLevel <= finishedLevel)  )
  {
    //Finished!
    return true; 
  }
  //Not yet finished
  return false;
}</p>

Step 4: Adding the Display

I had a 20x4 HD44780 character display lying around which I bought for less than 1 Euro on a clearance. I wanted to use this in this project, but did not want to lose 10pins of my arduino just for driving the LCD.

To overcome this issue, I used an I2C backpack for the display. This is basically an adapter which drives all pins of the LCD but only costs you 2 pins + 5V & ground on your arduino. As stated in the Bill of Materials, these backpacks can be bought for less than 3 dollars on the internet.

The downside of using such a device is that the standard Arduino character LCD library doesn't work, and you have to browse on the internet for a while until you find a suiting replacement. The replacement I used is:F Malpartida's NewLiquidCrystal library. which works great, so many thanks to F Malpartida of electroFUN LTD!

Unfortunately it took me 2 evenings to get the display up & running with the backpack. So watch out for the following issues

Issues encountered

  • The I2C address was different than indicated in the documentation: should be 0x20, but was 0x27.
    • HINT: In case nothing happens when interfacing with the LCD you probably have the wrong address :)
  • My backpack was wired differently from all the example libraries, so a had to figure out which LCD pin was driven by which pin of the I2C chip (required using the datasheet of the I2C chip for the pinout, and a voltage meter to figure out the connections to the LCD) --> When I used the correct address, the backlight started flickering, but no text was shown on screen.. which indicated the different wiring!

Step 5: Keeping Time

The arduino does not feature a real-time-clock (RTC). Since this project is a daylight simulation system, we need to know what time it is in order to start dimming the LEDs.

The most common & affordable RTC's are the DS1302 (SPI) and DS1307 (I2C). For this project I used the DS1302 connected via SPI. Connecting to this RTC is well explained on the arduino playground website

I used the DS1302 library by Henning Karlsen. Which is easy to use, and very well documented. So thanks Henning for the hard work & for sharing the code!

Example code to get the time, and check it against a set time:

boolean CheckIsDay()
{ //If called the second time, transit to the correct state Time time = rtc.getTime(); if ((time.hour > Day_Start_Hour && time.hour < Day_End_Hour) || (time.hour == Day_Start_Hour && time.min >= Day_Start_Min) || (time.hour == Day_End_Hour && time.min < Day_End_Min)) { return true; } { return false; } }

Example code for setting the time / date:

// Set the clock to run-mode, and disable the write protection
rtc.halt(false); rtc.writeProtect(false); // The following lines can be commented out to use the values already stored in the DS1302 rtc.setDOW(SATURDAY); // Set Day-of-Week (ENUM capital english full name of the day) rtc.setTime(16, 12, 0); // Set the time (24hr format) rtc.setDate(4, 1, 2014); // Set the date dd mm yyyy

Step 6: Structuring Code: State Machine & Tasks

I'm used to programming in higher level languages, with large supporting frameworks.. because of this I felt a need for some additional structure in my code.

Soft Timer

In order to program like there are multiple tasks running in the same time, I used the Soft Timer library. Using this library prevents having a 'god' loop method which controls everything, and in which the control flow becomes unreadable. Or as the creator explains it:

SoftTimer enable a higher level Arduino programing, jet easy to use, and lightweight. You often face with the problem that you need to do multiply tasks in the same time. In SoftTimer manner programmer creates Tasks that runs periodically.
When you use SoftTimer you do not implement the "loop" function of the Arduino. All your code will run event driven, all processes running asynchronous, no more blocking code (like delay()) is needed.


Note that you sacrifice predictability in your timing, in my project this doesn't matter.. but if hard real-time is needed, don't use libraries like this!


State machine:

Another thing I added for structure is a state machine.

Since the device can only be in one state at a single time, and the code for entering and exiting the states is in the same function, this really helps to improve the predictability of your device!

/*-----( Declare State enum )-----*/
#define STARTUP_STATE 1 #define CONFIGURATION_STATE 2 #define DAY_STATE 3 #define NIGHT_STATE 4 #define DAY_NIGHT_TRANSITION 5 #define NIGHT_DAY_TRANSITION 6

void ChangeState(int newState)

{ SoftTimer.add(&backlightTask); //Leave current state switch (currentState) { case STARTUP_STATE: SoftTimer.remove(&startupTask); break; case CONFIGURATION_STATE: break; case DAY_STATE: SoftTimer.remove(&timerTask); SoftTimer.remove(&defaultDisplayTask); break; case NIGHT_STATE: SoftTimer.remove(&timerTask); SoftTimer.remove(&defaultDisplayTask); break; case DAY_NIGHT_TRANSITION: SoftTimer.remove(&dayToNightTask); break; case NIGHT_DAY_TRANSITION: SoftTimer.remove(&nightToDayTask); break; } //Enter new state switch (newState) { case STARTUP_STATE: SoftTimer.add(&startupTask); break; case CONFIGURATION_STATE: DrawSettingsUI(); break; case DAY_STATE: SoftTimer.add(&timerTask); SoftTimer.add(&defaultDisplayTask); DrawSettingsUI(); break; case NIGHT_STATE: SoftTimer.add(&timerTask); SoftTimer.add(&defaultDisplayTask); break; case DAY_NIGHT_TRANSITION: ledBrightnessLevel = dayLevel; SoftTimer.add(&dayToNightTask); break; case NIGHT_DAY_TRANSITION: ledBrightnessLevel = nightLevel; SoftTimer.add(&nightToDayTask); break; } //Save current state currentState = newState; }

Step 7: Combining Everything

After breadboarding fun, it's time to create the circuit using the experimentation shield. The voltage regulators, transistors for powering the LEDs, and circuits for driving the display and rotary encoder are combined into one very crowded shield.

In order to be able to control the light for each individual bird cage, one switch was added for each cage.

I used a Dremel to create a window for the LCD, and a standard 7mm drill to create the holes for the switches and the rotary encoder. Note: I used painters tape to cover the places where I was to drill the holes so that the drill doesn't slip away from the surface and damage the casing.

Unfortunately I did not take the large circuit board into account when marking the edges of the LCD display to cut out of the case. This resulted in the ugly stripe above the LCD..

All in all, the device turned out quite nice. Judge for yourself based on the pictures :)

Step 8: Testing

In any project that has a little complexity I recommend: test, test, test!

And don't test for a short while, keep the device running for days, find the boundary scenario's until you're sure enough that it won't fail when it's in use.

In my project, timekeeping and dimming the lights are the parts I focused on.. This resulted in the following

issues:

  • Buffer overflows when writing to the LCD (I'm a spoiled programmer.. forgot about those things)
  • The power supply failed after a day of operation
  • The LUT contained a typo, which caused the light to flicker
  • Programming errors caused a situation that if the device was first powered on at night, it would never transfer to 'day' mode..
  • The RTC did not keep time longer than 5 minutes without power (the battery was dead)

All in all.. it was well worth the effort!

Step 9: Results

Installing the lights was a bit of a pain, since all cages needed to be fitted with LED modules and wiring, and the wiring of all 14 cages had to nicely routed to the controlling unit.. But after a day's work, and quite a lot of solder the birds could be moved to their newly lit cages!

This was about 3 months ago. And I'm happy to say that the system has worked without issues! In the meanwhile the birds have started breeding --> which means they're actually happy with the lighting :)

So: if you like this instructable, have questions or comments: let me know what you think!

If I failed to credit someone: this was unintentional.. so let me know!

And have fun creating!


Patrick


The full code for the bird lighting system (also supplied as an attachment)

/* BirdLight

Daytime simulation for Bird cages
Hardware: Arduino + I2C 16x4 LCD + SPI DS1302 + BD509 Transistors

Uses a 12bit logarithmic LUT for gently increasing / decreasing brightness during Day to night and night to day transitions.

Copyright 2013 - Patrick Bronneberg

This sketch is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version.

This sketch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.

*/ /*-----( Import needed libraries )-----*/ #include <wire.h> #include <lcd.h> #include <liquidcrystal_i2c.h> // F Malpartida's NewLiquidCrystal library #include <ds1302.h> #include <softtimer.h> #include <softpwmtask.h> #include <pcimanager.h></pcimanager.h></softpwmtask.h></softtimer.h></ds1302.h></liquidcrystal_i2c.h></lcd.h></wire.h></p><p>/*-----( Configuration )-----*/ const double Night_Level = 5.0; const double Day_Level = 99.0; const unsigned int Day_Start_Hour = 7; const unsigned int Day_Start_Min = 0; const unsigned int Day_End_Hour = 21; const unsigned int Day_End_Min = 40;</p><p>/*-----( Declare Constants )-----*/ #define OFF 0 #define ON 1 #define RTC_MULTIPLIER 4 // 4 changes received for each step on the encoder</p><p>#define LCD_I2C_ADDR 0x27 // Define I2C Address for the PCF8574A #define LCD_BACKLIGHT_PIN 3 // The pin to use to control the LCD backlight (from i2c expander)</p><p>const int LED_PWM_PIN = 9; // the pin that the PWM LEDs are attached to #define LEDValue OCR1A // Use 12 bit timer1 for PWM Dimming</p><p>const int PWMMax = 4095; // Set maximum brightness for PWM // Define a logarithmic LUT for PWM dimming const int PWMLut[] = {0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,9,9,9,10,11, 13,15,16,17,19,21,23,25,26,27,29,31,32,33,35,37,39,41,43,45,47,49,51,53,55,57, 60,63,66,69,71,74,77,80,84,88,91,94,98,102,106,110,114,118,123,128,133,138,143, 148,154,160,166,172,179,185,192,199,207,214,222,230,239,248,257,266,276,286,296, 306,317,329,341,353,366,379,392,406,421,436,451,466,483,500,518,536,555,574,595, 616,638,661,684,707,732,757,784,811,840,869,900,931,964,997,1032,1067,1105,1144, 1184,1224,1267,1311,1357,1404,1453,1503,1555,1609,1665,1722,1782,1843,1907,1973, 2042,2112,2185,2260,2339,2419,2503,2589,2679,2771,2867,2965,3069,3174,3284,3397, 3514,3629,3761,3905,4024,4079,4094}; //Define the size of the lut: note that integers are 16bits so 2 bytes const int lutSize = sizeof(PWMLut)/2;</p><p>/*-----( Declare State enum )-----*/ #define STARTUP_STATE 1 #define CONFIGURATION_STATE 2 #define DAY_STATE 3 #define NIGHT_STATE 4 #define DAY_NIGHT_TRANSITION 5 #define NIGHT_DAY_TRANSITION 6</p><p>/*-----( Declare objects )-----*/ LiquidCrystal_I2C lcd(LCD_I2C_ADDR,2,1,0,4,5,6,7); DS1302 rtc(3, 4, 5); //Real Time Clock on pins 3,4,5 (SPI)</p><p>// 20 minutes, 70% transition, 168 LUT steps --> (20 * 60 * 1000) / (0.7 * 168) Task dayToNightTask(10000, dayToNightTransition); Task nightToDayTask(10000, nightToDayTransition); Task defaultDisplayTask(10000, displayDefault); Task timerTask(20000, checkTimer); Task displayTask(30000, display); Task backlightTask(30000, toggleBacklight); Task startupTask(10000, startup); //Task configurationTask(200, configuration);</p><p>/*-----( Declare global variables )-----*/ int ledBrightnessLevel = 0; boolean isBacklightEnabled = false; boolean isConfigDisplayed = false; boolean startupCalled = false; int currentState = 0; int dayLevel = 0; int nightLevel = 0; int lastPosition = 0; int sleepRuns = 0;</p><p>void setup() { //Initialize the LED PWM pinMode(LED_PWM_PIN,OUTPUT); TCCR1A = (1 << COM1A1) | (1 << WGM11); // Enable Fast PWM on OC1A (Pin 9) TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS10); // Mode 14 Fast PWM/ (TOP = ICR1), pre-scale = 1 ICR1 = PWMMax; //Set the TOP value for 12-bit PWM // Set the clock to run-mode, and disable the write protection rtc.halt(false); rtc.writeProtect(false); // The following lines can be commented out to use the values already stored in the DS1302 rtc.setDOW(SATURDAY); // Set Day-of-Week (ENUM capital english full name of the day) rtc.setTime(16, 12, 0); // Set the time (24hr format) rtc.setDate(4, 1, 2014); // Set the date dd mm yyyy lcd.begin (16,4); // initialize the lcd // Set the backlight pin, start disabled lcd.setBacklightPin(LCD_BACKLIGHT_PIN,POSITIVE);</p><p> // Reset the display lcd.clear(); // Calculate levels from configured percentages dayLevel = LutPercentageToLevel(Day_Level); nightLevel = LutPercentageToLevel(Night_Level); // Start displaying the current time SoftTimer.add(&displayTask); // Startup the device ChangeState(STARTUP_STATE); Serial.begin(9600); Serial.println("Wim's Vogelparadijs"); }</p><p>void ChangeState(int newState) { SoftTimer.add(&backlightTask); //Leave current state switch (currentState) { case STARTUP_STATE: SoftTimer.remove(&startupTask); break; case CONFIGURATION_STATE: break; case DAY_STATE: SoftTimer.remove(&timerTask); SoftTimer.remove(&defaultDisplayTask); break; case NIGHT_STATE: SoftTimer.remove(&timerTask); SoftTimer.remove(&defaultDisplayTask); break; case DAY_NIGHT_TRANSITION: SoftTimer.remove(&dayToNightTask); break; case NIGHT_DAY_TRANSITION: SoftTimer.remove(&nightToDayTask); break; } //Enter new state switch (newState) { case STARTUP_STATE: SoftTimer.add(&startupTask); break; case CONFIGURATION_STATE: DrawSettingsUI(); break; case DAY_STATE: SoftTimer.add(&timerTask); SoftTimer.add(&defaultDisplayTask); DrawSettingsUI(); break; case NIGHT_STATE: SoftTimer.add(&timerTask); SoftTimer.add(&defaultDisplayTask); break; case DAY_NIGHT_TRANSITION: ledBrightnessLevel = dayLevel; SoftTimer.add(&dayToNightTask); break; case NIGHT_DAY_TRANSITION: ledBrightnessLevel = nightLevel; SoftTimer.add(&nightToDayTask); break; } //Save current state currentState = newState; }</p><p>double LutLevelToPercentage(int lutLevel) { return lutLevel*100.0/lutSize; }</p><p>int LutPercentageToLevel(double lutPercentage) { return (int)((lutSize/100.0)*lutPercentage); }</p><p>void display(Task* me) { lcd.setCursor(1,0); //Start at character 0 on line 0 lcd.print(rtc.getDateStr(FORMAT_SHORT,FORMAT_LITTLEENDIAN,'/')); lcd.setCursor(10,0); //Start at character 0 on line 0 lcd.print(rtc.getTimeStr(FORMAT_SHORT)); }</p><p>void toggleBacklight(Task* me) { if (isBacklightEnabled) { lcd.setBacklight(OFF); isBacklightEnabled = false; //Remove the task after running once SoftTimer.remove(me); } else { lcd.setBacklight(ON); isBacklightEnabled = true; } }</p><p>void displayDefault(Task* me) { if (isConfigDisplayed) { DrawDayNightUI(); isConfigDisplayed = false; } else { DrawSettingsUI(); isConfigDisplayed = true; } }</p><p>void checkTimer(Task* me) { Time time = rtc.getTime(); int currentLevel = 0; switch (currentState) { case DAY_STATE: currentLevel = dayLevel; if (time.hour == Day_End_Hour && time.min == Day_End_Min) { //Start decreasing brightness ChangeState(DAY_NIGHT_TRANSITION); } break; case NIGHT_STATE: currentLevel = nightLevel; if (time.hour == Day_Start_Hour && time.min == Day_Start_Min) { //Start increasing brightness ChangeState(NIGHT_DAY_TRANSITION); } break; } //Set the current brightness on the leds analogWrite(LED_PWM_PIN, PWMLut[currentLevel]); }</p><p>void DrawSettingsUI() { lcd.home(); // Print our menu on the LCD lcd.setCursor(0,1); //Start at character 0 on line 1 lcd.print(" DAG -- NACHT "); char timerString[20]; sprintf(timerString, " %02d:%02d || %02d:%02d ",Day_Start_Hour, Day_Start_Min, Day_End_Hour,Day_End_Min); lcd.setCursor(0,2); //Start at character 0 on line 1 lcd.print(timerString); char levelString[20]; sprintf(levelString, " %02d%% || %02d%% ",(int)Day_Level, (int)Night_Level); lcd.setCursor(0,3); //Start at character 0 on line 1 lcd.print(levelString); }</p><p>void DrawDayNightUI() { // Print the dimming information on the lcd lcd.setCursor(0,1); //Start at character 0 on line 1 lcd.print("---- STATUS ----"); lcd.setCursor(0,2); //Start at character 0 on line 2 int brightness = 0; switch (currentState) { case DAY_STATE: lcd.print("MODUS: DAG"); brightness = Day_Level; break; case NIGHT_STATE: lcd.print("MODUS: NACHT"); brightness = Night_Level; break; } lcd.setCursor(0,3); //Start at character 0 on line 3 char levelString[17]; sprintf(levelString, "Intensiteit: %02d%%",brightness); lcd.print(levelString); }</p><p>void DrawDimmingUI(int level) { int brightness = (int)LutLevelToPercentage(level); // Print the dimming information on the lcd lcd.setCursor(0,1); //Start at character 0 on line 1 lcd.print("---- DIMMEN ----"); lcd.setCursor(0,2); //Start at character 0 on line 2 lcd.print(" "); lcd.setCursor(0,3); //Start at character 0 on line 3 char levelString[17]; sprintf(levelString, "Intensiteit: %02d%%",brightness); lcd.print(levelString); }</p><p>void DrawStartupUI() { // Print the dimming information on the lcd lcd.setCursor(0,1); //Start at character 0 on line 1 lcd.print(" Wim's "); lcd.setCursor(0,2); //Start at character 0 on line 2 lcd.print(" Vogelparadijs "); lcd.setCursor(0,3); //Start at character 0 on line 3 lcd.print(" Versie: v1.0b "); }</p><p>boolean CheckIsDay() { //If called the second time, transit to the correct state Time time = rtc.getTime(); if ((time.hour > Day_Start_Hour && time.hour < Day_End_Hour) || (time.hour == Day_Start_Hour && time.min >= Day_Start_Min) || (time.hour == Day_End_Hour && time.min < Day_End_Min)) { return true; } { return false; } }</p><p>void startup(Task* me) { if (startupCalled) { //If called the second time, transit to the correct state Time time = rtc.getTime(); if (CheckIsDay()) { ChangeState(DAY_STATE); } else { ChangeState(NIGHT_STATE); } } else { startupCalled = true; DrawStartupUI(); } }</p><p>void dayToNightTransition(Task* me) { boolean finished = ChangeLedBrightness(nightLevel, -1); if (finished) { ChangeState(NIGHT_STATE); } }</p><p>void nightToDayTransition(Task* me) { boolean finished = ChangeLedBrightness(dayLevel, 1); if (finished) { ChangeState(DAY_STATE); } }</p><p>boolean ChangeLedBrightness(int finishedLevel, int ledFadeAmount) { DrawDimmingUI(ledBrightnessLevel); // set the current brightness analogWrite(LED_PWM_PIN, PWMLut[ledBrightnessLevel]); </p><p> // change the brightness for next time through the loop: ledBrightnessLevel = ledBrightnessLevel + ledFadeAmount;</p><p> //Check if dimming is finished if ((ledFadeAmount > 0 && ledBrightnessLevel >= finishedLevel) || (ledFadeAmount < 0 && ledBrightnessLevel <= finishedLevel) ) { //Finished! return true; } //Not yet finished return false; }</p>

Step 10: TODO

Like with most projects, there are some items left on the TODO list:

  • The rotary encoder does not control anything yet
    • It is connected, but I did not yet have time to write a menu structure to allow changing the configuration.
    • I want to replace the experimentation board with a nice PCB
      • All the wiring and the many solder points makes me a bit nervous about the reliability
      • And I never designed or ordered a PCB before, so it will be a nice new experiment!


In case of any updates, I'll post them on this instructable.

Full Spectrum Laser Contest

Participated in the
Full Spectrum Laser Contest

Makerlympics Contest

Participated in the
Makerlympics Contest

123D Circuits Contest

Participated in the
123D Circuits Contest