Why does D0 never turn off? (code help)


#1

Hi all, I’m totally puzzled as to why my output turns on at the right time in this code, but once it’s on, it never turns off. My understanding is that the code enters the outer loop when the current minute is 57, then enters one of the internal loops depending on whether or not the hour is 6 or 18. Each time the internal loop increments, the “secaccum” seconds counter increments, and once it reaches the given upper boundary for the loop, we should be exiting that loop. At that point, we’re still looping inside the outer loop, but each time we increment, we also turn the output off. Once we’re outside of the 58th minute of the hour, the output is always off and the accumulator is always zero.

So, what am I missing here?

code:

// This #include statement was automatically added by the Spark IDE.
#include "SparkTime/SparkTime.h"

UDP UDPClient;
SparkTime rtc;

int secaccum;
int correction = D0;
int status = D1;

void setup() 
{
    pinMode(correction, OUTPUT);
    pinMode(status, OUTPUT);
    rtc.begin(&UDPClient, "north-america.pool.ntp.org");
    rtc.setTimeZone(-7); // gmt offset
}

void loop()
{
    while(rtc.minute(rtc.now())==57 || rtc.minute(rtc.now())==58)
    {
        if(secaccum<rtc.second(rtc.now())) secaccum++;
        while(secaccum>=57 && secaccum<=63 && (rtc.hour(rtc.now())!=6 || rtc.hour(rtc.now())!=18))
        {
            digitalWrite(correction, HIGH);
            if(secaccum<rtc.second(rtc.now())) secaccum++;
            if(secaccum>=64) break;
        }
        while(secaccum>=57 && secaccum<=67 && (rtc.hour(rtc.now())==6 || rtc.hour(rtc.now())==18))
        {
            digitalWrite(correction, HIGH);
            if(secaccum<rtc.second(rtc.now())) secaccum++;
            if(secaccum>=68) break;
        }
        digitalWrite(correction, LOW);
    }
    digitalWrite(correction, LOW);
    secaccum=0;
}

Thanks for your help!


#2

There are three issues with secaccum. The first is that it is not being initialized. The second is that it is never being updated… At least not intentionally…

Lastly, this probably should be

if(secaccum==57 ...

As this existing code sets the value to 57, not checks it

Darryl


#3

Hmm…when I pasted the code, it seems to have omitted the “>” character. Basically, anywhere you see me saying secaccum=57, it should be secaccum>=57. Weird! Also seems to have omitted several other lines of my code…

Let me try again, with an edit to properly initialize secaccum;

EDIT: Apparently this forum software has some weird bugs when you try to post code, of all things. Lines keep disappearing when I paste! I’ll have to find a different way to paste, code is still not working after I initialize secaccum using int secaccum = 0. I promise I’m updating secaccum, at least I think I am…using “if(rtc.second(rtc.now())) secaccum++;” both in the outer and inner WHILE loops (meaning, the loop that happens only between the 57th and 58th minute, and the loops that occur within that loop, which actually turn on D0.


#4

It’s not a bug, it just happened to be a markdown code :smiley:

Our suggestion is to post on a GIST and share the link. It’s more tidy that way for the thread too.


#5

I edited your first post with this formatting tip in mind:

Are you trying to make an Alarm? Try this library:

It also uses the built-in RTC so even less libraries needed.


#6

Thanks for the tips! I’m not exactly running an alarm, it’s actually a controller for an analog slave clock. I will check out that library though in case it helps.

Here’s my code as it stands now:

// This #include statement was automatically added by the Spark IDE.
#include "SparkTime/SparkTime.h"

UDP UDPClient;
SparkTime rtc;

//int secaccum = 0;
int correction = D0;
int status = D1;

void setup() 
{
    pinMode(correction, OUTPUT);
    digitalWrite(correction, LOW);
    pinMode(status, OUTPUT);
    rtc.begin(&UDPClient, "north-america.pool.ntp.org");
    rtc.setTimeZone(-7); // gmt offset
}

void loop()
{
    while(rtc.minute(rtc.now())==57 || rtc.minute(rtc.now())==58)
    {
        //if(secaccum<rtc.second(rtc.now())) secaccum++;
        //while(secaccum>=57 && secaccum<=63 && (rtc.hour(rtc.now())!=6 || rtc.hour(rtc.now())!=18))
        while((((rtc.second(rtc.now())>=57 && rtc.minute(rtc.now())==57) || (rtc.second(rtc.now())<=3 && rtc.minute(rtc.now())==58)) && (rtc.hour(rtc.now())!=6 || rtc.hour(rtc.now())!=18)))
        {
            digitalWrite(correction, HIGH);
            //if(secaccum<rtc.second(rtc.now())) secaccum++;
            //if(secaccum>=64) break;
        }
        //while(secaccum>=57 && secaccum<=67 && (rtc.hour(rtc.now())==6 || rtc.hour(rtc.now())==18))
        while((((rtc.second(rtc.now())>=57 && rtc.minute(rtc.now())==57) || (rtc.second(rtc.now())<=7 && rtc.minute(rtc.now())==58)) && (rtc.hour(rtc.now())==6 || rtc.hour(rtc.now())==18)))
        {
            digitalWrite(correction, HIGH);
            //if(secaccum<rtc.second(rtc.now())) secaccum++;
            //if(secaccum>=68) break;
        }
        digitalWrite(correction, LOW);
    }
    digitalWrite(correction, LOW);
    //secaccum=0;
}```

I just added the "digitalWrite(correction, LOW)" to the setup function, but I'm out of town so I can't test that out.

Could this be an issue with pull-down resistance? Right now I've just got an LED and a resistor connected to the output, but I forget the resistor value. I don't get the impression that the digital outputs are all that picky for this type of thing, but I figure it's worth asking!

#7

So I think we need to take a step back. Your library will poll an NTP server for the current time once when first started, then once every hour by default it seems… then keep that time updated based on the millis() counter on the Spark Core.

Every time you call rtc.now() it’s going to return a new current time in Unix time format, or maybe that’s Unix Epoch time… I dunno… the code is not commented at all really. It’s one of those times in milliseconds though… I think.

Let’s take a look at this block of code:

while((((rtc.second(rtc.now())>=57 && rtc.minute(rtc.now())==57) || (rtc.second(rtc.now())<=3 && rtc.minute(rtc.now())==58)) && (rtc.hour(rtc.now())!=6 || rtc.hour(rtc.now())!=18)))
{
  digitalWrite(correction, HIGH);
}

You have a lot of tests in there, checking the current time now() against some constant. One big problem is that every time the code sequences through and calls now() it’s different, so rtc.second(rtc.now())>=57 might be false but then when it gets to the next one the time has advanced just enough to now make that statement true. When you are AND’ing and OR’ing a bunch of things together you need to be comparing one reading to a bunch of constants, not changing readings to constants.

To fix this you need to save the now() reading to make it fixed, and then enter your multi-expression sequence. Like so:

uint32_t unix = rtc.now();
while((((rtc.second(unix)>=57 && rtc.minute(unix)==57) || (rtc.second(unix)<=3 && rtc.minute(unix)==58)) && (rtc.hour(unix)!=6 || rtc.hour(unix)!=18)))
{
  digitalWrite(correction, HIGH);
}

Now let’s see what’s going on with this while statement. This bit of code is redundant:
rtc.second(unix)>=57 && rtc.minute(unix)==57

It will be true when the second is equal to 57, but false any other time. Which is the same as just this statement alone rtc.minute(unix)==57

The rest looks like these statements:

while (
  1. second equals 57 OR
  2. (minute equals 58 AND second less than 4) AND
     (hour is not 6 OR hour is not 18)
)

The second OR is going to be a problem. I’m guessing you meant AND because if one of them evaluates to true, i.e. let’s say it’s 6pm (18o’clock), the first part will be true, the hour is not 6, so then the whole thing is true because of the OR statement. But it’s 6pm! Rats. So I’m guessing you want (hour is not 6 AND hour is not 18)

So then you can just AND them all together, with one OR statement…

while (
  1. second equals 57 OR
  2. (minute equals 58 AND 
      second less than 4 AND
      hour is not 6 AND 
      hour is not 18)
)

I’m not entirely sure what that is all doing for you, but I think I’m helping here :smile:

So building it back up:

uint32_t unix = rtc.now();
while(rtc.minute(unix)==57) || 
     (rtc.second(unix)<4 && 
      rtc.minute(unix)==58 && 
      rtc.hour(unix)!=6 &&
      rtc.hour(unix)!=18)
)
{
  digitalWrite(correction, HIGH);
}

But then since it’s a while loop, you want to update your “fixed” now() time each time through… so we just add that to the while loop like so:

uint32_t unix = rtc.now();
while(rtc.minute(unix)==57) || 
     (rtc.second(unix)<4 && 
      rtc.minute(unix)==58 && 
      rtc.hour(unix)!=6 &&
      rtc.hour(unix)!=18)
)
{
  digitalWrite(correction, HIGH);
  unix = rtc.now();
}

Some times it helps to write pseudo code first, then do your actual coding based off of easy to understand worded logic.

Hopefully this is a push in the right direction to figuring out what’s wrong with your code :wink:


#8

It works!

Took me a few months to get back to it, but here we go:

// This #include statement was automatically added by the Spark IDE.
#include "SparkTime/SparkTime.h"

UDP UDPClient;
SparkTime rtc;

int correction = D0;
int status = D1;
unsigned long unix;

void setup() 
{
    pinMode(correction, OUTPUT);
    digitalWrite(correction, LOW);
    pinMode(status, OUTPUT);
    rtc.begin(&UDPClient, "north-america.pool.ntp.org");
    rtc.setTimeZone(-7); // gmt offset
}

void loop()
{
    unix = rtc.now();
    if(rtc.second(unix)%2)
    {
        digitalWrite(status, HIGH);
    }
    else
    {
        digitalWrite(status, LOW);
    }
    if(((rtc.second(unix)>=57 && rtc.minute(unix)==57) || (rtc.second(unix)<=3 && rtc.minute(unix)==58)) && (rtc.hour(unix)!=6 && rtc.hour(unix)!=18))
    {
        digitalWrite(correction, HIGH);
        unix = rtc.now();
    }
    if(((rtc.second(unix)>=57 && rtc.minute(unix)==57) || (rtc.second(unix)<=7 && rtc.minute(unix)==58)) && (rtc.hour(unix)==6 || rtc.hour(unix)==18))
    {
        digitalWrite(correction, HIGH);
        unix = rtc.now();
    }
    else digitalWrite(correction, LOW);
    unix = rtc.now();
}

My problem turned out to be my use of “while” loops. I failed to realize that this program is one big loop already, so I didn’t need all the loops within loops. I think it was actually causing the Spark to hang up, because every time the program was supposed to start doing something, if I wanted to flash it again afterward I’d have to remove power and restart it. Now, I don’t have that problem!

Thanks for your help.