Shop OBEX P1 Docs P2 Docs Learn Events
How do I delay? — Parallax Forums

How do I delay?

DannyDanny Posts: 56
edited 2005-09-22 02:57 in BASIC Stamp
Working on a project, as usual.
Have a tester, part of my previous posting, have found code for state machines and implemented with no problems.
Issue is how to delay sending a "heartbeat" to let me know system is alive without missing an input trigger or release.
looking for a heartbeat about once a second with continued polling of state of inputs.
If I put a pause in the heartbeat section I miss short (<1sec triggers).
Any slick sample code or hints out there????


[noparse][[/noparse]code]'{$STAMP BS2}
'Set Serial port info here

TxD CON 16
baud CON 813
pace CON 10

Cycle VAR Nib

' contact inputs To P0 To P8
x0 VAR Byte ' old state of inputs
x1 VAR Byte ' new state of inputs
xx VAR Byte ' transition state of inputs
xy VAR Byte ' transition state 0->1

x0=INL ' read states at start

waiting:
x1=INL ' read new state
xx=x1 ^ x0 ' see if there are any transitions
xy = xx & x1 ' distinguish 0->1 xitions from 1->0
x0=x1 ' update old state

Heartbeat:
SEROUT TxD, baud, pace,[noparse][[/noparse]"H", CR]' do something
GOTO checkallinputs

checkallinputs:
FOR Cycle=1 TO 65535

BRANCH NCD xx,[noparse][[/noparse]waiting,INPUT1,INPUT2,INPUT3,INPUT4,INPUT5,INPUT6,INPUT7,INPUT8]


INPUT1:
xx=xx & %11111110 ' knock down the 1st input bit
BRANCH xy.BIT0,[noparse][[/noparse]input1opened,input1closed]
input1opened:
SEROUT TxD, baud, pace,[noparse][[/noparse]"1T", CR]' do something
GOTO checkallinputs ' back to test other inputs
input1closed:
SEROUT TxD, baud, pace,[noparse][[/noparse]"1C", CR]' do something
GOTO checkallinputs

INPUT2:
xx=xx & %11111101 ' knock down the 2nd input bit
BRANCH xy.BIT1,[noparse][[/noparse]input2opened,input2closed]
input2opened:
SEROUT TxD, baud, pace,[noparse][[/noparse]"2T", CR]' do something
GOTO checkallinputs ' back to test other inputs
input2closed:
SEROUT TxD, baud, pace,[noparse][[/noparse]"2C", CR]' do something
GOTO checkallinputs

INPUT3:
xx=xx & %11111011 ' knock down the 3rd input bit
BRANCH xy.BIT2,[noparse][[/noparse]input3opened,input3closed]
input3opened:
SEROUT TxD, baud, pace,[noparse][[/noparse]"3T", CR]' do something
GOTO checkallinputs ' back to test other inputs
input3closed:
SEROUT TxD, baud, pace,[noparse][[/noparse]"3C", CR]' do something
GOTO checkallinputs

INPUT4:
xx=xx & %11110111 ' knock down the 4th input bit
BRANCH xy.BIT3,[noparse][[/noparse]input4opened,input4closed]
input4opened:
SEROUT TxD, baud, pace,[noparse][[/noparse]"4T", CR]' do something
GOTO checkallinputs ' back to test other inputs
input4closed:
SEROUT TxD, baud, pace,[noparse][[/noparse]"4C", CR]' do something
GOTO checkallinputs

INPUT5:
xx=xx & %11101111 ' knock down the 5th input bit
BRANCH xy.BIT4,[noparse][[/noparse]input5opened,input5closed]
input5opened:
SEROUT TxD, baud, pace,[noparse][[/noparse]"5T", CR]' do something
GOTO checkallinputs ' back to test other inputs
input5closed:
SEROUT TxD, baud, pace,[noparse][[/noparse]"5C", CR]' do something
GOTO checkallinputs

INPUT6:
xx=xx & %11011111 ' knock down the 6th input bit
BRANCH xy.BIT5,[noparse][[/noparse]input6opened,input6closed]
input6opened:
SEROUT TxD, baud, pace,[noparse][[/noparse]"6T", CR]' do something
GOTO checkallinputs ' back to test other inputs
input6closed:
SEROUT TxD, baud, pace,[noparse][[/noparse]"6C", CR]' do something
GOTO checkallinputs

INPUT7:
xx=xx & %10111111 ' knock down the 7th input bit
BRANCH xy.BIT6,[noparse][[/noparse]input7opened,input7closed]
input7opened:
SEROUT TxD, baud, pace,[noparse][[/noparse]"7T", CR]' do something
GOTO checkallinputs ' back to test other inputs
input7closed:
SEROUT TxD, baud, pace,[noparse][[/noparse]"7C", CR]' do something
GOTO checkallinputs

INPUT8:
xx=xx & %01111111 ' knock down the 8th input bit
BRANCH xy.BIT7,[noparse][[/noparse]input8opened,input8closed]
input8opened:
SEROUT TxD, baud, pace,[noparse][[/noparse]"8T", CR]' do something
GOTO checkallinputs ' back to test other inputs
input8closed:
SEROUT TxD, baud, pace,[noparse][[/noparse]"8C", CR]' do something
GOTO checkallinputs

NEXT

GOTO Heartbeat

P.S. also looking for samples of parsing the H, 1T, 1C, at the other end of the serial ink.
Found some examples, but having "issues" implementing.
Thanx in advance!

▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
"Never create anything you can't control"
"The amount of intelligence on the planet is fixed... the population is growing"

Comments

  • Tracy AllenTracy Allen Posts: 6,658
    edited 2005-09-20 05:37
    The Heartbeat routine in your program listing does not take much time, to Serout "H",CR. Only less than 10 milliseconds. Much less than the 1 second dwell time of your trigger inputs. Am I missing something?

    If necessary, it may be possible to stretch the trigger pulses (with an RC circuit).

    I don't understand the purpose of the FOR-NEXT loop in the listing. The program will never reach NEXT.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Tracy Allen
    www.emesystems.com
  • Jon WilliamsJon Williams Posts: 6,491
    edited 2005-09-20 13:55
    You can create your own timer to cause the heartbeat character to go out every second -- it's pretty easy as you see below.· Also, you have a lot of redundant code that can be reduced to a nice loop; this will save you a lot of trouble later when you want to modify the processing.· I've taken the liberty to recode the major portion of your program using my "Elements of PBASIC Style" naming standards:

    Scan_Inputs:
    · newBtns = INL································ ' scan buttons
    · chgBtns = newBtns ^ oldBtns·················· ' look for changes
    · is1Btns = chgBtns & newBtns·················· ' mark 0->1 inputs
    · oldBtns = newBtns···························· ' save current scan

    Heartbeat:
    · IF (timer = 0) THEN·························· ' timer expired?
    ··· SEROUT TxD, Baud, Pace, [noparse][[/noparse]"H", CR]·········· '· yes, send "H"
    · ENDIF
    · timer = timer + 1 // 250····················· ' update timer
    · PAUSE 4

    Process_Inputs:
    · DO WHILE (chgBtns > %00000000)
    ··· tag = (NCD chgBtns) - 1···················· ' get active input
    ··· chgBtns.LOWBIT(tag) = 0···················· ' mark processed
    ··· status = "T" - (is1Btns.LOWBIT(tag) * 17)
    ··· SEROUT TxD, Baud, Pace, [noparse][[/noparse]("1" + tag), status, CR]
    · LOOP

    · GOTO Scan_Inputs

    I'm sure it's obvious that the heartbeat timer is not going to be exactly one second --·sending the heartbeat message and processing inputs will add a little extra time.· I think, though, this does what you want.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Jon Williams
    Applications Engineer, Parallax

    Post Edited (Jon Williams (Parallax)) : 9/20/2005 1:54:51 PM GMT
  • DannyDanny Posts: 56
    edited 2005-09-20 21:53
    OK, I don't understand what you are doing here.
    I've tried to upload and test this and it won't work, or compile for that matter.
    I understand giving me "hints" as opposed to doing the work for me, but I just don't get how you are reading inputs and sending data.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    "Never create anything you can't control"
    "The amount of intelligence on the planet is fixed... the population is growing"
  • Bruce BatesBruce Bates Posts: 3,045
    edited 2005-09-20 22:16
    Danny -

    You will probably need to add the following statement at the very beginning of your program:

    ' {$PBASIC 2.5}

    Regards,

    Bruce Bates
  • DannyDanny Posts: 56
    edited 2005-09-20 22:42
    Did that Bruce, didn't make a difference, however this code DOES work exactly the wayI want it!

    '{$STAMP BS2}
    'Set Serial port info here

    TxD CON 16
    baud CON 813
    pace CON 10

    Cycle VAR Nib
    Timer VAR Word

    ' contact inputs To P0 To P8
    x0 VAR Byte ' old state of inputs
    x1 VAR Byte ' new state of inputs
    xx VAR Byte ' transition state of inputs
    xy VAR Byte ' transition state 0->1

    x0=INL ' read states at start

    waiting:
    x1=INL ' read new state
    xx=x1 ^ x0 ' see if there are any transitions
    xy = xx & x1 ' distinguish 0->1 xitions from 1->0
    x0=x1 ' update old state

    Heartbeat:
    IF (timer = 500) THEN Sendheartbeat ' timer expired?
    timer = timer + 1
    ENDIF
    GOTO checkallinputs

    Sendheartbeat:
    SEROUT TxD, baud, pace,[noparse][[/noparse]"H", CR]' do something
    Timer = 0


    checkallinputs:
    BRANCH NCD xx,[noparse][[/noparse]waiting,INPUT1,INPUT2,INPUT3,INPUT4,INPUT5,INPUT6,INPUT7,INPUT8]


    INPUT1:
    xx=xx & %11111110 ' knock down the 1st input bit
    BRANCH xy.BIT0,[noparse][[/noparse]input1opened,input1closed]
    input1opened:
    SEROUT TxD, baud, pace,[noparse][[/noparse]"1T", CR]' do something
    GOTO checkallinputs ' back to test other inputs
    input1closed:
    SEROUT TxD, baud, pace,[noparse][[/noparse]"1C", CR]' do something
    GOTO checkallinputs

    INPUT2:
    xx=xx & %11111101 ' knock down the 2nd input bit
    BRANCH xy.BIT1,[noparse][[/noparse]input2opened,input2closed]
    input2opened:
    SEROUT TxD, baud, pace,[noparse][[/noparse]"2T", CR]' do something
    GOTO checkallinputs ' back to test other inputs
    input2closed:
    SEROUT TxD, baud, pace,[noparse][[/noparse]"2C", CR]' do something
    GOTO checkallinputs

    INPUT3:
    xx=xx & %11111011 ' knock down the 3rd input bit
    BRANCH xy.BIT2,[noparse][[/noparse]input3opened,input3closed]
    input3opened:
    SEROUT TxD, baud, pace,[noparse][[/noparse]"3T", CR]' do something
    GOTO checkallinputs ' back to test other inputs
    input3closed:
    SEROUT TxD, baud, pace,[noparse][[/noparse]"3C", CR]' do something
    GOTO checkallinputs

    INPUT4:
    xx=xx & %11110111 ' knock down the 4th input bit
    BRANCH xy.BIT3,[noparse][[/noparse]input4opened,input4closed]
    input4opened:
    SEROUT TxD, baud, pace,[noparse][[/noparse]"4T", CR]' do something
    GOTO checkallinputs ' back to test other inputs
    input4closed:
    SEROUT TxD, baud, pace,[noparse][[/noparse]"4C", CR]' do something
    GOTO checkallinputs

    INPUT5:
    xx=xx & %11101111 ' knock down the 5th input bit
    BRANCH xy.BIT4,[noparse][[/noparse]input5opened,input5closed]
    input5opened:
    SEROUT TxD, baud, pace,[noparse][[/noparse]"5T", CR]' do something
    GOTO checkallinputs ' back to test other inputs
    input5closed:
    SEROUT TxD, baud, pace,[noparse][[/noparse]"5C", CR]' do something
    GOTO checkallinputs

    INPUT6:
    xx=xx & %11011111 ' knock down the 6th input bit
    BRANCH xy.BIT5,[noparse][[/noparse]input6opened,input6closed]
    input6opened:
    SEROUT TxD, baud, pace,[noparse][[/noparse]"6T", CR]' do something
    GOTO checkallinputs ' back to test other inputs
    input6closed:
    SEROUT TxD, baud, pace,[noparse][[/noparse]"6C", CR]' do something
    GOTO checkallinputs

    INPUT7:
    xx=xx & %10111111 ' knock down the 7th input bit
    BRANCH xy.BIT6,[noparse][[/noparse]input7opened,input7closed]
    input7opened:
    SEROUT TxD, baud, pace,[noparse][[/noparse]"7T", CR]' do something
    GOTO checkallinputs ' back to test other inputs
    input7closed:
    SEROUT TxD, baud, pace,[noparse][[/noparse]"7C", CR]' do something
    GOTO checkallinputs

    INPUT8:
    xx=xx & %01111111 ' knock down the 8th input bit
    BRANCH xy.BIT7,[noparse][[/noparse]input8opened,input8closed]
    input8opened:
    SEROUT TxD, baud, pace,[noparse][[/noparse]"8T", CR]' do something
    GOTO checkallinputs ' back to test other inputs
    input8closed:
    SEROUT TxD, baud, pace,[noparse][[/noparse]"8C", CR]' do something

    GOTO Heartbeat

    Now to work on parsing the serial string at the other end making things happen!

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    "Never create anything you can't control"
    "The amount of intelligence on the planet is fixed... the population is growing"
  • Jon WilliamsJon Williams Posts: 6,491
    edited 2005-09-20 23:47
    My program is doing what yours is, albeit -- dare I say -- a bit more elegantly.· When you see chunks of redundant code in a program you can usually find a way to optimize your code space, and that's all I've done by putting the workhorse code in a loop.· I finished the program and ran it on a PDB -- I think it works just like you want: The "H" is transmitted about every second and when any input changes the status is reported.· I've attached code and the output (captured by the Debug Terminal).

    Here are the details on the tricky bits:

    ··· idx = (NCD chgdIns) - 1

    NCD normally returns a value of 1 to 8 (for a byte) to point to the highest set bit.· I subtract one to use it as a bit index, so idx will hold 0 to 7, depending on the input (IN0 - IN7) that's being processed.

    ··· chgdIns.LOWBIT(idx) = 0

    This clears the bit that is being processed.· LOWBIT is an array of bits for the variable it's attached to.

    ··· status = "T" - (to1Ins.LOWBIT(idx) * 17)

    This is the same as:

    ··· IF (to1Ins.LOWBIT(idx) = 0) THEN
    ····· status = "T"
    ··· ELSE
    ······status = "C"
    ····ENDIF

    ... it's just a slimmer approach.· This works because "T" - 17 = "C".

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Jon Williams
    Applications Engineer, Parallax
    560 x 594 - 67K
  • DannyDanny Posts: 56
    edited 2005-09-21 01:57
    You can dare to say anything! I am the last to be offended, I realize that most times I write programming that tends to "brute force" what I am trying to do.
    Goes with being a jack of all trades and master of none. Thanks for attaching your program, now I can actually step through it a line at a time and figure it out!

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    "Never create anything you can't control"
    "The amount of intelligence on the planet is fixed... the population is growing"
  • Jon WilliamsJon Williams Posts: 6,491
    edited 2005-09-21 03:15
    My pleasure! Brute force is okay from time-to-time, but a cool application like yours (e.g., using Tracy's input-change trick -- one of my favorites) deserves a bit of elegance. And now you have more room to grow the program!

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Jon Williams
    Applications Engineer, Parallax
  • Tracy AllenTracy Allen Posts: 6,658
    edited 2005-09-21 05:58
    The program from long ago originated from a question in a yahoo groups thread:

    http://groups.yahoo.com/group/basicstamps/message/9070
    http://groups.yahoo.com/group/basicstamps/message/9109

    There, the implication was that very different types of actions might have to be taken in response to each trigger, thus, the redundant code present there is a framework to hold the different ad hoc or brute force actions. The code is also posted at
    http://emesystems.com/BS2fsm.htm#doors

    The program came before PBASIC 2.5 and before DO-WHILE-LOOP syntax. Anyway, at the time I was pleased with the zippy BRANCH on NCD construct that reiterates on itself to loop and also to process trigger events that might occasionally be simultaneous.

    Jon's compact version and PBASIC style improvements are possible thanks to PBASIC 2.5 syntax and also due to the regular pattern here in the actions to be taken in response to each trigger. The action is simply to send the letter and the trigger number via a SEROUT. Jon, I have mixed feelings about fancy variable names. I guess I think of xx and xy are descriptive mnemonics and would probably be using Greek letters if I could! To my mind, doing so, keeping the variable names abstract, makes the structure of the logic stand out and makes it more portable to different situations. But I do agree, the comments have to get into the code to document it somehow or other.


    It looks to me like the added timer should work just fine, so long as the trigger inputs do not come too fast and furious. If need be, Danny, you could alternatively use an external RC or precision timer on a Stamp pin to enforce the one second heartbeat. For example, attach a 1.5 Mohm resistor in parallel with a 1uf capacitor from additional trigger pin p8 to ground. At the top of the loop, charge up that capacitor to +5 volts with HIGH 8, then make the pin an INPUT 8. The capacitor will discharge through the resistor with a time constant of about one second, and pin p8 can be detected going from high to low at that time. That detection could be accomplished within the framework of the trigger scanning that you have, and treated just like the other triggers. Once detected, refresh the high level for the next go round.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Tracy Allen
    www.emesystems.com
  • Jon WilliamsJon Williams Posts: 6,491
    edited 2005-09-21 12:23
    I wouldn't call my symbol-naming strategy "fancy" -- I think of it as self-commenting code. I'd rather spend a few moments defining verbose symbol names than all the extra time required to comment a program so that others can follow it.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Jon Williams
    Applications Engineer, Parallax
  • Tracy AllenTracy Allen Posts: 6,658
    edited 2005-09-21 15:58
    Your efforts toward clarity have really paid off. That goes for the whole Parallax effort, to make documentation and programs consistently readable. My hat is off.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Tracy Allen
    www.emesystems.com
  • Jon WilliamsJon Williams Posts: 6,491
    edited 2005-09-21 17:08
    Thanks, Tracy, especially for so graciously allowing me to "liberate" so many of your programming tricks! Hopefully the trimmed down version of Danny's program works for him, and ultimately makes sense.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Jon Williams
    Applications Engineer, Parallax
  • DannyDanny Posts: 56
    edited 2005-09-21 18:44
    Thanx very much Jon, Tracy, and Bruce for your help!

    Another question for you Jon, and anyone else interested.
    Obviously I have a transmitter and receiver set up here, I can transmit the input and trigger or clear message with no problem.
    On SERIN, I am trying to match the string coming from the transmit side and do actions.
    I am receiving the 1T, 1C, etc. and H meassages, and putting them in a variable word for evaluation.
    Since I know exactly what I am sending, and this is a closed system, there is no problem with having to add checksums or error correction.
    When I do a match using IF INVAR="1T" THEN dothis, I get an error message highlighting the T expecting a variable.
    I went back to the transmit program and changed to 1 letter/number signals and the error went away and I have both sides working now (thanks again).
    But, I program several types of control systems and they have very simple serial string matching, where I can list a string and the system will provide a digital output on a match and do nothing on a miss. It will match any ascii string I can throw at it. I don't expect a stamp to do this, but is there a way to macth a short mixed string easily?
    I really have to see about visiting you guys when i get out to SFO again, I drive past you at least once a month, but I figure you won't sell me anymore product if I do that!
    Thanx, Danny

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    "Never create anything you can't control"
    "The amount of intelligence on the planet is fixed... the population is growing"
  • Jon WilliamsJon Williams Posts: 6,491
    edited 2005-09-21 21:49
    You can make your life easier by sending "HH" for the heartbeat, that way every transmission is two bytes.· Now, on the RX side, you can do something like this (partial listing):

    Main:
    · SERIN RxD, Baud, [noparse][[/noparse]chan, status]
    · IF (chan = "H") THEN
    ··· ' process heartbeat
    ··ELSE
    ·· ·chan = chan - "0"····················· ' convert ASCII to digit
    ·· ·IF (status = "T") THEN
    ·· ·· ' do something for "chan"·for "T"
    ·· ·ELSE
    ·· ·· ' do something for "chan" for "C"
    ·· ·ENDIF
    · ENDIF
    · GOTO·Main

    What you have to keep in mind is that SERIN and SEROUT only work with bytes, and the BASIC Stamp doesn't support strings the way you were attempting to·do in your·code.· The example above expects two bytes; if the first is an "H" then we have a heartbeat·output and you can reset a watchdog or whatever else you need to do with the heartbeat.

    If not "H" then chan will hold the channel number as an ASCII character·so a bit of math is used to extract that.· Finally, you look at the·status character·and do the appropriate processing.
    ·

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Jon Williams
    Applications Engineer, Parallax
  • Tracy AllenTracy Allen Posts: 6,658
    edited 2005-09-22 02:57
    When you say "transmitter" and "receiver" that makes me wonder if this involves a radio link, which however might not be consistent with the statement "Since I know exactly what I am sending, and this is a closed system, there is no problem with having to add checksums or error correction."

    In any case, a header byte can help keep things in sync. Preface every pair of bytes with a single character that is always the same and does not otherwise appear in the message. Say letter "A". Then on the receiving end use a WAIT modifier, [noparse][[/noparse]WAIT ("A"),chan,status].

    In this setup, it doesn't seem that you really need two bytes. The message could be contained in the two nibbles of one byte. $1C to $8C, $1B to $8B, and $AA for heartbeat.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Tracy Allen
    www.emesystems.com
Sign In or Register to comment.