Shop OBEX P1 Docs P2 Docs Learn Events
BS2 Timer - Best Approach Request — Parallax Forums

BS2 Timer - Best Approach Request

knightofoldcodeknightofoldcode Posts: 233
edited 2007-03-27 05:07 in BASIC Stamp
Board,

I'm having a hard time figuring out the best way to proceed.

I need to be able to design some type of unit that will turn on a relay for x seconds. The number of seconds is semi-variable, ie a setting that needs to be changed when the BS2 is programmed.

I could need up to 8+ relays. Timing is not extremely critical. Timing is somewhere in the range of 5 seconds to 1 hour, maybe more.

But if the BS2 triggers relay 1 for 5 minutes, it may need to trigger relay 2 in 2 minutes...so the code can't tie down the BS2.

I can't really figure out the best way to do this... I could use a 555 timer for each and every relay, but that seems rather pointless, and seems to increase the chip count way above what it needs to be.

I know exactly how to do it in Visual Basic, and PHP.... just not in pBasic. [noparse];)[/noparse]

All I can come up with is a loop that waits 1 second, then we add up the seconds, but again that seems wasteful... I would be willing to use a RTC (Real Time Chip) but this seems overkill for this situation.

Any suggestions?

MUCH TIA,
Knight.

▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=-=-=-=-=-=-=-=-=-
This message transmitted with 100% recycled electrons.
-=-=-=-=-=-
Gravity doesn't exist. The Earth sucks.
-=-=-=-=-=-
Make a man a fire, and he will be warm for the night.
Light the man on fire, and he will be warm for the rest of his life.
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=-=-=-=-=-=-=-=-=-

Comments

  • ZootZoot Posts: 2,227
    edited 2007-03-25 02:18
    An RTC would make it easy, and they're not pricey, but if you think it's overkill, and you don't want to have to time and adjust the main loop(s) in your code, then I would use a 555 set up to run a square-wave at 1hz or so. Tie the output of the 555 to a Stamp pin.

    Now in your program, make a subroutine something like this:

    
    2hz PIN 0   'or whatever pin you use from the 555
    timer VAR Word 'this will be a countdown (or up as you prefer) timer that ticks twice every second (so it counts 1/2 second intervals) 
    
    Handle_Timer:
        timer = timer.BIT0 ^ 2hz * -1 + timer
        RETURN
    
    'or if you want to count up
    Handle_Timer:
       timer = timer.BIT0 ^ 2hz * 1 + timer
       RETURN
    
    'or if you want to count down and STOP at 0 (timeout)
       timer = timer.BIT0 ^ 2hz * -1 + timer * ( timer MAX 1 )
       RETURN
    
    



    Now all you have to do is make sure that the sub Handle_Timer is run at least 4-6 times per second and you'll get a pretty accurate counting clock. If your main loop is very fast, you can run it all the time. If you have routines that are slow within the loop, you might want to sprinkle calls to the sub throughout your program to make sure you don't miss the change in edge from the 2hz pulse.

    To use it, you can either capture and store the current timer count and compare, or just wait for pre-determined intervals if timing is not critical, etc.:

    
    someTime VAR Byte   '255 / .5 seconds = 122 seconds max count
    someTime2 VAR Word 'about 10 hours
    
    someTime = timer - 100  'presuming countdown on timer, sometime would be value of timer 50 seconds from now
    someTime2 = timer - 12000 '6000 seconds from now, or 100 hours
    DO
    
        IF someTime = timer THEN
              GOSUB DoSomethingUseful
              someTime = timer - 100   'reset timer for another 50 seconds
        ENDIF
    
        IF someTime2 = timer THEN
            'etc
           someTime2 = timer - 12000   'reset timer
        ENDIF
    
        GOSUB Handle_Timer
    
    LOOP
    
    
    



    There are also lots of cool ways you can extend and divide and use the timer for reasonably accurate time intervals. You can do a timer that will count full seconds by looking at BIT1 of the timer, or you can do stuff like

    DO 
       IF timer//120 = 0 THEN 'this would execute more or less once per minute
           'do some stuff
       ENDIF
    LOOP
    



    Now all that said, I use the SQWOUT pin from an RTC for doing this (like the DS1307) --- the RTC gives me a rock steady 1hz output, and I don't have the overhead of requesting seconds, mins, hours, and parsing them unless I actually need real clock time.

    Does this make sense?

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    When the going gets weird, the weird turn pro. -- HST
  • Mike GreenMike Green Posts: 23,101
    edited 2007-03-25 02:43
    It should really be easier than either of your suggestions. The trick is not to do the timing directly. You've got lots and lots of leeway with the minimum time interval being 5 seconds. The BS2 can easily and pretty accurately do timing to a 1/10 of a second with a PAUSE 100. You simply have a table in EEPROM for each relay with alternating time off and time on in 1/10 of a second stored in words. You have a loop that, every 1/10 of a second, decrements a word variable for each relay. If the count becomes zero, the relay gets toggled and the next count gets loaded into the variable (and the table pointer for that relay gets incremented). If the amount of time for each loop is more than 1ms, you use a PAUSE 999 instead of PAUSE 1000 (or even smaller if necessary). If a relay needs to start in the on state, you could simply initialize it that way. If you want a sequence to repeat from the beginning, just use a count of zero to mean that.
  • Sarten-XSarten-X Posts: 32
    edited 2007-03-25 02:55
    It's my understanding you need them to go only one way, meaning they all start as on, then they all turn off at different times. Whatever the case, where the methods below say TRIGGER, this means that a relay's time has come up.

    In these examples, I'll use generic units of time. This should be the resolution of your timing. Bear in mind one hour is only 3600 seconds, and that while processing time is tiny, it can add up if you do too much. The smaller a unit is, the more precise you can make the timing, but the shorter the maximum time can be (due to the limits of variables). The larger a unit is, the more accurate you can make the timing (since the pause is a known time, and the other processing isn't known). A good unit size is essentially the greatest common factor among all your times.

    Before programming:
    1. Figure out the length of 1 unit.
    2. Determine the timing for each relay, in units

    When running:
    1. Load the times, in units, into words (or if the units are large enough, like 2 units per minute, bytes) of RAM. I'd use an array, so I could use a for loop in step 3.
    2. Begin a loop until all timing variables are 0.
    3. For each of the memory spaces:
    4. If the number is 1, TRIGGER.
    5. If the number is not 0, decrease the memory value by one.
    6. Go to the next variable (loop back to step 3).
    7. Pause for 1 unit's time, in milliseconds.
    8. Loop back to step 2.

    That's basically it. That's a nice, straightforward way to do the timing, at the expense of destructively using a word (or byte) of RAM per relay.

    But you didn't ask for a simple way. You asked for the best way. So here's a way that requires the destructive use of only two bytes of RAM at minimum (t and y) to control any number of relays:

    Before programming:
    1. Figure out the length of 1 unit.
    2. Determine the timing for each relay, in units
    3. Use a DATA statement to store the timing data into the EEPROM sequentially.
    4. Use a constant (Offset) to store the offset of the timing data in EEPROM. This is 0 if you have no other DATA statements.
    6. Use the "word" prefix as necessary with DATA and READ statements

    When running:
    1. Begin a loop.
    2. Increase a timer variable t by one.
    3. Read the data at (Offset) into a temporary variable y.
    4. if t = y, TRIGGER.
    5. Read the data at (Offset+1) into y.
    6. if t = y, TRIGGER.
    7. Read the data at (Offset+2) into y.
    8. if t = y, TRIGGER.
    ...
    9. pause for 1 unit's time
    10. Loop back to step 1.

    At the expense of another variable (probably a nibble will do), here called x, you can substitute a for loop as follows:

    When running:
    1. Begin a loop.
    2. Increase a timer variable t by one.
    3. for x = 0 to n
    3. Read the data at (Offset + x) into a temporary variable y.
    4. if t = y, TRIGGER.
    5. NEXT x
    6. pause for 1 unit's time
    7. Loop back to step 1.


    You're on a BS2. You have 16 words of RAM that can be used, 12 words of which can be used easily. Obviously, if you choose your unit length so that nothing will ever exceed 255, you can store your data in bytes and save RAM.

    EDIT: As mentioned in the post above, adjust the PAUSE statements to compensate for processing time, if it's more that half a millisecond.

    Also, I apologize for the crappy formatting of this post (the lack of code blocks)... I knew it'd be long, so I typed it up in notepad.

    Post Edited (Sarten-X) : 3/25/2007 3:00:36 AM GMT
  • Sarten-XSarten-X Posts: 32
    edited 2007-03-25 03:06
    Also, a side note I found out the hard way: If you don't use transistors to properly separate the relays from the stamp, you'll burn out your I/O pins. If you want, I can get you a nice & easy circuit to use that costs only a few bucks each.
  • knightofoldcodeknightofoldcode Posts: 233
    edited 2007-03-26 06:27
    Sarten-X said...
    Also, a side note I found out the hard way: If you don't use transistors to properly separate the relays from the stamp, you'll burn out your I/O pins. If you want, I can get you a nice & easy circuit to use that costs only a few bucks each.

    Yes, I have successfully attached relay's to my BS's using a transistor... I like to use the darlington arrays with built in diodes, when I have this many relays, or even if I have 2-3 relays. But I appreciate the warning! bigsmile.gif

    I think I'm likely to go with a RTC, even though I originally stated against it. I just felt that using a RTC to calculate a small 5-10 second relay doesn't seem like a decent idea, that's why I went ahead and asked for help on the forums. I intend to use the RTC to activate relays at a set time, 1:00 AM.

    *1) I need to activate a servo at 1:00 AM.
    *2) I need to activate a relay for 15 minutes when a contact switch is pressed.
    *3) I need to monitor how long a line is held high. (Monitor how long a motor is turn on and running.)

    On some gasoline generators, I've seen a analog meter that says how long the generator has ran in it's entire life. Kind of like a odometer for a generator, I need to duplicate this in the third need. I figure I'll use some form of timing method that everyone has presented already for this requirement.

    The application is a 50-60 gallon air compressor. I need to purge out the condensation in the bottom of the tank. (First need) Also I need to engage a solenoid (using a relay) to turn on the flow of air when a user needs to use the air, that's the second need.

    I actually asked the question fairly vague, because I wanted the answer to work for about three projects...and it has. Both suggestions are going to be implemented.

    As for the solutions presented:

    Both Mike and Sarten,
    I greatly appreciate the information you provided, while it didn't help me in figuring out this project, it did help me in some other parts of a different project. [noparse]:D[/noparse] Where I'll need slightly more precision. (Around 1-2 seconds, again, lots of leeway.)

    I think that solution would require me to time my routines and adjust my pause statements to reflect the amount of time that my subroutines take...that sounds like alot of trial and error, and I'm a really lazy kinda person. wink.gif

    Since I'm going to go with a RTC anyways, for the 1:00 AM call, I think it'd be best to do what Zoot suggested and use a 555 or my RTC to provide a 1hz signal that a subroutine looks at.


    Zoot,
    It actually made perfect sense. wink.gif I think because I already had something like this in my mind, I just didn't know how to implement it.

    As for the connection to the SWQOUT pin, I own a Parallax PDB, I assume I could just connect directly the SQW line directly into a pin on the stamp without any passive components, is that correct?... I assume I should also use a pull-up or pull-down resistor, but other then that, nothing else should be needed right? I havn't actually looked at the data sheet yet for the DS1307. embarrassed.gif

    Knight.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=-=-=-=-=-=-=-=-=-
    This message transmitted with 100% recycled electrons.
    -=-=-=-=-=-
    Gravity doesn't exist. The Earth sucks.
    -=-=-=-=-=-
    Make a man a fire, and he will be warm for the night.
    Light the man on fire, and he will be warm for the rest of his life.
    -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=-=-=-=-=-=-=-=-=-
  • Sarten-XSarten-X Posts: 32
    edited 2007-03-26 07:29
    A thought about the odometer... Try a real one? Several years ago, the furnace in my house was replaced, and I still have the timer from it. It was for use on 120v stuff, and I have no idea how to hook it up to anything useful, but it's my understanding that when it got a pulse on one of its lines, it would increment the visible counter, indicating the total number of cycles. This wouldn't require any writing to the EEPROM (which would burn out eventually) and it has a visible readout. It's nearly bedtime now, but I'll see what I can find tomorrow.
  • ZootZoot Posts: 2,227
    edited 2007-03-26 14:07
    knightofoldcode -- the DS1307 requires a pull-UP on the SQW pin. You also want to tie the pull-up to a Vdd source that is switched off when the main power is off -- the SQW pin generates pulses from the backup battery. The SQW pin can also be set to other frequencies than 1hz -- see the datasheet for info on enabling and setting the freq on this pin. A current limiting resistor wouldn't hurt in the event of coding errors.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    When the going gets weird, the weird turn pro. -- HST
  • knightofoldcodeknightofoldcode Posts: 233
    edited 2007-03-27 05:07
    Sarten-X said...
    A thought about the odometer... Try a real one? Several years ago, the furnace in my house was replaced, and I still have the timer from it. It was for use on 120v stuff, and I have no idea how to hook it up to anything useful, but it's my understanding that when it got a pulse on one of its lines, it would increment the visible counter, indicating the total number of cycles. This wouldn't require any writing to the EEPROM (which would burn out eventually) and it has a visible readout. It's nearly bedtime now, but I'll see what I can find tomorrow.

    The reason I wanted the odometer is more for function... I need the BS2 to detect a runaway motor, but I don't really care about how long it's ran for it's lifetime, I was just using the generator's as a example. I do appreciate the simplicity of a manual odometer though... no eeprom writes, and no problems with battery backup... I read somewhere though that with a brand new CR2032 battery, a DS1307 would exhaust usable power from the battery in something like ten years... I don't think that'll be an issue for it, and I'll use the scratchpad memory on the DS1307 for the odometer readings, so I won't have to worry about killing my EEProm. But again, thanx.

    Much thanx, Zoot, I'll just read the datasheet. wink.gif

    Knight.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=-=-=-=-=-=-=-=-=-
    This message transmitted with 100% recycled electrons.
    -=-=-=-=-=-
    Gravity doesn't exist. The Earth sucks.
    -=-=-=-=-=-
    Make a man a fire, and he will be warm for the night.
    Light the man on fire, and he will be warm for the rest of his life.
    -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=-=-=-=-=-=-=-=-=-
Sign In or Register to comment.