Arduino Aquarium LED Controller

10,370

71

34

About: Engineering!

The idea behind this device is to automate my aquarium light. By automation I mean not only switching light on/off but simulating a sun raise/sunset.

The project is build around arduino nano, set of white led( cold and warm), set of RGB leds and a DS3231 real time clock.

The control is possible thanks to rotary encoder with a push button on its axis and a I2C HD44780 16X2 LCD.

This project can be build for every applications that needs to simulate sunrise/sunset daylight, colors etc.

For example you can use it in you terrarium, vivarium or just in the corner of a dark room above the plants.

Supplies:

Step 1: The Schematics

The circuit is divided to few blocks.

  • MCU - Arduino Nano 3.0 Atmega328 Ch340
  • Real time clock module. It is based on DS3231 chip and it looks like the attached picture. I tried to draw the full schematic, but if possible use prebuild module. It's easier.
  • DC-DC converters - this are not necessary. I have a DC24V bus at home and I want to regulate the current through the LEDs (white and RGB) separately. The white leds that I use(3 in series) need ~ 9-10V, but the RGB(3 in series per color) need ~ 11-12V. Yes I can put a resistor, but I decided to be this way cause I want to avoid using resistors due to heat issue.
  • Rotary encoder is ED1112S-20 and I use the button on its axis.
  • LCD is 16X2 HD44780 but here, like the time keeping module I took it prebuild with the I2C circuit soldered to it.
  • There is 5 LED channels - Warm White, Cold White, Red, Green and Blue. Each channel is controlled by different PWM output of arduino. For the MOSFETs I use IRLU024N - logic gate activated mosfet, but again you can use a different ones.
  • LEDs - I used pre-fabricated modules. The whites are from cree and the RBGs are from Edison (left over from old projects). Both of them are 3 leds per color connected in series. If you are using your own leds, please consider putting current limiting resistors in series of the leds.

Step 2: The Source Code and Algorithm

First of all I used this example to find the address of my time keeping module. In my case it is on 0x68 and the LCD is on 0x27.

The time algorithm works like this : all events are count on steps and one step is one minute starting from midnight 00:00. For example 08:30 is calculated like 8*60mins + 30min = 510.

Menu 0: it rotates in every 3 sec : Current time, current white leds pwm, current RGB pwm.

Menu 1: setting sunrise, sunset and steps

Setting sunrise : setting the sunrise time.

Setting sunset : setting the sunset time. The time here should be bigger than the sunrise time.

Setting steps : Ok...Lets give an examples. Lets say sunrise is in 08:00, sunset in 18:00 and steps are set to 60. This means that in 08:00 the PWM of the white leds will be switched on with value 1 and will gradually increase until in 09:00 ( 60min ) reach 254 ( max values is 254 ). Same is with the sunset. In 17:00 the white pwm will start to gradually decrease, so in 18:00 the white pwm will be 0 i.e. no light. Steps can be set up from 0 - no transition to 254 - it will take more than 4 hours to reach max intensity of the white leds. The PWM is changing on linear basis. I was thinking for logarithmic way but I left it like this( to save memory :) ).

Menu 2: Day Light and offset

Setting the cold white : here you can manually set up the value of the cold white leds. For example everything from 0 to 254 is accepted as manual value. If the value is 255 - this is accepted as auto mode and the light intensity will be modulated based on sunrise/sunset/step parameters described above.

Setting the warm white : same as the cold white from 0 to 254, 255 is automatic.

Setting the offset : the value of the offset can be chosen in the range -127:+127. For example if you set the cold leds to 255(auto), warm leds to 255 (auto) and the offset to -50 this means that the difference between cold and warm led intensity will be cold_pwm = |warm - 50 |. If offset is +50 this means that warm_pwm = |cold - 50|. The idea here is if you still want to have auto mode, but change the color temperature of the white light...In some cases I want the warm light to be stronger and to give more warm colors to the plants, fish...etc ( I have shrimps only ).

Menu 3: Set RGB

Setting R : setting the red color. Like above 0-254 means manual and the red channel will remains on the selected value until changed. 255 - auto mode.

Setting G : setting the green as above

Setting B : setting the blue as above.

Menu 4 : RaiseSet RGB min

No this is a tricky :). To make a different kind of effects for sunrise, sunset there are 3 settings that have meaning again on steps or minutes. All of them are from 0 to 255 and 255 doesn't mean auto. It's just 255.

Look at the attached graph. Sunrise is set for 07:30 (450min from midnight), Sunset is 19:30 (1170min) and the steps are 90 min. and...remember another value 64!

To create a color effect of sunrise/sunset my idea was to say lets start increasing the red first, then the green then the blue...and increase them in such a way that that when the main white light reach 64, all RGB to remains constant at 64 so not to change dramatically the color temperature, pre-selected with the white leds and offset.

So in the example shown...White leds will be at PWM=64 in 08:22 (sunrise 08:00 with gradually steps 90) and the RED is set here to 90, this means that the red will start PWM = 1 in around 06:00. In 06:10 red PWM =5, in 6:20 PWM = 11 and so on. and in 08:22 the red will be at PWM = 64, equal to the white led pwm and will stay there until the white led PWM doesn't fall below 64. After Sunset the picture repeats but this time its mirrored...starting from 64 for the RED PWM and taking 90mins to go to 0. It is same for the rest of the colors.

By changing those 3 values in this menu it is possible to simulate a different color and duration of sunrise/sunset.

Attached here is the excel file, to better understand the principle. You have to change only the green fields

In the graph - on the X axis is the time in minutes from midnight. On the Y axis is the PWM value of each channel. Here cold and warm whites are with offset 0 that is why they are overlapped, but the values for the R,G,B in RaiseSet RGB menu are clearly visible.

To set up the date/time you have to press and hold the rotary encoder axis during power on. Release it after power on. The first value is for seconds, then press the axis again and set up the minutes, hours, day of the week[1-Monday, 7-Sunday], month, last 2 digits of the year.

All parameters are stored in EEPROM, so you can disconnect the power any time, re-connect it and if the time keeping circuit has a stend-by battery it will continue working as per the current time.

Step 3: Build, Testing and Future Updates

I build everything on a bread board(lazy lazy lazy...), cause it was just an temporary project...but actually like everything temporary, it's running for almost an year till now. The idea for this aquarium was to have something green self sustained in the bathroom. I was thinking only for plants, but then I took few shrimps.

One think that is not included here, but I planned to do it, is to simulate a lightning storm. The software is almost ready but I still can't find a time to implement it...it is working now so why to touch it(lazy again :) ). I was thinking to use AS3935 for that purpose, but we will see :)...

Second think is a library to set up your sunrise/sunset based on geographical coordinates so the sunrise/set to mimic the real light outside.

Currently only 1/3 of the arduino nano memory is used, so there is a possibility for a wide variety of extras.

It is easy to add a Humidity/Temperature sensor so if you have a flowers (vivariums or something) to monitor the humidity and switch on/off a humidifier or something similar.

What else...o yep...moonlight simulation, but may be in the future with another menu - UPDATE - Jandy171 already added this future. Find details here .

I am gathering ideas for DIY automatic fish feeder :), so finally I can be 100% lazy :).

2 People Made This Project!

Recommendations

  • Party Challenge

    Party Challenge
  • Woodworking Contest

    Woodworking Contest
  • Arduino Contest 2019

    Arduino Contest 2019

34 Discussions

0
None
Em_JE

Question 18 days ago

can i sell If i build this project and i sell to people for they reef tank or freshwater ?

1 answer
0
None
stynabgEm_JE

Answer 15 days ago

No! It is for non commercial purposes only! If you want to sell it, develop your own.

0
None
waynerob11

Question 4 weeks ago on Step 2

This code compiles but with multiple errors - whats wrong with it????
[code]
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <EEPROM.h>
//=============used defines
#define DS3231_I2C_ADDRESS 0x68
#define enc_a 2
#define enc_b 3
#define enc_c 8
#define ledc 5
#define ledw 6
#define ledr 9
#define ledg 10
#define ledb 11
#define refresh_rate1 100
#define menu_time_out 10000 //kogato si wlqzal w menu da redaktirash neshto i do 10 ne e pipnat enkodera ili butona da izliza ot towa menu.
#define max_lcd_status2show 3
#define rotate_lcd_status2show 3
#define max_menu_item 4 //max number 0,1,2 = 2
//====================VARS===================
byte buttons_status;
unsigned long buttons_millis;
int i;
byte lcd_status2show, item, subitem;
volatile byte enc_status, enc_dir, enc_but, last_enc;
unsigned long mil_sec, mil_min, mil_sec_old, mil_min_old; //time keeping
unsigned int current_time_in_min, sun_rise_in_min, sun_set_in_min, sun_set_target_in_min, sun_rise_target_in_min;
byte current_time[2], sun_rs_step[5], divstep;
byte pwm[5], upwm[5], target_pwm; //to load it from EEPROM ? or to calc it
char d_offset; //tuk +-127 zapiswa kakwa e razlikata m/u topla i studena dnewna swetlina ako e na awtomatichno
byte red_hue[3];
//======================INSTANCE==============
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);

void setup()
{
//byte addr;
Serial.begin(9600);
pinMode(enc_a, INPUT_PULLUP); pinMode(enc_b, INPUT_PULLUP); pinMode(enc_c, INPUT_PULLUP);
pinMode(ledc, OUTPUT); pinMode(ledw, OUTPUT);
last_enc=3;
delay(100); //wait 100mS for pull ups to be setup, and don't cause any sudden interrupt
enc_status=0x00;enc_dir=0;enc_but=0;
attachInterrupt(digitalPinToInterrupt(enc_a), read_enc, CHANGE);
attachInterrupt(digitalPinToInterrupt(enc_b), read_enc, CHANGE);

//i=0;
lcd.begin(16, 2);lcd.clear();lcd.backlight();lcd.setCursor(0, 0); //(Col, Row);
lcd_status2show = 0;item = 0;subitem=0;
mil_sec = 0; mil_min = 0; mil_sec_old = 0; mil_min_old = 0;
//upwm EEPROM addr [0:4], ds_offset[5]
//sur_rs_step EEPROM addr [10:14]
for(byte addr=0;addr<=4;addr++) { upwm[addr] = EEPROM.read(addr); }
d_offset = EEPROM.read(5);
for(byte addr=6;addr<=8;addr++) { red_hue[addr-6] = EEPROM.read(addr); }
for(byte addr=10;addr<=14;addr++) { sun_rs_step[addr-10] = EEPROM.read(addr);}

sun_rise_in_min = sun_rs_step[0]*60+sun_rs_step[1];
sun_set_in_min = sun_rs_step[2]*60+sun_rs_step[3];
sun_set_target_in_min = sun_set_in_min - sun_rs_step[4]; //wremeto koeto trqbwa da pochne da namalq
sun_rise_target_in_min = sun_rise_in_min +sun_rs_step[4];
divstep = 255/sun_rs_step[4];
readDS3231hm(&current_time[1], &current_time[0]);
current_time_in_min = (current_time[0] *60) + current_time[1];
event_processor();
if(digitalRead(enc_c) == LOW){setupTime();}
print_menu_item();
}
void loop()
{
//int temp_calc1, temp_calc2;
//byte addr;
//tick a second
if(millis() > mil_sec_old )
{
mil_sec++;
mil_sec_old = millis() + 1000;
if((mil_sec % rotate_lcd_status2show) == 0 && item==0)
{
lcd_status2show = (lcd_status2show+1) % max_lcd_status2show;
if(item==0){print_menu_item();}
}
//rotate every rotate_lcd_status2show sec
//displayTime();
}
//tick a minute
if(millis() > mil_min_old)
{
mil_min++;
mil_min_old = millis()+60000;
readDS3231hm(&current_time[1], &current_time[0]);
print_menu_item();
current_time_in_min = (current_time[0] *60) + current_time[1];
event_processor();
}

read_but();
if(item<128 && enc_dir!=0 ){
switch(enc_dir)
{
case 1:if(item == 0){item=max_menu_item;} else{item--;}break;
case 2:if(item == max_menu_item){item=0;} else{item++;}break;
}
enc_dir=0;print_menu_item();
}
if(item<128 && enc_but==1 && item!=0)
{
//sub items change
item = item + 128;
enc_but=0;
//Serial.print("In Menu\n");
}

if(item>128) //casue 128 ne moje da e, pyrwoto meniu nqma opciq za buton
{
switch(item-128)
{
case 1:
while(enc_but!=1)
{
if(enc_dir == 1){enc_dir =0;if(sun_rs_step[0] != 0){sun_rs_step[0]--;}else{sun_rs_step[0]=23;}print_menu_item();}
if(enc_dir == 2){enc_dir =0; if(sun_rs_step[0] != 23){sun_rs_step[0]++;} else{sun_rs_step[0]=0;} print_menu_item();}
read_but();
}
enc_but=0;

while(enc_but!=1)
{
if(enc_dir == 1){enc_dir =0; if(sun_rs_step[1] != 0) {sun_rs_step[1]--;} else{sun_rs_step[1]=59;}print_menu_item();}
if(enc_dir == 2){enc_dir =0; if(sun_rs_step[1] != 59){sun_rs_step[1]++;} else{sun_rs_step[1]=0;} print_menu_item();}
read_but();
}
enc_but=0;
while(enc_but!=1)
{
if(enc_dir == 1){enc_dir =0; if(sun_rs_step[2] != 0) {sun_rs_step[2]--;} else{sun_rs_step[2]=23;}print_menu_item();}
if(enc_dir == 2){enc_dir =0; if(sun_rs_step[2] != 23){sun_rs_step[2]++;} else{sun_rs_step[2]=0;} print_menu_item();}
read_but();
}
enc_but=0;
while(enc_but!=1)
{
if(enc_dir == 1){enc_dir =0; if(sun_rs_step[3] != 0) {sun_rs_step[3]--;} else{sun_rs_step[3]=59;}print_menu_item();}
if(enc_dir == 2){enc_dir =0; if(sun_rs_step[3] != 59){sun_rs_step[3]++;} else{sun_rs_step[3]=0;} print_menu_item();}
read_but();
}
enc_but=0;
while(enc_but!=1)
{
if(enc_dir == 1){enc_dir =0; sun_rs_step[4]--;print_menu_item();}
if(enc_dir == 2){enc_dir =0; sun_rs_step[4]++;print_menu_item();}
read_but();
}
enc_but=0;
//to add steps
//temp_calc1 = (sun_rs_step[0] * 60) + (sun_rs_step[1]);
//temp_calc2 = (sun_rs_step[2] * 60) + (sun_rs_step[3]);

if( ((sun_rs_step[2]*60) + (sun_rs_step[3])) >= ((sun_rs_step[0]*60) + (sun_rs_step[1])) )
{

for(byte addr=10;addr<=14;addr++){EEPROM.update(addr, sun_rs_step[addr-10]);}
}
else{
for(byte addr=10;addr<=14;addr++){ sun_rs_step[addr-10] = EEPROM.read(addr);}}
sun_rise_in_min = sun_rs_step[0]*60+sun_rs_step[1];
sun_set_in_min = sun_rs_step[2]*60+sun_rs_step[3];
sun_set_target_in_min = sun_set_in_min - sun_rs_step[4]; //wremeto koeto trqbwa da pochne da namalq
sun_rise_target_in_min = sun_rise_in_min +sun_rs_step[4];
divstep = 255/sun_rs_step[4];
event_processor();

item = item - 128;
break;
case 2:
while(enc_but!=1)
{
if(enc_dir == 1){enc_dir =0;upwm[0]--;print_menu_item();update_pwm();}
if(enc_dir == 2){enc_dir =0;upwm[0]++;print_menu_item();update_pwm();}
read_but();
}
enc_but=0;
while(enc_but!=1)
{
if(enc_dir == 1){enc_dir =0;upwm[1]--;print_menu_item();update_pwm();}
if(enc_dir == 2){enc_dir =0;upwm[1]++;print_menu_item();update_pwm();}
read_but();
}
enc_but=0;
while(enc_but!=1)
{
if(enc_dir == 1){enc_dir =0;d_offset--;print_menu_item();}
if(enc_dir == 2){enc_dir =0;d_offset++;print_menu_item();}
read_but();
}
enc_but=0;
for(byte addr=0;addr<=1;addr++){EEPROM.update(addr,upwm[addr]);}
EEPROM.update(5, d_offset);
event_processor();
item = item - 128;
break;
case 3:
while(enc_but!=1)
{
if(enc_dir == 1){enc_dir =0;upwm[2]--;print_menu_item();update_pwm();}
if(enc_dir == 2){enc_dir =0;upwm[2]++;print_menu_item();update_pwm();}
read_but();
}
enc_but=0;
while(enc_but!=1)
{
if(enc_dir == 1){enc_dir =0;upwm[3]--;print_menu_item();update_pwm();}
if(enc_dir == 2){enc_dir =0;upwm[3]++;print_menu_item();update_pwm();}
read_but();
}
enc_but=0;
while(enc_but!=1)
{
if(enc_dir == 1){enc_dir =0;upwm[4]--;print_menu_item();update_pwm();}
if(enc_dir == 2){enc_dir =0;upwm[4]++;print_menu_item();update_pwm();}
read_but();
}
enc_but=0;
for(byte addr=2;addr<=4;addr++){EEPROM.update(addr,upwm[addr]);}
event_processor();
item = item - 128;
break;
case 4:
while(enc_but!=1)
{
if(enc_dir == 1){enc_dir =0;red_hue[0]--;print_menu_item();}
if(enc_dir == 2){enc_dir =0;red_hue[0]++;print_menu_item();}
read_but();
}
enc_but=0;
while(enc_but!=1)
{
if(enc_dir == 1){enc_dir =0;red_hue[1]--;print_menu_item();}
if(enc_dir == 2){enc_dir =0;red_hue[1]++;print_menu_item();}
read_but();
}
enc_but=0;
while(enc_but!=1)
{
if(enc_dir == 1){enc_dir =0;red_hue[2]--;print_menu_item();}
if(enc_dir == 2){enc_dir =0;red_hue[2]++;print_menu_item();}
read_but();
}
enc_but=0;
for(byte addr=6;addr<=8;addr++){EEPROM.update(addr,red_hue[addr-6]);}
event_processor();
item = item - 128;

break;
}
}
//if(enc_but==1){ enc_but=0; i = 0; lcd.clear();lcd.print(i, DEC);displayTime();}
}
//=========================================================================================================================================================================================================
//=========================================================================================================================================================================================================
/* ENCODER ROUTINES
* CW d135 CCW d75 = 1/1 4/0 18/2 75/3
* =2/2 8/0 33/1 135/3
* ---------------------------
* 11 11
* 10 10 10
* 00 00 00
* 01 01 01
* 11 11
*/
//BUTTON INTERRUPT
void read_but()
{
bool pin_status;
delay(3);
pin_status=digitalRead(enc_c);
if(pin_status == LOW && enc_but !=128) {enc_but = 128;}
if(pin_status == HIGH && enc_but ==128){enc_but=1;}
}
void read_enc()
{
//returns 0 if nothing is set
//returns 1 if A is set but B is not
byte a=0;
delayMicroseconds(200);
if(digitalRead(enc_a) == HIGH ){a=B01;}
if(digitalRead(enc_b) == HIGH ){a=a+2;} //set the second bit
if(last_enc!=a)
{
last_enc = a;
enc_status = enc_status*4;
enc_status = enc_status + a;
//Serial.print(enc_status, DEC);Serial.print("/");Serial.print(a, DEC);Serial.print(" ");
switch(enc_status)
{
case 0x4b:enc_dir=1;enc_status=0;/*Serial.print("\n");*/break; //ccw, enc_dir=1
case 0x87:enc_dir=2;enc_status=0;/*Serial.print("\n");*/break; //cw enc_dir=2
default : enc_dir =0;break;
}
}
}
//=========================================================================================================================================================================================================
//=========================================================================================================================================================================================================
// Convert normal decimal numbers to binary coded decimal
byte decToBcd(byte val)
{
return( (val/10*16) + (val%10) );
}
// Convert binary coded decimal to normal decimal numbers
byte bcdToDec(byte val)
{
return( (val/16*10) + (val%16) );
}
/*void setDS3231time(byte second, byte minute, byte hour, byte dayOfWeek, byte dayOfMonth, byte month, byte year)
{
// sets time and date data to DS3231
Wire.beginTransmission(DS3231_I2C_ADDRESS);
Wire.write(0); // set next input to start at the seconds register
Wire.write(decToBcd(second)); // set seconds
Wire.write(decToBcd(minute)); // set minutes
Wire.write(decToBcd(hour)); // set hours
Wire.write(decToBcd(dayOfWeek)); // set day of week (1=Sunday, 7=Saturday)
Wire.write(decToBcd(dayOfMonth)); // set date (1 to 31)
Wire.write(decToBcd(month)); // set month
Wire.write(decToBcd(year)); // set year (0 to 99)
Wire.endTransmission();
}*/
void setupTime()
{//byte second, byte minute, byte hour, byte dayOfWeek, byte dayOfMonth, byte month, byte year
byte set_time[6];
while(digitalRead(enc_c) == LOW );
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("PAR:");
read_but();
for(byte i=0;i<7;i++){set_time[i] = 15;}
for(byte i=0;i<7;i++)
{
lcd.setCursor(4, 0);lcd.print(i,DEC);
lcd.setCursor(0, 1);lcd.print(" ");lcd.setCursor(0, 1);lcd.print(set_time[i],DEC);
while(enc_but!=1)
{
if(enc_dir == 1){enc_dir =0;set_time[i]--;lcd.setCursor(0, 1);lcd.print(" ");lcd.setCursor(0, 1);lcd.print(set_time[i],DEC);}
if(enc_dir == 2){enc_dir =0;set_time[i]++;lcd.setCursor(0, 1);lcd.print(" ");lcd.setCursor(0, 1);lcd.print(set_time[i],DEC);}
read_but();
}
enc_but=0;
}
Wire.beginTransmission(DS3231_I2C_ADDRESS);
Wire.write(0); // set next input to start at the seconds register
for(byte i=0;i<7;i++)
{
Wire.write(decToBcd(set_time[i]));
}
Wire.endTransmission();
lcd.clear();
lcd.print("Done");
delay(2000); //wait 2 sec

}
void readDS3231hm(byte *minute, byte *hour)
{
Wire.beginTransmission(DS3231_I2C_ADDRESS);
Wire.write(1); // set DS3231 register pointer to 01h
Wire.endTransmission();
Wire.requestFrom(DS3231_I2C_ADDRESS, 2);
*minute = bcdToDec(Wire.read());
*hour = bcdToDec(Wire.read() & 0x3f);
}
void readDS3231time(byte *second,byte *minute,byte *hour,byte *dayOfWeek,byte *dayOfMonth,byte *month,byte *year)
{
Wire.beginTransmission(DS3231_I2C_ADDRESS);
Wire.write(0); // set DS3231 register pointer to 00h
Wire.endTransmission();
Wire.requestFrom(DS3231_I2C_ADDRESS, 7);
// request seven bytes of data from DS3231 starting from register 00h
*second = bcdToDec(Wire.read() & 0x7f);
*minute = bcdToDec(Wire.read());
*hour = bcdToDec(Wire.read() & 0x3f);
*dayOfWeek = bcdToDec(Wire.read());
*dayOfMonth = bcdToDec(Wire.read());
*month = bcdToDec(Wire.read());
*year = bcdToDec(Wire.read());
}
void displayTime()
{
byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;
// retrieve data from DS3231
readDS3231time(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month,
&year);
// send it to the serial monitor
Serial.print(hour, DEC);
// convert the byte variable to a decimal number when displayed
Serial.print(":");
if (minute<10)
{
Serial.print("0");
}
Serial.print(minute, DEC);
Serial.print(":");
if (second<10)
{
Serial.print("0");
}
Serial.print(second, DEC);
Serial.print(" ");
Serial.print(dayOfMonth, DEC);
Serial.print("/");
Serial.print(month, DEC);
Serial.print("/");
Serial.print(year, DEC);
Serial.print(" Day of week: ");
switch(dayOfWeek){
case 1:
Serial.println("Sunday");
break;
case 2:
Serial.println("Monday");
break;
case 3:
Serial.println("Tuesday");
break;
case 4:
Serial.println("Wednesday");
break;
case 5:
Serial.println("Thursday");
break;
case 6:
Serial.println("Friday");
break;
case 7:
Serial.println("Saturday");
break;
}
}
//=========================================================================================================================================================================================================
//=========================================================================================================================================================================================================
void print_menu_item()
{
byte local_item;
lcd.clear();
lcd.setCursor(0, 0);
if(item >128) {local_item = item - 128 ; lcd.cursor(); }
else{lcd.noCursor();local_item = item;}
switch(local_item)
{
case 0:
switch(lcd_status2show)
{
case 0:lcd.print("#Time :");break;
case 1:lcd.print("#Day Light PWM");break;
case 2:lcd.print("#RGB PWM");break;
}
lcd.setCursor(0, 1); //(Col, Row);
switch(lcd_status2show)
{
case 0:lcd.print(current_time[0], DEC);lcd.print(":");if(current_time[1] < 10 ) { lcd.print("0");}lcd.print(current_time[1]);break;
case 1:lcd.print(pwm[0], DEC); lcd.print(" "); lcd.print(pwm[1],DEC);break;
case 2:lcd.print(pwm[2], DEC); lcd.print(" "); lcd.print(pwm[3],DEC);lcd.print(" "); lcd.print(pwm[4],DEC);break;
}
break;
case 1:lcd.print("Set Sun R/S/Step");
lcd.setCursor(0,1); lcd.print(sun_rs_step[0], DEC);lcd.print(":"); if(sun_rs_step[1] <10) {lcd.print("0");}lcd.print(sun_rs_step[1],DEC);lcd.print(" "); lcd.print(sun_rs_step[2],DEC);lcd.print(":");if(sun_rs_step[3] <10) {lcd.print("0");}lcd.print(sun_rs_step[3],DEC);lcd.print(" ");lcd.print(sun_rs_step[4],DEC);
break;
case 2:
lcd.print("Day Light&offset");
lcd.setCursor(0,1); lcd.print(upwm[0], DEC);lcd.print(" "); lcd.print(upwm[1],DEC);lcd.print(" "); lcd.print(d_offset,DEC);
break;
case 3:
lcd.print("Set RGB");
lcd.setCursor(0,1); lcd.print(upwm[2], DEC);lcd.print(" "); lcd.print(upwm[3],DEC);lcd.print(" "); lcd.print(upwm[4],DEC);
break;
case 4:
lcd.print("RiseSet RGB min");
lcd.setCursor(0,1); lcd.print(red_hue[0], DEC);lcd.print(" "); lcd.print(red_hue[1],DEC);lcd.print(" "); lcd.print(red_hue[2],DEC);
break;
/* case 5:
lcd.print("MoonLight"); // (min)(max)(step)
//lcd.print(preset_hhys, DEC);
break;
case 6:
lcd.print("ThunderStorm"); //(intenzite)(prodyljitelnosti)(colorset)
//lcd.print(preset_hhys, DEC);
break;
case 7:
lcd.print("MAN HEAT ON/OFF");
//lcd.print(preset_hhys, DEC);
break;
case 8:
lcd.print("FAN RUN EVRY:");
lcd.print(fan_run_on, DEC);
break;
case 9:
lcd.print("FAN ON TIME:");
lcd.print(fan_on_time, DEC);
break;*/
}
}
void event_processor()
{
//unsigned long millis_old = micros();
unsigned long millis_new;
//http://www.dma.fi.upm.es/recursos/aplicaciones/logica_borrosa/web/fuzzy_inferencia/funpert_en.htm
if(current_time_in_min < sun_rise_in_min || current_time_in_min > sun_set_in_min){target_pwm = 0;}
if(current_time_in_min >=sun_rise_in_min && current_time_in_min <= sun_rise_target_in_min){target_pwm = (current_time_in_min - sun_rise_in_min)*divstep;}
if(current_time_in_min > sun_rise_target_in_min && current_time_in_min < sun_set_target_in_min ){target_pwm = 255;}
if(current_time_in_min >= sun_set_target_in_min && current_time_in_min <= sun_set_in_min ){ target_pwm = 255-((current_time_in_min - sun_set_target_in_min)*divstep);}
//sun_rs_step[4] - kolko wreme da otnema do maksimalnata stojnost na oswetlenieto
//Trygwa pyrwo studena
if(d_offset<0){ pwm[0] = target_pwm; if( (target_pwm + d_offset) >= 0 ) {pwm[1] = target_pwm + d_offset;}else{pwm[1]=0;} }
//startira samo toplo
if(d_offset>0){ pwm[1] = target_pwm; if( (target_pwm - d_offset) >= 0 ) {pwm[0] = target_pwm - d_offset;}else{pwm[0]=0;} }
//startira i dwete ednowremenno
if(d_offset==0){pwm[0]=target_pwm;pwm[1]=target_pwm; }
//
for(byte i=0; i<=2;i++)
{
if(upwm[i+2] == 255 ){ pwm[i+2]= calc_rgb_sun_rs(red_hue[i]);}
}
Serial.print(pwm[0],DEC);Serial.print(" ");
Serial.print(pwm[1],DEC);Serial.print(" ");
Serial.print(pwm[2],DEC);Serial.print(" ");
Serial.print(pwm[3],DEC);Serial.print(" ");
Serial.print(pwm[4],DEC);Serial.print(" ");
Serial.print(red_hue[0],DEC);Serial.print(" ");
Serial.print(red_hue[1],DEC);Serial.print(" ");
Serial.print(red_hue[2],DEC);Serial.print(" ");
Serial.print(target_pwm,DEC);Serial.print(" ");
Serial.print(sun_rise_in_min,DEC);Serial.print(" ");
Serial.print(sun_rise_target_in_min,DEC);Serial.print(" ");
Serial.print(sun_set_target_in_min, DEC);Serial.print(" ");
Serial.print(sun_set_in_min,DEC);Serial.print(" ");
Serial.print(current_time_in_min,DEC);Serial.print(" ");
Serial.print(current_time[0],DEC);Serial.print(" ");
Serial.print(current_time[1],DEC);Serial.print("\n");
update_pwm();
//millis_new = micros();
// Serial.println(millis_new-millis_old, DEC);

}
byte calc_rgb_sun_rs(byte value)
{
if(current_time_in_min < (sun_rise_in_min - value) || current_time_in_min > (sun_set_in_min + value) ) {return 0;}
if(current_time_in_min >= (sun_rise_in_min - value ) && target_pwm <=64 && current_time_in_min <= sun_rise_target_in_min ){ return (255*(current_time_in_min-sun_rise_in_min+value))/(sun_rs_step[4]+4*value);}
if(target_pwm > 64 ) { return 64;}
if(current_time_in_min >= sun_set_target_in_min && target_pwm <64 && current_time_in_min <= (sun_set_in_min + value) ) { return 64- ((255*(current_time_in_min-sun_set_in_min +(sun_rs_step[4]/4)))/(sun_rs_step[4]+4*value));}
//64 - ((255*(current_time_in_min-sun_set_in_min +(sun_rs_step[4]/4)))/(sun_rs_step[4]+4*value))
//(64-(255*(current_time_in_min-sun_set_in_min + (sun_rs_step[4]/4)))/(sun_rs_step[4]+4*value))
}
void update_pwm()
{
for(byte i=0; i<=4;i++)
{
if(upwm[i]!=255){ pwm[i] = upwm[i];}
}
set_pwm();
}
void set_pwm()
{
analogWrite(ledc, pwm[0]);analogWrite(ledw, pwm[1]);analogWrite(ledr, pwm[2]);analogWrite(ledg, pwm[3]);analogWrite(ledb, pwm[4]);
}
[/code]
0
None
This_dude_553

Question 4 months ago

would it be possible to have an extra menu to change the target pwm of rgb? this would add th possibility of changing the daylight colour, and would enable the user to compensate for different leds/different amount of leds

3 answers
0
None
stynabgThis_dude_553

Answer 4 months ago

Hi, you can still do it with current software. In my case I used 2 day light channels "ledc" - cold white leds 6500k and "ledw"-warm white leds 3000k.
In the menu "Day Light&Offset" you can selec +- (offset) one of the white leds chanels to be stronger or weeker than the other chanel, when they are at max brightness(one of them is always not at maximum in case Offset !=0 ) and you can make the effect that you are looking for.

If you want to add menu, you can increase the #define max_menu_item 4 to "5" and add menu in the main loop after "if(item>128)" - here you set up what varaibale to be changed and in "void print_menu_item()" you can add the text that you want to be printed. BTW in "void print_menu_item()" there are more than 4 menus defined, so you can edit the next available "case 5:" after you increase #define max_menu_item 4 to "5".

Finally if you want to change the max RGB value you need to replace "64" with a varaibale that you are going to set up in menu 5 in the main loop.

This is what I remember, since it was more than 2 years after I made this one. Try and see what will happen.

BR, Stani


0
None
lullaby88This_dude_553

Reply 3 months ago

Hey,
I managed to integrate the menu entry called "Daylight Colour", which is for setting up the a target RGB that is supposed to change for sunset /-rise. I applied the byte "target_pwm_set", which stores the value for the Daylight colour.
Now I need to add that to the 64-part mentioned above, which is probably this one:

byte calc_rgb_sun_rs(byte value)
{
if(current_time_in_min < (sun_rise_in_min - value) || current_time_in_min > (sun_set_in_min + value) ) {return 0;}
if(current_time_in_min >= (sun_rise_in_min - value ) && target_pwm <=64 && current_time_in_min <= sun_rise_target_in_min ){ return (255*(current_time_in_min-sun_rise_in_min+value))/(sun_rs_step[4]+4*value);}
if(target_pwm > 64 ) { return 64;}
if(current_time_in_min >= sun_set_target_in_min && target_pwm <64 && current_time_in_min <= (sun_set_in_min + value) ) { return 64- ((255*(current_time_in_min-sun_set_in_min +(sun_rs_step[4]/4)))/(sun_rs_step[4]+4*value));}
//64 - ((255*(current_time_in_min-sun_set_in_min +(sun_rs_step[4]/4)))/(sun_rs_step[4]+4*value))

Okay, so if I understand it correctly, it will drive the LEDs either to the value of 64 or 0.
Now I have three values for my "target_pwm_set", one for each colour. So I cannot simple change "64" to "target_pwm_set" but actually I would need a calculation for each colour, right?

Regards!

0
None
lullaby88

3 months ago

Hey,
first of all thank you for this great project and the lovely code!
I managed to set up my arduino and got the encoder and display and also the LED running.

I just have one problem: I do not know what resistors you used for the IRLU24N. I tried several ones and not all of them worked. Currently I have no resistors in front of the gate, only 220Ohm between the IRLU24 and the LEDs. This way I can control all the colours but I cannot switch off the LEDs. So even if I set sunset to an hour before, it will not get dark. This actually is the only problem I have...

I also tried 47R before the Gate and 100k between Gate and Ground. but this way the LEDs do not work at all.

So what should I do?

BTW, I am using a 5m LED-Strip RGBW, maybe that is a problem?
Anyways, thanks for your help! I have had a lot of fun working on the project so far!

2 replies
0
None
lullaby88lullaby88

Reply 3 months ago

Here's some news... I got rid of all the resistors and suddenly it works like Charm. The 220 Ohm between the IRLU and the LEDs just dimmed them anyways.
I just tested sunset and it worked. Is it normal that in night-mode only the white LEDs switch off and the RGB turns blue?
Can I somehow set the brightness/ colour of night mode?

0
None
lullaby88lullaby88

Reply 3 months ago

Okay, trial-and-error here... ;)
I tested the build without any resistors and it seems that this causes crashes for the Arduino. I have added 100 Ohm resistors between the PWM-Pins of the Arduino and the IRLU and now it seems to work perfectly. I added your moonlight build, too. So now I will build an enclosure and so on...

Btw, do you have any idea how add a weather/cloud simulation into the code?
As far as I understood, the cloud simulation is only a random change in the brightness of R, G or B that is triggered randomly. Any idea how to do this?

Cheers! It is a great project!

0
None
RodolfoM9

7 months ago

what version of arduino ide are you using?

I'm having a problem with the LiquidCrystal_I2C library

41: 46: Error: 'POSITIVE' not declared in this scopeLiquidCrystal_I2C lcd (0x27,2,1,0,4,5,6,7,3, POSITIVE);

1 reply
0
None
AndriusFRodolfoM9

Reply 7 months ago

I use Arduino IDE 1.8.6 version and

Newliquidcrystal_1.3.5 library. It compiles without errors. With other libraries had the same error.

0
None
ŁukaszF7

Question 8 months ago

it is possible to change the code for;

set time when should be the expecting time for end of the sunset or sunrise and the expecting power for light, not like it is now:

you have to set how many minutes will be the sunset or sunrise?

for example:

You set time to start sunrise and set power (start power for pwm) 7:00A.M 10%

you set expecting time for end the sunrise and expecting power for pwm 11:00A.M 40%

2 answers
0
None
stynabgŁukaszF7

Answer 8 months ago

Hi, In general I can give only guidelines, I will not change the code and send it to you, cause I am currently too busy to do it.

There is no such an option that you described, but you can code it if you want. The source is available, so you can change it as you like.

0
None
ŁukaszF7stynabg

Reply 8 months ago

so only one point. how to put the code for "stop time for the rise or for dimmer" the rest I can do it

0
None
RodolfoM9

8 months ago

Detalhesrduino_moonlightV2: 41: 46: erro: 'POSITIVO' não foi declarado neste escopo

LiquidCrystal_I2C lcd (0x27,2,1,0,4,5,6,7,3, POSITIVO);

^

Foram incluídas várias bibliotecas para "OneWire.h"

Usado: C: \ Usuários \ hollp \ Documents \ Arduino \ libraries \ OneWire

Não usado: C: \ Usuários \ hollp \ Documents \ Arduino \ libraries \ OneWire-master

Foram incluídas várias bibliotecas para "DallasTemperature.h"

Usado: C: \ Usuários \ hollp \ Documents \ Arduino \ libraries \ DallasTemperature-3.8.0

Não usado: C: \ Usuários \ hollp \ Documents \ Arduino \ libraries \ Arduino-Temperature-Control-Library-master

status de saída 1


What error is this and what I can not copy

1 reply
0
None
ŁukaszF7

8 months ago

The project id awesomee!!!

But one question.

Its possibile to add extra menu for example for the "rising sun" before sunrise And "dawn" before moon time?
Also with next change in the PWM parameters And time between.
How to change the code?

2 replies
0
None
stynabgŁukaszF7

Reply 8 months ago

Why do you need this menu? There is a simillar menu already in the project that will do the job.

In menu Set SUN R/S/Step you can select when you want you main LEDs to be at maximum and Step is how slowly to increase the brighntess of those leds.

If you want to change the color prior sunrise/sun set just slect "RiseSet RGB" min menu different values for the RGB channels.

0
None
ŁukaszF7stynabg

Reply 8 months ago

it will be nice to set another "setpoint" just that