Fading 2 LEDs in sequence with third LED lighting up at the end

Hello everyone

I am new to arduino and am trying to build a controller for a terrarium.

After a lot of research I started coding. I build this test setup on an Arduino Uno:

  • blue LED on pin 10 - represents the light
  • red LED on pin 11 - represents the heat
  • yellow LED on pin 12 - represents neon tubes

The idea is that the lampState will serve as gate if its time for the sun to rise lampState LOW to HIGH or for the sun to set HIGH to LOW. This will be combined with a RTC later on.

If its time for the sun to rise first the blue LED should start lighting up to 80% of the maximum (in the test the interval is set to 100 - it would be 1470 later on to have a fade of 5 minutes - may be even 10 minutes). after 5 seconds the red LED should start lighting up with the same interval. This is also for test purpose, later it will be 30 Seconds or a Minute.

At the end the lampState is set to HIGH and the yellow LED is set to HIGH.

The following problems occured:

  • if “if(lampState = LOW) {” is not commented out nothing works
  • the blue LED fades out after a while and the red LED stays on as it should

Can oneone please tell me how to adapt the code so:

  • the lampState LOW will work
  • so the blue LED will stay on

This is the code that gave me the best results so far:

Main Loop:

/*---Libraries---*/
/*#include <OneWire.h>
#include <DallasTemperature.h>
#include <DHT.h>
*/
/*---Pins---*/
#define DHT22AussenPin 2 // Temperatur und Luftfeuchtigkeit Umgebung
#define DHT22HotPin 3 // Luftfeuchtigkeit Seite HotSpot
#define DHT22CoolPin 4 // Luftfeuchtigkeit Seite CoolSpot
#define ledRed 11 // Simuliert Wärmelampe - Led rot um anstieg zu testen
#define ledBlue 10 // Simuliert Sonne
#define neon 12 // Neonröhren
#define ledGreen 13 // Simuliert Ventilator
#define ONE_WIRE_BUS 8 // DS18B20 Wire-bus an Pin 8

/*---Definitionen---*/
#define DHTTYPE DHT22 // welchen Sensor benutzen wir?

/*---Objekte---*/
/*DHT dhtAussen (DHT22AussenPin, DHTTYPE);
DHT dhtHot (DHT22HotPin, DHTTYPE);
DHT dhtCool (DHT22CoolPin, DHTTYPE);
*/

void setup() {
  // put your setup code here, to run once:

/*---Objekte starten---*/
/*dhtAussen.begin(); // DHT-22 Aussenraum starten
dhtHot.begin(); // DHT-22 HotSpot starten
dhtCool.begin(); // DHT-22 CoolSpot starten
*/
Serial.begin(9600); // for debugging

/*---Pinmodes---*/
pinMode (ledRed, OUTPUT); // Wärmelampe - ledRed ist ein Output
pinMode (ledBlue, OUTPUT); // Sonne - ledBlue ist ein Output
pinMode (neon, OUTPUT); // Neonröhren sind ein Output
pinMode (ledGreen, OUTPUT); // Ventilator ist ein Output

}

void loop() {

//Test_ds18b20(); // Temperatur vergleichen
//Test_dht22 ();
LightHeat_Test();
//Test_Temp ();
}

“LED Loop”:

// Licht und Wärme Test
void LightHeat_Test() {

/*---Variablen---*/
int i = 0; // PWM Variable
int j = 0; // PWM Heat Variable
//long fadeDelay = 1470; // Delay beim Eindimmen von 1470ms, um Eindimmen auf 5 Min zu verlängern.
unsigned long fadeSun = 0;
unsigned long fadeHeat = 0;
int progresSun = 0;
int progresHeat = 0;

/*---Test Wärmedelay---*/
long heatDelay = 5000;
unsigned long currentHeat = 0;
long previousHeat = 0;

/*---Testvariablen um RTC zu ersetzen---*/
long fadeDelay = 100;
long waitDelay = 10000; // 10 Sek warten
unsigned long wait = 0;

/*---States---*/
int lampState = LOW;

// BRAUCHT ES DIE SONNE UND DIE WÄRME ODER IST BEIDES IN EINEM?

/*---Test Licht Sonnenaufgang---*/
//if(lampState = LOW) { // Testlauf
//if(lampState = LOW && Zeit >= XXX && >= YYY) {
for(i = 0; i <= 204; i++) { // Licht bis 80% max PWM eindimmen
analogWrite(ledBlue, i);
progresSun = map(i, 0, 255, 0, 100); // Fortschritt drucken
Serial.print(progresSun);
Serial.println("% Sonne");
fadeSun = millis();
while (millis() < fadeSun + fadeDelay) {} // Verzögerung Eindimmen über 5 Min
//lampState = i;
}
/*---Test Wärme---*/
currentHeat = millis();
if(currentHeat - previousHeat > heatDelay){
for(j = 0; j <= 204; j++) { // Licht bis 80% max PWM eindimmen
analogWrite(ledRed, j);
progresHeat = map(j, 0, 255, 0, 100); // Fortschritt drucken
Serial.print(progresHeat);
Serial.println("% Hitze");
fadeHeat = millis();
while (millis() < fadeHeat + fadeDelay) {} // Verzögerung Eindimmen über 5 Min
previousHeat = currentHeat;
}
}
lampState = HIGH; // Die Sonne ist nun aufgegangen
digitalWrite(neon, HIGH);
wait = millis(); // Testlauf
while (millis() < wait + waitDelay) {} // Testlauf
}

Thank you for the help. May be someone could even give me an additional hint how to use arrays, objects or others on this code.

Cheers,

moses

if(lampState = LOW) { // Testlauf

Does not test the state of lampState, rather it sets it to zero

= for assignment
== for comparison

Hi UKHeliBob

Thank you for the reply!

Perfect! Now I understand why the State machine wouldn’t work.

Would you have an idea on why the blue LED fades out or turns off?

After those problems would be solved I would add the code to do the reverse. Is it possible to write

for(i <= 255, i >0 0, i--){

Is I dont know on what value i will be as I start?

Thank you!

moses

for(i <= 255, i >0 0, i--){would not work for many reasons:

  • you need “;” and not “,”
  • 0 0 does not mean anything
  • you have to initialize i: for (i = some value…

Hi lesept

Sorry tipo…

I meant:

for(i <= 255; i >= 0; i--)

But ok so the for loop can only be initialized with i = some thing?

Would any one have an idea on why the blue LED fades out after a short while? I meant to keep it on until the end of the waiting period.

I added the following to make sure the LEDs are turned off after the test. But it wont help either:

digitalWrite(ledRed, LOW);
digitalWrite(ledBlue, LOW);
digitalWrite(neon, LOW);

The red LED stays on while the blue led fades. Without an apparent reason for me to see.

Thank you,

moses

edit:
I guess there must be something wrong with the use of the milli() and the while and / or if functions or the placement of the curly braces. I have been moving them around and rewriting them and what not but I just dont see how I need to change it to make it function ok.

Perhaps a different approach?

I’ve used pin 5 as a lampState input. When high the statemachine simulates “sunrise.” When the timings are complete, it moves to “sunup.”

When lampState goes low the code enters “sunset” and fades the LEDs out and then turns off the neon. It then enters “sundown” and stays there until the lampState input goes high again. Shouldn’t be too hard to put the state changes in under RTC control.

I’ve used compressed timing values as you did to get the rise/set cycle time to a tolerable 1-minute or so for debug.

Too big to post so attached.

terrarium.ino (9.35 KB)

Hi Blackfin

Thank you very much for the big efforts. I guess I will have to take a moment to understand all the code. As I am quite new to Arduino and programming I will have to read up a bit on some bits (I wouldn't have been able to come up with something like this). It opens up new ideas and concepts! Thank you very much. I might have to get back to you later on may be...

Thanks again!

moses

Could you, Blackfin, or anyone else point out to me good ressources (specificaly arduino as well as C++ and may be C, see below) - be it an internetsite or a book - to get more into programming? As complete as possible. I am aware that there isnt just one ressource that will cover it all.

Is it enough just to get more into C++ or would it also be good or necessary to learn somethings about C as well?

Thank you everyone for the inputs!

Someone mentioned this guy's course on Youtube in a thread I came across recently. Apparently he is an actual professor and the series is quite good. I haven't actually watched it myself but it seems to cover a lot of important topics.

Paul McWhorter - Arduino Lessons

My advice would be to dream up things you want to build and just try them out. Learn on the fly. Try things first, then if you get stuck head off to google and if you can't find the answer then post in here.

hey Metallor

This sounds like a good concept. I thought it might be more efficient to become more fluent and knowledgeable first. But I find trial and error also a good way of doing things. As I am learning about structs now thanks to Blackfins input.

I might have a look at the youtube channel you menntioned additionally.

Cheers

moses

Hi everyone

I have studied the code Blackfin provided and after I have studied about struct I was able to understand most of it. Some questions remain how ever that I would like to post here:

  1. The following definitions seem to linked to the array. How are they included in die array and how are they addressed?
#define BLUE    0
#define RED     1
#define NEON    2

#define SUNRISE     0
#define SUNUP       1
#define SUNSET      2
#define SUNDOWN     3
  1. The definition of the members of the struct includes values, which dont seem to correspond to the commend or there seem to be two comments for the value. How am I to understand this?
.updatePeriod = 294,//2941,       //204 steps in 10 minutes == 2941 mS/step
       .initialPauseSS = 500,//5000,     //5-sec pause for sunset
  1. For what reason does part of the funtion to be in setup? The pinMode part is understandable. But the rest?

  2. How does lampState (pin 5) change? Does it have to be done manually? Is this because of the demonstration purpose?

  3. What exactely happens in the following statement? Why is “grLEDControl*.updatePeriod” added? Isn’t this to increase the fading? I couldn’t find it in the fading part of the code.*
    * *grLEDControl[i].timeDelay = grLEDControl[i].initialPauseSS + grLEDControl[i].updatePeriod;* *
    6. In the main loop ixdLED is defined and often linked to the struct. How does it connect to the struct to get information?
    * *switch( grLEDControl[idxLED].state )* *
    7. Why is it an advantage to put timeNow instead of millis() if millis () has just been attributed to timeNow (millis()-timeNow)?
    ```
    *timeNow = millis();

if( (timeNow - grLEDControl[idxLED].timerLED) >= grLEDControl[idxLED].timeDelay )
           {*
* *8. This seems to lead to the fade. But is it gradual over a longer period of time?* *
if( grLEDControl[idxLED].valPWM < grLEDControl[idxLED].maxPWM )
                   grLEDControl[idxLED].valPWM++;

* *9. What is this for?* *
grLEDControl[idxLED].timeDelay = grLEDControl[idxLED].updatePeriod;
* *10. Why "else"? It seems to me Sunrise isn't really changed to Sunup.* *
else
                   grLEDControl[idxLED].state = SUNUP;

* *11. Similar to question 1: How are NEON, BLUE, RED linked to the struct?* *
if( grLEDControl[NEON].state != SUNUP )
* *12. Why is this written in the following way instead of = millis() like before in the code?* *
grLEDControl[idxLED].timerLED = timeNow;
* *13. What does this mean?* *
idxLED++;
   if( idxLED == 2 ).
       idxLED = 0

```
Thank you everone for your help. I think these are all the questions for now.
moses

  1. When you do this
#define BLUE    0
#define RED     1
#define NEON    2
etc

The words BLUE, RED, NEON etc are automatically replaced in the code by the values 0, 1 and 2 just as if you had typed them in

  1. so that you don't need to keep reading millis() and also to ensure that the same value for the current time is used throughout the program

  2. The code snippet increments idxLED and if the value is now 2 it sets it back to zero. This means that idxLED can only ever have a value of 0 or 1

moserroger:
Hi everyone

I have studied the code Blackfin provided and after I have studied about struct I was able to understand most of it. Some questions remain how ever that I would like to post here:

  1. The following definitions seem to linked to the array. How are they included in die array and how are they addressed?
#define BLUE    0

#define RED    1
#define NEON    2

These are used to index into the grLEDControl array. It’s clearer (to me at least), to use descriptive labels like grLEDControl rather than grLEDControl[0] to help identify what’s being accessed.

#define SUNRISE     0

#define SUNUP      1
#define SUNSET      2
#define SUNDOWN    3

These are names for the states in the state machine. Again, the states could just be called “0”, “1” etc but names are clearer. You could also use an enum here.

  1. The definition of the members of the struct includes values, which dont seem to correspond to the commend or there seem to be two comments for the value. How am I to understand this?
.updatePeriod = 294,//2941,       //204 steps in 10 minutes == 2941 mS/step

.initialPauseSS = 500,//5000,    //5-sec pause for sunset

The dual values are for debug/real. The smaller values make the events happen ten-times faster than if the larger values were used. The comment “//” allows the longer, original values to be left there for reference or to switch back to when debug is done.

  1. For what reason does part of the funtion to be in setup? The pinMode part is understandable. But the rest?

In the for loop initial conditions are being set.

  1. How does lampState (pin 5) change? Does it have to be done manually? Is this because of the demonstration purpose?

You tell me: It’s an input. :slight_smile: What is/was driving it?

  1. What exactely happens in the following statement? Why is “grLEDControl*.updatePeriod” added? Isn’t this to increase the fading? I couldn’t find it in the fading part of the code.*
    * *grLEDControl[i].timeDelay = grLEDControl[i].initialPauseSS + grLEDControl[i].updatePeriod;* *
    [/quote]
    You wanted the heater to lag the sunrise a bit. “initialPause” – SS for sunset and SR for sunrise – do that.
    > 6. In the main loop ixdLED is defined and often linked to the struct. How does it connect to the struct to get information?
    >
    >
    > * *> switch( grLEDControl[idxLED].state )* *>
    idxLED or “index LED” tells us which of the three two LEDs is being worked on for this pass through the state machine. In some parts of the statemachine (like when determining when to turn the NEON on or off) the array members are accessed directly. But for timing fades etc, the index (idxLED) is used.
    > 7. Why is it an advantage to put timeNow instead of millis() if millis () has just been attributed to timeNow (millis()-timeNow)?
    >
    >
    > * *> timeNow = millis();* *>
    timeNow is a snapshot of millis() and doesn’t change until it is re-assigned. All the timing events will be based on that common point in time. You certainly can use millis() directly if you want but you need to be aware of the changing nature of millis(). For example, if you have:
    * *if( millis() - someTime > someTimeDelay ) {     //.     //.     //.     //...do stuff     someTime = millis();     }//if* *
    millis() could have changed between the time you compared it to someTime and when you assign millis() to someTime. The effect is subtle. If timing’s not that important there’s no need for the variable.
    Using a snapshot of millis() like timeNow can also be handy when you have many timing events being checked and you want them to stay in sync with one another.
    > 8. This seems to lead to the fade. But is it gradual over a longer period of time?
    >
    >
    > * *> i(f grLEDControl[idxLED].valPWM < grLEDControl[idxLED].maxPWM )                   grLEDControl[idxLED].valPWM++;* *>
    You’ve only got 256 values for PWM. Combined with the non-linear nature of light output from an LED vs voltage applied means the PWM may look a little “steppy”, more noticeable in the early or late stages of PWM (when the LED is dim).
    > 9. What is this for?
    >
    >
    > * *> grLEDControl[idxLED].timeDelay = grLEDControl[idxLED].updatePeriod;* *>
    timeDelay is the value against which the (millis() - timer) is made. This is just ensuring that the delay value is updated correctly. For example, if the initialPause has been applied, this assignment removes that.
    > 10. Why “else”? It seems to me Sunrise isn’t really changed to Sunup.
    >
    >
    > * *> else                   grLEDControl[idxLED].state = SUNUP;* *>
    When the PWM value is maxed out the sunrise is finished and it’s now SUNUP :slight_smile:
    > 11. Similar to question 1: How are NEON, BLUE, RED linked to the struct?
    >
    >
    > * *> if( grLEDControl[NEON].state != SUNUP )* *>
    They are not really linked to the struct as used as clear, human-readable references to an array. As above, … is (supposed to be) clearer to the reader than …[0]…
    > 12. Why is this written in the following way instead of = millis() like before in the code?
    >
    >
    > * *> grLEDControl[idxLED].timerLED = timeNow;* *>
    See above re the use of a snapshot of millis() vs the current value. For this project it probably could have gone either way without consequence. “timeNow” is just my practice and convention.
    > 13. What does this mean?
    >
    >
    > * *> idxLED++;   if( idxLED == 2 ).       idxLED = 0* *>
    Each pass through the statemachine we look at one of the two adjustable LEDs (red and blue.) idxLED points to one of those. After one is done, the value if idxLED is bumped to point to the next LED for the next pass through the SM. When that one is done, we set up to the first LED again.
    (P.S. Thanks ULHeliBob)

(P.S. Thanks ULHeliBob)

I just picked a couple of general questions to answer as I was not up to speed on the project as a whole and knew that you would be along with details of other more specific questions

Thank you both very much Blackfin and UKHeliBob

Alle your explanations have made things a lot clearer and I am getting step by step to some understanding … Unfortunately some pieces of questions remain:

  1. A basic question. What is the “gr” of “grLEDControl” an acronym for?

  2. What is the advantage of having this code in setup? Or would it be a disadvantage to put it into the main loop? Especially the following part:

grLEDControl[i].timerLED = timeNow;
        if( digitalRead( lampState ) == HIGH )
        {
            grLEDControl[i].state = SUNRISE;        
            grLEDControl[i].timeDelay = grLEDControl[i].initialPauseSR + grLEDControl[i].updatePeriod;
           
        }//if
        else
        {
            grLEDControl[i].state = SUNSET;        
            grLEDControl[i].timeDelay = grLEDControl[i].initialPauseSS + grLEDControl[i].updatePeriod;
            
        }//else
  1. I thought Blackfin put pin 5 into play to debug as an alternative to lampState. Do I actually need lampState if I have the other states such as SUNRISE, etc.?

  2. I was aware of “initialPause”. But I am still not too sure about the place where “updatePeriod” actually slowly increments the pwm-value.

5.1. the slow increment might happen in the following if loop.

if( grLEDControl[idxLED].valPWM < grLEDControl[idxLED].maxPWM )
                    grLEDControl[idxLED].valPWM++;

Would it also be possible to write it in a for loop? Would it rather be an advantage or disadvantage?

while( grLEDControl[idxLED].valPWM < grLEDControl[idxLED].maxPWM )
                    grLEDControl[idxLED].valPWM++;

Is there some other way that “updatePeriod” comes into play to increment the pwm?

  1. I am not yet quite sure if I understood the principle of idxLED. What is the advantage of it in timing faces, etc.? and to use the others such as BLUE in other statements?

6.1 If I have the following:

idxLED = 0;
switch( grLEDControl[idxLED].state )
case    SUNRISE:

This means actually that the switch statement reads the state value of the 0-member of the array with would be the blue LED? And the red LED is addressed in the following pass through the loop? This doesnt lead to delay or anything?

  1. Is there a way to make the fade less steppy? There is no way to use more that 256 steps?

  2. Doesn’t the statement mentionned and repeated below assign “timeDelay” to “updatePeriod” with “updatePeriod” being the small increments of the pwm value?

grLEDControl[idxLED].timeDelay = grLEDControl[idxLED].updatePeriod;

Does it just ensure that “timeDelay” doesnt mess up the code which could happen if “timeDelay” would stay combined with “initialPause”? Because the subsequent statement I find with “timeDelay” is the following in case SUNUP:

grLEDControl[BLUE].timeDelay = grLEDControl[BLUE].updatePeriod + grLEDControl[BLUE].initialPauseSS;

Or is there another reason for this that I am not aware of?

  1. Does this mean that “else” changes state to SUNUP after the x-time the loop has run to increment PWM to max? What is the difference of the other cases where there are prep statements for the next state?

I think this is all… Hope my questions are understandable. Thank you again for your efforts in helping me getting a bit more fluent with Arduino.

Cheers,

moses