Stuck with sunrise function if statements

Hello,

I'm trying to do a sunrise / sunset sketch which works fine actally, but i'm stuck right now.
I have attached the part of my sketch.
SO i'm driving an led that can be:
Manual ON
Manual OFF
Dimmed ON by the sunrise time trigger
Dimmed OFF by the sunset time trigger

IF there is for example a power outage during the sunrise/sunset cycle and power returns, i don't want the led to restart this cycle, but just turn on/off.

That is why i have the +10 in the if statements. For testing i want the led to start dimming on, but if power returns when time >10mins + sunrise time, the led should just be ON.

My problem with how i implemented this is that the dimming cycle can now never be longer then 10mins. It just turns ON after these 10mins.

How can i do this better?
I hope i'm explaining this problem good enough.

  if ( manualLEDoff == 1 ) {
    whiteDrv.setChannel8bit(3, 255);
    ledstate = "manual off";
  }
  
  else if ( manualLEDon == 1 ) {
    whiteDrv.setChannel8bit(3, 0);
    ledstate = "manual on";
  }

  else if ( minOfDay < (mSunset + 10) && minOfDay >= mSunset) {
    isDay = 0;
    isNight = 1;
    if ( !dimmedoff ) {
      if ( !SunsetStarted ) {
        Serial.println("Started Sunset Sequence");
        client.publish(mqttDimmingTopic, String("Started Sunset Sequence").c_str(), true);
      }
      ledfadeON();
      lcd.clear();
      lcd.print("Light OFF");
      ledstate = "Dimmed off";
    }

  }
  else if ( minOfDay > mSunrise && minOfDay < (mSunrise + 10) ) {
    isDay = 1;
    isNight = 0;
    if (!dimmedon) {
      if (!SunriseStarted) {
        Serial.println("Started Sunrise Sequence");
        client.publish(mqttDimmingTopic, String("Started Sunrise Sequence").c_str(), true);
      }
      ledfadeOFF();
      lcd.clear();
      lcd.print("Light ON");
      ledstate = "Dimmed on";
    }
  }

  else if ( minOfDay > (mSunrise + 10) && minOfDay < mSunset && SunriseStarted != 0) {
    whiteDrv.setChannel8bit(3, 0);
    lcd.clear();
    lcd.print("Light ON");
    ledstate = "ON after 10min";
  }

  else {
    whiteDrv.setChannel8bit(3, 255);
    lcd.clear();
    ledstate = "OFF after 10min";
  }

Please post your whole program.

That's the trivial part i'm stuck with. but i attached the entire sketch.
Sorry if it's a mess.

Aquarium_ESP.zip (6.78 KB)

Please attach the program as a .ino file. I am not going to download a ZIP file that could contain anything.

And if the program is less than 9k in length just include it in your next Reply.

...R

it's split up in multiple .h files. That's why i zipped it.
Here's all the files. Or should i put them all together?

Bart_e:
Here's all the files. Or should i put them all together?

I'm lazy - sorry. I'm not going to download and read and try to understand the links between multiple files. It just takes too long.

Can you write a short program that illustrates the problem?

...R

Ok sorry, i understand that.
I have cleared most irrelevant code.

The important piece where my problem lies is the one i quoted in the opening post.

minOfDay is the minutes since midnight
mSunrise is sunrise time in minutes
mSunset is sunset time in minutes.

I want to start the fade on when the mindofday > msunrise. But when there is a power outage during the dimming i want the led to be on immediately and not start the fade again.
Same for sunset.
All this should be overruled by the ManualLEDoff and ManualLEDon variables.

I hope this is a bit more clear.
Thx

#include <NTPClient.h>
#include <WiFiUdp.h>
#include <TimeLord.h>
#include <TimeLib.h>
#include <SimpleTimer.h>
#include <DallasTemperature.h>
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <Wire.h> 
#include <PCA9685.h>

#define ONE_WIRE_BUS D3                       // Pin for the one-wire temperature sensors


PCA9685 whiteDrv(0x40);                       // PWM breakout board i2c address

String realtime;
unsigned long epochtime;
float minOfDay;


unsigned long currentMillis;
unsigned long previousMillis = 0;
unsigned long lastUpdate = 0;

byte manualLEDon = 0;
byte manualLEDoff = 0;

int OffsetsunRise = 0;
int OffsetsunSet = 0;

int sunsethour;
int sunsetminute;

int sunrisehour;
int sunriseminute;

float mSunrise, mSunset;

byte isDay, isNight;


int fadeAmount = 1;
const long interval = 5; //7058 = 30min
unsigned int brightnessSunrise = 0;
unsigned int brightnessSunset = 255;
unsigned int brightness;

bool dimmedon, dimmedoff;
bool SunriseStarted, SunsetStarted;
String ledstate = "test";

// what is our longitude (west values negative) and latitude (south values negative)
float LONGITUDE = 4.01; //(-52.8)
float LATITUDE = 50.74; //(5.31)
TimeLord tardis;
byte today[] = {  0, 0, 12, 0, 0, 0  };


WiFiUDP ntpUDP;


// the timer object
SimpleTimer timer;

// By default 'time.nist.gov' is used with 60 seconds update interval and
// no offset
NTPClient timeClient(ntpUDP, "europe.pool.ntp.org", 3600, 60000);


OneWire oneWire(ONE_WIRE_BUS);





// a function to update the time object
void updateTime() {
  timeClient.update();
  realtime = timeClient.getFormattedTime();
  epochtime = timeClient.getEpochTime();
}


void ledfadeON(){
  

  if (!dimmedon){
    SunsetStarted = true;
    if (currentMillis - previousMillis >= interval) {
      // save the last time you blinked the LED
      previousMillis = currentMillis;

      // set the LED with the ledState of the variable:
      brightnessSunrise = brightnessSunrise + fadeAmount;
      brightness = brightnessSunrise;
      whiteDrv.setChannel8bit(3, brightness);
      Serial.println(brightnessSunrise);
    }
  }
  if (brightnessSunrise == 255){ //1020
    dimmedon = true;
    dimmedoff = false; 
  }
}

void ledfadeOFF(){
  SunriseStarted = true;
  if (!dimmedoff){
    if (currentMillis - previousMillis >= interval) {
      // save the last time you blinked the LED
      previousMillis = currentMillis;
      
      // set the LED with the ledState of the variable:
      brightnessSunset = brightnessSunset - fadeAmount;
      brightness = brightnessSunset;
      whiteDrv.setChannel8bit(3, brightness);
      Serial.println(brightnessSunset);
    }
  }
  if (brightnessSunset == 0){
    whiteDrv.setChannel8bit(3, 0);
    dimmedon = false;
    dimmedoff = true; 
  }
}



void setup() {

  Serial.begin(115200);
  //Serial.setDebugOutput(true);
  delay(100);
    
  Wire.begin(14,12);                                // initialize the PWM connection
  whiteDrv.begin();
  whiteDrv.setFrequency(1000);                      // set PWM frequency


  timeClient.begin();
  timer.setInterval(1000, updateTime);


  tardis.TimeZone(60);                      // tell TimeLord what timezone your RTC is synchronized to. You can ignore DST
                                            // as long as the RTC never changes back and forth between DST and non-DST
  tardis.Position(LATITUDE, LONGITUDE);     // tell TimeLord where in the world we are

}



void loop() {

  currentMillis = millis();

  timer.run();
  today[3] = day(epochtime);
  today[4] = month(epochtime);
  today[5] = year(epochtime);               // store today's date (at noon) in an array for TimeLord to use
  tardis.SunRise(today);                    // calculate sunrise time
  sunrisehour = today[tl_hour];
  sunriseminute = today[tl_minute];
  mSunrise = today[2] * 60 + today[1] + OffsetsunRise;
  tardis.SunSet(today);                     // calculate sunset time
  sunsethour = today[tl_hour];
  sunsetminute = today[tl_minute];
  mSunset = today[2] * 60 + today[1] + OffsetsunSet;

  minOfDay = hour(epochtime) * 60 + minute(epochtime);

  if ( manualLEDoff == 1 ) {
    whiteDrv.setChannel8bit(3, 255);
    ledstate = "manual off";
  }
  
  else if ( manualLEDon == 1 ) {
    whiteDrv.setChannel8bit(3, 0);
    ledstate = "manual on";
  }

  else if ( minOfDay < (mSunset + 10) && minOfDay >= mSunset) {
    isDay = 0;
    isNight = 1;
    if ( !dimmedoff ) {
      if ( !SunsetStarted ) {
        Serial.println("Started Sunset Sequence");
        
      }
      ledfadeON();
      ledstate = "Dimmed off";
    }

  }
  else if ( minOfDay > mSunrise && minOfDay < (mSunrise + 10) ) {
    isDay = 1;
    isNight = 0;
    if (!dimmedon) {
      if (!SunriseStarted) {
        Serial.println("Started Sunrise Sequence");
        
      }
      ledfadeOFF();
      ledstate = "Dimmed on";
    }
  }

  else if ( minOfDay > (mSunrise + 10) && minOfDay < mSunset && SunriseStarted != 0) {
    whiteDrv.setChannel8bit(3, 0);
    ledstate = "ON after 10min";
  }

  else {
    whiteDrv.setChannel8bit(3, 255);
    ledstate = "OFF after 10min";
  }


}

But when there is a power outage during the dimming i want the led to be on immediately and not start the fade again.

How is the Arduino supposed to know what you want? If there is a power outage, and the time is past mSunrise, but less than mSunrise + fade time, how is the Arduino supposed to know to just turn the lights on, rather than fade?

You are right on that. Is it a bad idea to store a value to eeprom if the sunrise cycle was started or ended?

Bart_e:
But when there is a power outage

How will you know there was a power outage?

Will it cause the Arduino to restart?

if you are using a Real Time Clock (or some external real-time source) why not just get the program to go immediately to the appropriate setting for whatever the time happens to be after power-up.

...R

I guess the best that you can hope for is to design your fade in (and fade out ?) algorithms in such a way that, if there is a power restore during the fading, that the fading starts at the same intensity as it would have done at that time point, had the power outage not happened.

OK. That is a bit abstract, so a concrete example.

17:20 sunset
17:21 LIGHT INTENSITY 90%
17:22 LIGHT INTENSITY 80%
17:23 LIGHT INTENSITY 70%
17:22 LIGHT INTENSITY 60%
. . .
. . .
17:30 Light off

If there is a power failure and power is restored at 17:23, then the light intensity is 70% and it reduces as normal after that.

Maybe another possibility is to say that if millis() < 10* 60 * 1000 ( ie less than 10 minutes from power up)
then all fading activity is prohibited.

Edit.
I'm Typing slower than Robin.

I have now added this if statement:

  if ( millis() < 10 * 60 * 1000 ){
    dimmerprohibit = 1;
  }
  else {
    dimmerprohibit = 0;
  }

and have changed the fading if statement to the following:

  if ( manualLEDoff == 1 ) {
    brightness = 255;
    whiteDrv.setChannel8bit(3, brightness);
    ledstate = "manual off";
  }
  
  else if ( manualLEDon == 1 ) {
    brightness = 0;
    whiteDrv.setChannel8bit(3, brightness);
    ledstate = "manual on";
  }

  else if ( minOfDay >= mSunset && minOfDay < mSunrise && !dimmerprohibit && brightness == 0 ) {
    isDay = 0;
    isNight = 1;
    if ( !dimmedoff ) {
      if ( !SunsetStarted ) {
        Serial.println("Started Sunset Sequence");
        client.publish(mqttDimmingTopic, String("Started Sunset Sequence").c_str(), true);
      }
      ledfadeONlog();
      lcd.clear();
      lcd.print("Light OFF");
      ledstate = "Dimmed off";
    }

  }
  else if ( minOfDay > mSunrise && minOfDay < mSunset && !dimmerprohibit && brightness == 255 ) {
    isDay = 1;
    isNight = 0;
    if (!dimmedon) {
      if (!SunriseStarted) {
        Serial.println("Started Sunrise Sequence");
        client.publish(mqttDimmingTopic, String("Started Sunrise Sequence").c_str(), true);
      }
      ledfadeOFFlog();
      lcd.clear();
      lcd.print("Light ON");
      ledstate = "Dimmed on";
    }
  }

  else if ( minOfDay > mSunrise && minOfDay < mSunset ) {
    brightness = 0;
    whiteDrv.setChannel8bit(3, brightness);
    lcd.clear();
    lcd.print("Light ON");
    ledstate = "ON after 10min";
  }

  else if ( minOfDay >= mSunset && minOfDay < mSunrise ){
    brightness = 255;
    whiteDrv.setChannel8bit(3, brightness);
    lcd.clear();
    ledstate = "OFF after 10min";
  }

If my reasoning is correct this will prohibit the dimming to start if the arduino was running for less than 10min. So for example it should turn the LED on ( brightness = 255 ).
After the 10mins the led should just stay 100% and not start the fade cycle again because of the && brightness = 255.

Let's hope it works as i think it should :slight_smile: thx for the help!!

There is a risk that those calculations will be done as integers and will overflow

if ( millis() < 10 * 60 * 1000 ){

It would be safer to do

if ( millis() < 600000UL ){ // 10 * 60 * 1000

...R

else if ( minOfDay >= mSunset && minOfDay < mSunrise && !dimmerprohibit && brightness == 0 ) {
  isDay = 1;
  isNight = 0;

really?

BulldogLowell:

else if ( minOfDay >= mSunset && minOfDay < mSunrise && !dimmerprohibit && brightness == 0 ) {

isDay = 1;
  isNight = 0;




really?

I'm using those in another function.
This is my first real "project" and experience in coding. Sorry if i do things that are not 'logical'.

Bart_e:
I'm using those in another function.
This is my first real "project" and experience in coding. Sorry if i do things that are not 'logical'.

well, if it daytime, musn't it be nighttime?

 if (isDay == 1){
    Serial.println("Licht AAN");
  }
  //if (isNight == 1){ 
  else{     //<<<<<<<<<<<<<<<< how about this instead?
    Serial.println("Licht UIT");
  }

you certainly have approached this in an interesting manner, like your serialinfo.h file that just prints global variables.

may I ask why you chose to do that? Was this code you inherited?

BulldogLowell:
well, if it daytime, musn't it be nighttime?

 if (isDay == 1){

Serial.println("Licht AAN");
  }
  //if (isNight == 1){
  else{    //<<<<<<<<<<<<<<<< how about this instead?
    Serial.println("Licht UIT");
  }





you certainly have approached this in an interesting manner, like your **serialinfo.h** file that just prints global variables.

may I ask why you chose to do that? Was this code you inherited?

Yes you are right about the day/night. I don't remember why i did it that way.
Certainly cleaner your way.

Very little code was inherited. I did the serialinfo myself for debugging purpose so i can keep track on what is happening.
Is this trange?
A lot of these things are a result of my inexperience. At the end of the day i can usually get it to work, just not very clean.

Bart_e:
Yes you are right about the day/night. I don't remember why i did it that way.
Certainly cleaner your way.

Very little code was inherited. I did the serialinfo myself for debugging purpose so i can keep track on what is happening.
Is this trange?
A lot of these things are a result of my inexperience. At the end of the day i can usually get it to work, just not very clean.

Generally, you would create such a structure (i.e. a discrete header and implementation file; *.h and/or *.cpp) when you had some code that was useful across many of your programs. Think of it like a toolbox you carry to the job, with lots of tools you rely on often but may not always use. Lots of programmers build their own toolkits and choose to include that toolkit as part of some of the programs they write. This is also common in companies that generate a lot of common-use code that is proprietary.

Also, you see separate header and implementation files (and you see this in your program too) when you are relying on someone else's code (ie a Library).

It is not common for someone to create a separate file structure which relies on Global variables. Rather, it is more common to see each of those special-purpose functions in the main program implementation simply because without that main program, the functions in that header are frankly... useless.

Ok. Thanks for explaining that.
My main reason for splitting up the sketch is to keep it a bit clean.
I'm editing my code when i have some spare time and that is mostly when i'm not behind my main desktop but using our small screen laptop.
I really hate using small screens and i find a large sketch a real pain on our laptop.

So that really is my reasoning :slight_smile:

Maybe when i get it completely working, i'll move everything together again.

Bart_e:
My main reason for splitting up the sketch is to keep it a bit clean.

Yes, but you are cleaning up the way my son cleans his bedroom, stuffing dirty laundry in drawers or closets!

Tidy up your program-specific functions, let them exist in your main file underneath loop().