Announcement

Collapse
No announcement yet.

555 countdown timer design question

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Curious.George
    replied
    Re: 555 countdown timer design question

    Originally posted by Dannyx View Post
    Yes, that is correct - my bad.
    That's why letting your code speak directly is always best (i.e., cut and paste).

    I don't speak binary so I'm having a hard time following those numbers, what do they mean exactly ?
    Revisit my earlier post. Please note that these were just example values that I pulled out of the air -- your actual values will depend on how your circuit is wired, etc.

    bit = Binary digIT = 0 or 1
    octal digit = 0, 1, 2, 3, 4, 5, 6, 7
    decimal digit = 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
    hexadecimal digit = 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F

    An octal digit is a shorthand for a group of 3 contiguous bits (###). Three bits, each having one of two values, means you have 2*2*2=8 combinations {000,001,010,011,100,101,110,111}.

    A hexadecimal (hex = 6, decimal = 10) digit is a shorthand for a group of 4 contiguous bits (####). Four bits, each having one of two values, means you have 2*2*2*2=16 combinations {0000,0001,0010,0011,0100,0101,0110,0111,1000,1001,1010,1011,1100,1101,1110,1111}.

    Note that the rightmost bit is the least significant -- it changes most often, just like the rightmost digit in a decimal number changes most often (00,01,02,03,04,05,06,07,08,09,10,11...}

    If we group bits into groups of 4 bits, then a byte (8 bits) can be conveniently represented by exactly two hexadecimal digits! If we tried to use octal digits, we can only represent three bits with each octal digit so we would need three octal digits to represent an eight bit byte; hex is thus more terse.

    The "0x" prefix is a way of indicating that the digits that follow are to be interpreted as a hexadecimal value -- otherwise, you wouldn't know that "77" was decimal, hexadecimal or even octal!

    So, 0x40 is a hexadecimal value. The two nybbles (i.e., half bytes) are '4' and '0'. Being half a byte, a nybble is exactly 4 bits -- just like a hexadecimal digit!

    To decode/encode between binary bits and hexadecimal digits:
    0 = 0000
    1 = 0001
    2 = 0010
    3 = 0011
    4 = 0100
    5 = 0101
    ...
    9 = 1001
    A = 1010
    B = 1011
    ...
    F = 1111

    So, 0x40 is really 0100 0000 or 01000000, binary.

    Since they follow a DEFINE tag, it would seem you assign CH_A/B/C those values (much like I tried to do when I wrote "define CH_A 2" which assigns pin 2 to that CH_A).
    #define is a directive that tells the compiler "whenever you see the identifier CH_A, replace it with 0x40 -- LITERALLY! (actually, this is handled in the preprocessor before the compiler actually sees it). So, I could write something like

    #define CH_A HAPPY
    #define CH_B BIRTHDAY

    in which case

    CH_A CH_B

    would be seen as

    HAPPY BIRTHDAY

    This may or may not make sense to the compiler. Clearly, if CH_A and CH_B were 0x40 and 0x08, then it would be seen as

    0x40 0x08

    which probably wouldn't make sense to the compiler (you typically need an operator -- like +, -, &, ^, ... -- between two numeric values!)

    To adopt the syntax that you used in your example code, you would treat CH_A and CH_B as variables and explicitly assign values to them:

    CH_A = 0x40;
    CH_B = 0x08;

    The difference is subtle but significant. If you later write (on purpose or by accident)

    CH_A = 5;

    Then, the value of CH_A will silently change from 0x40 (which is actually 64 decimal) to 5.

    In my case, that would, instead, be seen as an error. Because the preprocessor would LITERALLY replace the "CH_A" with "0x40" and your statement would appear as

    0x40 = 5;

    which makes no sense! It is equivalent to saying

    64 = 5;

    Leave a comment:


  • stj
    replied
    Re: 555 countdown timer design question

    indeed.

    but you need to learn - it's the foundation of computer coding.

    so i'm gonna teach you.

    decimal digits go from 0-9.
    when they loop, they roll over to the next digit creating 10.

    hexadecimal digits go from 0 to F (0123456789ABCDEF)
    then looping to 10.
    so 10 in hexadecimal is actually equal to 16 in normal (decimal) numbers.

    why does this matter?
    because it relates to binary.
    1-2-4-8
    4bits
    add them up and you get 15

    1248 - 1248
    8bits - hex is FF and decimal = 255
    Last edited by stj; 12-23-2017, 11:47 AM.

    Leave a comment:


  • Dannyx
    replied
    Re: 555 countdown timer design question

    Originally posted by stj View Post
    it's not binary, it's hexadecimal.
    Proof that I don't speak it

    Leave a comment:


  • stj
    replied
    Re: 555 countdown timer design question

    it's not binary, it's hexadecimal.

    Leave a comment:


  • Dannyx
    replied
    Re: 555 countdown timer design question

    Originally posted by Curious.George View Post
    First, I assume "CH A" is supposed to be "CH_A", etc. -- spaces are not allowed inside identifiers.
    Yes, that is correct - my bad.

    #define CH_A 0x40
    #define CH_B 0x08
    #define CH_C 0x04
    #define CH_D 0x01
    I don't speak binary so I'm having a hard time following those numbers, what do they mean exactly ? Gotta hand it to the people who can actually write code like that Since they follow a DEFINE tag, it would seem you assign CH_A/B/C those values (much like I tried to do when I wrote "define CH_A 2" which assigns pin 2 to that CH_A). This worked for me as well: the code still executed properly even after using DEFINE instead of INT for my "button pin" (the same pin as before, just for testing purposes), which I read was not only better as far as memory usage is concerned, but it also prevents errors, something you mentioned way back when you said the compiler can detect errors - I may be wrong in these assumptions though.

    The problem was trying to read the bastards at the same time, since it resulted in the erratic behavior I mentioned. Seems I was on to something and got close enough, just have to use an AND rather than OR - slightly counterintuitive, since logically your brain tells you "if this OR that is pressed, do something", but it's not quite like that for a micro

    Leave a comment:


  • Curious.George
    replied
    Re: 555 countdown timer design question

    Originally posted by Dannyx View Post
    Admitting that the countdown functions properly for now (which isn't certain, but only time will tell - pun intended ), I turned my attention back to the problem of making 4 separate buttons (channels if you will) issue the same command. What I tried was defining 4 individual pins as CH A, CH B, etc. then combing them under a single element named "button" as such: button = (CH A || CH B || CH C || CH D). It didn't work: as soon as the code uploaded, it immediately started "doing stuff", as if one of the buttons was being held down.
    Note that code is far more unambiguous than prose!

    First, I assume "CH A" is supposed to be "CH_A", etc. -- spaces are not allowed inside identifiers.

    If what you wrote was:

    #define CH_A 0x40
    #define CH_B 0x08
    #define CH_C 0x04
    #define CH_D 0x01

    Then the expression "(CH_A || CH_B || CH_C || CH_D)" is an expression of constants which is, itself, a constant. In particular, "0x40 || 0x08 || 0x04 || 0x01". But, this isn't 0x4D as you might expect by or'ing the bits together. Your use of the logical operator (||) means this is interpreted as "(CH_A or CH_B or CH_C or CH_D)". Any nonzero value is interpreted as TRUE.

    So, as CH_A is nonzero (i.e., TRUE)
    and CH_B is nonzero (i.e., TRUE)
    and CH_C is nonzero (i.e., TRUE)
    and CH_D is nonzero (i.e., TRUE)

    you've got "(TRUE or TRUE or TRUE or TRUE)" which is... "TRUE"! So, the statement boils down to "button = TRUE".

    If, instead, you read the digital input and split it into individual buttons:

    masked_buttons = read(whereverthebuttonsare) & BUTTON_MASK;
    CH_A = (masked_buttons & BUTTON_A);
    CH_B = (masked_buttons & BUTTON_B);
    CH_C = (masked_buttons & BUTTON_C);
    CH_D = (masked_buttons & BUTTON_D);

    then each of these will be nonzero (i.e., TRUE) if and only if the associated button is pressed.

    But, note that they will each have different values of TRUE -- CH_A will be 0x40 when A is pressed but CH_B will be 0x08 when B is pressed, CH_C will be 0x04 when C is pressed, etc.

    This is not a problem -- as long as you don't try to compare A to B (or C, etc.). So, if you want to ask the question: "are A and B both in the same state/position?" you can't say "if (CH_A == CH_B)" because that will only be TRUE if both of them are NOT pressed (i.e., both are "0").

    It was a stupid idea really, because what is now obvious but I didn't realise at that time, is that the code also "listens" for the release of the button to do something, and in an OR statement like that 3 of the 4 pins would always read LOW, therefore you'd get a HIGH read and a LOW read simultaneously which obviously doesn't work - not because the micro doesn't allow it, but because the code doesn't know what to do first. Rather than facing the horrors of "wrestling" with the code again and trying to tweak it to account for 4 separate channels, I may end up going with the hardware solution and wire all 4 channels to the same pin.
    No, when you look for the release, you combine them as before and your test now tells you when NONE of them are TRUE (i.e., all of them are released/FALSE).

    Leave a comment:


  • stj
    replied
    Re: 555 countdown timer design question

    why are you even using this millis thing?
    just start a timer yourself.
    go on utube and search vids from "julien ilet"
    he does arduino pwm stuff with custom timers and walks you through the code.

    Leave a comment:


  • Dannyx
    replied
    Re: 555 countdown timer design question

    Admitting that the countdown functions properly for now (which isn't certain, but only time will tell - pun intended ), I turned my attention back to the problem of making 4 separate buttons (channels if you will) issue the same command. What I tried was defining 4 individual pins as CH A, CH B, etc. then combing them under a single element named "button" as such: button = (CH A || CH B || CH C || CH D). It didn't work: as soon as the code uploaded, it immediately started "doing stuff", as if one of the buttons was being held down.

    It was a stupid idea really, because what is now obvious but I didn't realise at that time, is that the code also "listens" for the release of the button to do something, and in an OR statement like that 3 of the 4 pins would always read LOW, therefore you'd get a HIGH read and a LOW read simultaneously which obviously doesn't work - not because the micro doesn't allow it, but because the code doesn't know what to do first. Rather than facing the horrors of "wrestling" with the code again and trying to tweak it to account for 4 separate channels, I may end up going with the hardware solution and wire all 4 channels to the same pin.

    Leave a comment:


  • Curious.George
    replied
    Re: 555 countdown timer design question

    Originally posted by Dannyx View Post
    Also, completely unrelated to the topic, you mentioned clocks: the clock wraps around only once if you use the 24H system like I do, since it carries on to 13:00 after hitting 12:59, then only resets once at 00:00
    The number of seconds wraps around every minute (59->00). Ditto for the number of minutes.

    Whether hours wraps once on a 24 hour clock or twice on 12 hour it still wraps. As does day-of-week, day-of-month, month, julian date, etc.

    I.e., we deal with modular arithmetic all the time but have grown accustomed to making these accommodations/compensations in our heads, almost intuitively. Your code has no intuition; if it needs to compensate, you must tell it how!

    Leave a comment:


  • Dannyx
    replied
    Re: 555 countdown timer design question

    Originally posted by Curious.George View Post
    it doesn't care how MUCH time has passed but, rather, if "more than longPress time has passed" (a simple "yes/no").
    And I believe it does that, doesn't it ?

    Also, completely unrelated to the topic, you mentioned clocks: the clock wraps around only once if you use the 24H system like I do, since it carries on to 13:00 after hitting 12:59, then only resets once at 00:00

    Leave a comment:


  • Curious.George
    replied
    Re: 555 countdown timer design question

    Originally posted by Dannyx View Post
    Still on the topic of millis, I had a read HERE and apparently there's nothing wrong if millis swings back to 0, since it doesn't produce a negative number like I'd expect it to with my "normal" math, instead it's binary and binary operates differently, giving a completely and wildly different result, in other words, anything BUT negative....
    There's never anything "wrong" with a counter/timer wrapping. The clocks that we use in our homes do it every day -- TWICE a day!

    The problem comes in how you interpret and accommodate that wrap-around.

    If you started a trip at 11:30 and it ended at 1:30 (both "unsigned" numbers), you wouldn't conclude that it was a -2 hour voyage! You know, in this case, that this is really half an hour to 12:00 followed by an hour and a half beyond 12:00 for a total of +2 hours.

    The inconvenience of this manifests when you realize that you have to make this sort of "adjustment" EVERY time you try to "do math" with these values. You're forcing your code to deal with details that it really isn't concerned with; it doesn't care how MUCH time has passed but, rather, if "more than longPress time has passed" (a simple "yes/no").

    (Inconvenience and unnecessary complexity translate to bugs)

    Leave a comment:


  • Dannyx
    replied
    Re: 555 countdown timer design question

    Still on the topic of millis, I had a read HERE and apparently there's nothing wrong if millis swings back to 0, since it doesn't produce a negative number like I'd expect it to with my "normal" math, instead it's binary and binary operates differently, giving a completely and wildly different result, in other words, anything BUT negative....
    Last edited by Dannyx; 12-20-2017, 09:15 AM.

    Leave a comment:


  • Dannyx
    replied
    Re: 555 countdown timer design question

    Originally posted by stj View Post
    can you reset the counter when you check the inputs?
    I thought of that, but there's no way of resetting millis(). Problem is no matter how much I struggle, I always end up back at millis, since I can't find a way to count down time without using it.....although I'm sure there is, I just can't figure it out for now.

    Leave a comment:


  • stj
    replied
    Re: 555 countdown timer design question

    can you reset the counter when you check the inputs?

    Leave a comment:


  • Dannyx
    replied
    Re: 555 countdown timer design question

    Let's put this in practice and correct me if I'm wrong (I probably am, that's why I'm not claiming to know anything): in my code, millis doesn't "work" all the time. Yes yes, I know millis counts ALL the time from the moment the arduino starts up, but what I mean is that it's being "called" upon only when needed, like here:

    if (digitalRead(button) == HIGH) {if (buttonActive == false) {
    buttonActive = true;
    buttonTimer = millis();}

    ....to mark the moment the button is pressed.

    Then the value is used down below, here, to determine if the button's been held down long enough

    ((unsigned long)(millis() - buttonTimer >= longPressTime) && (longPressActive == false)) {do stuff}

    So what would happen if this number rolled over to 0 ? I'd get 0 minus 1 ? (let's pretend millis has just rolled over and I manage to hit the button within one millisecond RIGHT at that very moment, which is not possible but bare with me). What I'm trying to figure out is what exactly happens when millis rolls over so I can hopefully target my searches better

    Leave a comment:


  • Curious.George
    replied
    Re: 555 countdown timer design question

    Originally posted by Dannyx View Post
    Found something on this topic HERE. It talks about using UNSIGNED longs, whereas my code uses just LONG. The difference seems to be that unsigned longs don't go negative (among other things which were far too technical to bother with for my basic skills), which from what I understand happens once millis() rolls over. I'm not sure WHAT exactly happens when an unsigned long TRIES to swing negative at that point or that I even understood this correctly.

    This apparently involves actually putting "unsigned long" before certain parts of the code, like before the line of code which detects my long press. I'd have to put "unsigned long" inside that IF....shall do some experimenting, see if it breaks the code or actually works.
    The problem has nothing to do with the choice of (signed) longs or unsigned longs. Rather, it is entirely dependant on the fact that the data has an abrupt discontinuity at some point in its range.

    E.g., a signed long will count to +2,147,483,647 and then advance to -2,147,483,648 (i.e., it looks like the value "jumped" backwards by 4,294,967,295 counts -- instead of forwards by 1!).

    Similarly, an unsigned long will count to +4,294,967,295 and then advance to 0 -- again, an abrupt jump backwards (by that same 4,294,967,295 counts), instead of continuing forwards.

    In each case, the problem arises because you have run out of bits -- you want to count to some value beyond the number of unique values that can be represented by the data type. Here, you've used up 4,294,967,296 unique values and now you want a 4,294,967,297th value!

    This is the case with all modular representations. E.g., 3 hours from 10PM isn't 13PM but, rather, 1AM.

    In the approach that I presented, time is a relative value whereas in your approach it is an absolute value (millis()) that you are trying to convert to a relative one (by using subtraction). This means you have to deal with the limitations of the absolute time representation -- namely, the discontinuity that occurs when millis() wraps.

    Had this not been discussed, here, you'd have had your code "misbehaving" every 50 days. And, when you tried to reproduce this "problem", you'd be unsuccessful -- because it wouldn't repeat for another 50 days. You may have been an eye witness to the problem (instead of relying on some third party report) yet been unable to catch it in the act.

    This is why software is harder to get right (an RC delay doesn't arbitrarily change of its own accord).

    Leave a comment:


  • Dannyx
    replied
    Re: 555 countdown timer design question

    Found something on this topic HERE. It talks about using UNSIGNED longs, whereas my code uses just LONG. The difference seems to be that unsigned longs don't go negative (among other things which were far too technical to bother with for my basic skills), which from what I understand happens once millis() rolls over. I'm not sure WHAT exactly happens when an unsigned long TRIES to swing negative at that point or that I even understood this correctly.

    This apparently involves actually putting "unsigned long" before certain parts of the code, like before the line of code which detects my long press. I'd have to put "unsigned long" inside that IF....shall do some experimenting, see if it breaks the code or actually works.

    Leave a comment:


  • Curious.George
    replied
    Re: 555 countdown timer design question

    Originally posted by redwire View Post
    Arduino millis() returns an unsigned long and rolls over after about 50 days. Before Arduino 0012, it rolled over after 9 hrs.
    Arduino Timing Rollover. Tons of discussion about it on the Internet.

    If you calculate end time, instead of the present elapsed time, your code will screw up after a rollover.
    That's the problem with any timer that continuously counts. Do you really care "what time" the button was pressed? What you really care is "how long AGO"? And, more specifically, in this case, "was it longer than 800 ms ago"?

    [Note that my implementation doesn't care if it's been 800 ms or 80,000 ms; the timer won't crap out because it implicitly stops when it "times out"!]

    So, why waste bits storing the start time if you really aren't interested in it? Do you care that the button was initially pressed at 4,294,967,290 milliseconds after some arbitrary point in time (probably when the device was powered on)? And, are you willing to live withthe consequences of that timer rolling over 6 milliseconds hence (at 4,294,967,296 == 0) to preserve that information??

    If, instead, you watch for a timer to "expire", then it can have a fixed start and end point. I.e., either counts up from zero to reflect "elapsed time" until meeting some particular "end-time"; or, down to zero from a fixed initial time that represents the interval sought. (This latter being the approach that I favor as all you ever care about after starting the timer is "how much time is left")

    In each case, the width of the timer defines the longest interval that can be measured -- regardless of when you decide to start the timer -- instead of the longest "absolute" time that can be recorded. So, I can keep track of the current "moment in time" (e.g., date) over a wide range of time (thousands of years) and to an incredibly fine resolution (nanoseconds) -- yet have timers that only deal with "seconds" and never more than a few minutes at a time.

    Or, eighteenths of seconds for a maximum interval of an hour or so (18*60*60 ~= 64K).

    Or...

    And, all can coexist without imposing their constraints on others.

    By contrast, the "millis()" approach just (typically) makes a free-running hardware timer visible to the developer and then coerces him/her into follishly thinking of ALL time on that yardstick. I.e., a timing service is worth much more than "exposing a timer".

    Leave a comment:


  • redwire
    replied
    Re: 555 countdown timer design question

    Arduino millis() returns an unsigned long and rolls over after about 50 days. Before Arduino 0012, it rolled over after 9 hrs.
    Arduino Timing Rollover. Tons of discussion about it on the Internet.

    If you calculate end time, instead of the present elapsed time, your code will screw up after a rollover.

    Leave a comment:


  • Curious.George
    replied
    Re: 555 countdown timer design question

    Originally posted by Dannyx View Post
    Will have to do some real life tests to see what happens in the (very) long run, since the thing will operate 24/7. I read that millis() overflows after approximately 50 days or so.....I obviously can't wait that long, so for now it's something I'll just have to hope doesn't screw me over. If it DOES prove to be an issue, I'll have no choice but to look deeper into it and somehow add more sophisticated timing methods, perhaps even external hardware like I read somewhere about this topic....
    If it truly is a "long" -- i.e., not a ulong -- then you'll see the first problem in ~25 days as the value wraps from +2,147,483,647 to -2,147,483,648. Thereafter, the problem will recur every ~50 days (50 days * 24 hrs / day * 60 min/ hr * 60 sec / min * 1000 ms / sec)

    A cheap fix (if supported) would be to jam a value of "0" into the timer at a convenient point in your algorithm (e.g., when you are not actively "timing" something)

    Leave a comment:

Working...
X