Announcement

Collapse
No announcement yet.

555 countdown timer design question

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

    Re: 555 countdown timer design question

    Originally posted by redwire View Post
    OMG it's Z-80 assembly language! You are dating yourself, I did that kind of coding in the 1980's.
    And, as far as "dating" myself, I started writing code for "microprocessors" on the i4004 -- counting bits to make sure the application would fit in the 1702's.

    Amusing when I see my current designs having more resources on board (many megabytes and gigahertz) than the PDP-11 that hosted the i4004's tools all those years ago!

    Comment


      Re: 555 countdown timer design question

      There's another thing I forgot to talk about: the remote has 4 buttons, hence 4 outputs. My original plan was to have all 4 doing the same thing, which may seem like a stupid idea and defeats the purpose, but will make it easier in the long run for the end user, rather than having to remember which button does what.

      Long story short: can I make 4 buttons do the same thing without having to fill the code with OR statements ? One "hardware" idea I had was to run diodes between each output of the remote module and the same pin I already defined as "button" on the arduino (just to prevent the output of one channel blowing up the rest, though I doubt it would damage anything - just a precaution). The software solution would be to have each channel assigned to a pin and tweak the code, but I'm not sure how you define "button" as being pins 1, 2, 3 AND 4.....not sure if it's possible.

      Also, CuriousGeorge suggested using a variable to store millis(), like NOW or such - should this have "long" before it like the rest of those variables in the code ?
      Wattevah...

      Comment


        Re: 555 countdown timer design question

        you just check your input port for "F" or something else - indicating one of the inputs has changed.

        Comment


          Re: 555 countdown timer design question

          Originally posted by Dannyx View Post
          Long story short: can I make 4 buttons do the same thing without having to fill the code with OR statements ?
          That depends on how the 4 inputs are presented (electrically) to the MCU.

          If they can all be "seen" (queried) in one operation (i.e., readPort or somesuch), then you can examine all of them at the same time.

          Imagine each button indicates when it is "pressed" by reading a HIGH level on some pin. AND, that this HIGH is reflected internally, in the software, as a '1' bit.

          Now, imagine that you "read" something to get the current levels of these bits. Assume that yields an 8bit value:
          76543210
          (the numbers indicating the "bit number", not the "value" of the bit)

          Call your four BIT inputs A, B, C, D. Assume they appear in this 8bit value in these places:
          7A54BD1C
          (note that I've deliberately scrambled their order and NOT arranged for them to be contiguous just to illustrate that they NEED NOT BE!)

          The other bits (7, 5, 4 and 1) may contain other information. Or not. In any case, their values are not of interest to us! So, we effectively have:
          xAxxBDxC
          where the x's mean we don't care about the bit's value.

          We can deliberately ignore these don't cares by masking them off -- forcing them to take a particular value (like '0') with a bit-wise logical operator, in this case, "AND":
          xAxxBDxC
          01001101
          =======
          0A00BD0C

          Now, if NONE of the buttons are pressed, all of A, B, C and D will be '0's so you'll see 00000000 after this operation. If JUST the A button is pressed,
          you'll see 01000000; just B yields 00001000; etc.

          If A and B are pressed, you'll see 01001000; B and C yields 00001001, etc.

          If ANY button or any number of buttons are pressed, you will see something that is NOT "00000000". So:
          buttons = read(whereverthebuttonsare)
          if (buttons & 01001101) { a_button_iss_pressed }

          The "professional" way of doing this is to create symbolic names for each button:
          #define BUTTON_A (0x40)
          #define BUTTON_B (0x08)
          #define BUTTON_C (0x04)
          #define BUTTON_D (0x01)
          and use these to build up a mask that isolates just the buttons:
          #define BUTTON_MASK (BUTTON_A | BUTTON_B | BUTTON_C | BUTTON_D)

          [You'll see that this resolves to 0x4D -- which is 01001101]

          Thereafter, you can do things like:
          masked_buttons = read(whereverthebuttonsare) & BUTTON_MASK;
          if (masked_buttons & BUTTON_A) {at least button A is pressed}
          if (masked_buttons & BUTTON_B) {at least button B is pressed}
          if (masked_buttons & (BUTTON_A | BUTTON_B)) {button A and/or B pressed}

          It gets a little trickier to look for combinations of buttons:
          if ((masked_buttons & (BUTTON_A | BUTTON_B)) == (BUTTON_A | BUTTON_B)){button A and B pressed}

          Also, CuriousGeorge suggested using a variable to store millis(), like NOW or such - should this have "long" before it like the rest of those variables in the code ?
          There's no real SIMPLE right answer, here. There's an inherent flaw in "watching a counter/timer" that my implementation avoids. Pick any size variable (int, long, etc.) There is a maximum value that can be stored in that variable -- just like the "millis()" timer has a maximum value, beyond which it wraps around.

          When the counter/timer/variable gets to that maximum, there is a discontinuity in the values as it wraps around -- the "next" value suddenly is less than the previous value... a LOT less!

          Assume your variable holds values 0-9. When it gets to 9, the next value will be 0. If you are measuring elapsed time by comparing the "current" value to the previous value and forming the arithmetic difference (i.e., current - previous), then suddenly "-9" time units have transpired since that previous time!

          The size of the counter/timer/variable just moves the point at which this happens. E.g., if you can count 0-99 then the 99 to 0 transition looks like "-99" time units.

          Of course, if you happened to capture the value when it was "87" and then again when it was "03" (which is really 103, sort of), you'll see "-84" time units (instead of the 103-87=16 that really elapsed).

          There is a very technical name for this sort of problem. We, in the industry, call it a "bug"! <grin> You have to decide if you want to fix it or live with it... rationalizing that it PROBABLY won't bite you too often...

          Comment


            Re: 555 countdown timer design question

            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....
            Wattevah...

            Comment


              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)

              Comment


                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.

                Comment


                  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".

                  Comment


                    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.
                    Wattevah...

                    Comment


                      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).

                      Comment


                        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
                        Wattevah...

                        Comment


                          Re: 555 countdown timer design question

                          can you reset the counter when you check the inputs?

                          Comment


                            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.
                            Wattevah...

                            Comment


                              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.
                              Wattevah...

                              Comment


                                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)

                                Comment


                                  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
                                  Wattevah...

                                  Comment


                                    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!

                                    Comment


                                      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.
                                      Wattevah...

                                      Comment


                                        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.

                                        Comment


                                          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).

                                          Comment

                                          Working...
                                          X