How to retrigger a wait/pause with a non-multitasking PicAXE

joshzstuff

New Member
Hello all,

I am working on a surveillance program with a PicAXE 08M.
The controller will turn on a camera when it senses motion.
After it times out (in 30 sec) it will turn the camera off, however if it senses motion during the 30 seconds, I want it to "re-trigger" the wait/pause back to 30 seconds. (for each sensor output)

This might be a command that I don't know about, I'm still new here, but here are the problems I've run into with solutions I've tried:

1) created a subroutine that cycles past a pause and an "if" statement for the sensor input (decision in logicator . . . told you I was new;) )
Repeating this the appropriate number of times to achieve my delay.

problem: only responds to sensor input when on the "if" statement, not when in the "pause" state.
(this may work if I cycled it down to a faster pause time, but without the working code I can't tell if the input is affecting the circuit)
Even with the above attempt, I don't know how to re-trigger the process.

2) check for a number of appropriate pulses to trigger the input
(a subroutine cycle)
Problem: not sure of the code to implement this or if it's possible.

3) using a "inc" or "dec" command in a subroutine (like #1 example)
Problem: the command to reset the variable is giving me an error in simulation.

4) use the "interupt" command
problem: I believe I may only use this once in a program, and I'll need it at least twice if I use it in this way.

Here is a flow chart of the entire project:



Thanks!
 

Technical

Technical Support
Staff member
Welcome to the forum.
Lot of choices, but if you are new a lot of small pauses is easiest

Code:
; 30 second delay 3000 x 10ms delay
 
startagain:
for w1 = 1 to 3000
  pause 10
   if pin1 = 1 then startagain ; switch triggered, so start again
next w1
; 30 seconds now elapsed
 

geoff07

Senior Member
You can use interrupts as much as you want - you just have to re-enable them when they occur. My suggestion is have a countdown timer ticking off the seconds to zero. If an event occurs, use the interrupt to reset the timer.

As long as the interrupt routine remains short you can have a variety of things happening in them as well, so one purpose does not preclude another.

Very simple to implement, and the code remains clean because the interrupt and reset is invisible to the main code.

Here is a code fragment that does the kind of thing you want. (B_=byte;W_=word;F_=flag;C_=constant;HI, HO=Hardware inputs/Outputs)

I chopped out most of the content but you see the watchdog decrementing away unless it gets reset. The interrupt is reinitialized when the interrupt routine exits.
Code:
'main code     

{        setint %00001000,%00001000   'input 3 high 
         do          
 
            'decrement counter 
            if B_watchdog > 0 then 
               dec B_watchdog 
            endif 
            'if counter is zero, beam is broken, has not been handled, pulse relay 
            if B_watchdog = 0 and F_handle_int = C_true then 
                              
            else 
                
            endif   
         loop   
} 
      endselect 
      end 
'++++++++++++++++++++++++++++++++++++++++++++++++++++++++++     
interrupt:
{      'command received ok so reset watchdog 
      do : loop until HI_interrupt <> C_true 'wait for it to clear 
      F_handle_int = C_true 'handle the break that has been detected 
      B_watchdog = C_watchdog_init 
      setint %00001000,%00001000   'input 3 high 
      return 
}
 
Last edited:

joshzstuff

New Member
2 Questions . . .

Welcome to the forum.
Thanks Tech

Lot of choices, but if you are new a lot of small pauses is easiest

Code:
; 30 second delay 3000 x 10ms delay
 
startagain:
for w1 = 1 to 3000
  pause 10
   if pin1 = 1 then startagain ; switch triggered, so start again
next w1
; 30 seconds now elapsed
Thanks for the code!
I've had my nose in the manual and I employed your code in my project.
I think I have mastered the small pause/ "if" statement trick now, and my program has grown in complexity. (more so in my most recent program)

(see code below)

1)Word or Byte variables ?
@technical (or others . . )
I'm wondering why you chose a word (w1) as your variable?
I followed your convention in the code below, but I changed to a byte variable in the code I will post next so I could keep track of my variables better.
Are there any particular advantages using a word rather than a byte?
Does the word variables use up more memory than bytes? [and for that matter bytes than bits?]

What are the advantages to using words over bytes? (larger possible numbers?)

You can use interrupts as much as you want - you just have to re-enable them when they occur. My suggestion is have a countdown timer ticking off the seconds to zero. If an event occurs, use the interrupt to reset the timer.
Thanks for the code & info geoff, I didn't know you could reuse interrupts like that.
I haven't quite mastered the interrupts yet, but I can definitely see their potential!
Perhaps you can see how I can simplify and neaten my program in this way?
I tend to use what I know, so I'm trying to broaden my knowledge.

Here is the program I wrote that satisfied my flowchart above:


Code:
symbol StSt = b1  	              'Start/Stop button
symbol Power = b0	                      'Power button
symbol sensor = pin3	                      'PIR sensor 

let dirs = %00000011

main1:
wait 2
goto main

Main:

	if sensor = 0 then goto main
	if sensor = 1 then high Power                'turn Cam ON
	pause 300
	low Power  
	
	pause 2900
	
	high 1				'start Cam
	pause 200
	low 1
	endif 
						'Changed 'gosub' to 'goto'
	goto startagain			'STACK ERROR- > nested gosubs
	


startagain:  
for w1 = 1 to 800
  pause 10
   if sensor = 1 then startagain 
next w1 
high 1
pause 300
low 1

Standby:
for w2= 1 to 5100
		pause 10
	if sensor = 1 then high 1
	pause 300 
	low 1
	goto startagain endif    	'Changed 'gosub' to 'goto'
					  	'STACK ERROR- > nested gosubs
'	
	next w2
	
	pause 3000
	
	goto main
Forgive me if I've strayed from convention in this program.

2) My 'gosub' problem . . .
This program did what I needed, but I had to change both my "gosub" commands to "goto" before they would work . . .?

I labeled where the 'STACK ERROR' where I got in trouble with the nested "gosubs"

This program worked the first few runs but when I ran it repeatedly I found the 'gosub' problem.

I thought that if you returned to the main code you were no longer in a nested layer? Apparently I don't understand what constitutes a "nested layer" . Anyone have a hint?

I'll post my more advanced code in my next post.
 
Last edited:

joshzstuff

New Member
output issue + new code

Lot of choices, but if you are new a lot of small pauses is easiest
I like easy, but perhaps I could use some of those 'choices' to simplify things . . .

With the 'gosub' limitation (described above) I basically wrote the code linearly using goto's instead.

sorry for the large codebox, I don't yet know how to limit it's size:

The new program adds some features from the old:
- It starts the cam at first run "cam priming"
- has safety 'resets' encase the cam and the picAxe & cam timing goes out of sync (sometimes temperature affects this) "heartbeat" "safety"
- I included the principal of Technical's code with an inc that determines if the PIR is tripped by a 'nuisance' signal "nuisance"
- if the 'nuisance' condition fails the recording goes to a shorter recording first "shortRec"


I tried to label the significant parts of code, sorry if it's more complex than it needs to be (but I'm open for any criticism)

Problem:

There is an output problem in program that will not manifest itself in the simulation
Line 119 - Close Rec: works in simulation, but I cannot get it to work with the PicAxe??

I can't find anything wrong with that portion of code, but is my problem perhaps something before it that is re-routing the code?


Code:
symbol Power = b0		'Power button
symbol StSt = b1  	'Start/Stop button
symbol Time = b2
symbol detect = b3
symbol ready = b4
symbol retrigger = b5
symbol heartbeat = b6
symbol Ncount =b7

symbol sensor = pin3	'PIR sensor 

let dirs = %00000011

							;goto start 	;@sim@

main1:
wait 2				'Program delay

high 1				'diagnostic sequence
pause 200
low 1
pause 300
high 1
pause 600
low 1
wait 4

high Power				'Cam priming sequence
pause 300
low Power

pause 4500

high 1
pause 300
low 1

wait 4

high Power
pause 2000
low Power

wait 3


goto main

Main:

	high 1			'safety reset
	pause 200
	low 1

for heartbeat= 1 to 5600					
	pause 10
	
	if sensor = 1 then goto Start
	
next heartbeat 
	
	high 1			'safety reset2 (heartbeat)
	pause 200
	low 1
	goto main


Start:
	
	high Power			'power cam
	pause 200
	low power	
	
		let Ncount = 0
		let detect = 0		
for Time = 1 to 10		'nuisance prevention
	pause 350
	if sensor = 1 then inc detect endif

	if detect > 4 then inc Ncount endif 'sensitivity setting
next Time
	
	;pause 3500
	
	if Ncount > 0 then high 1	'nuisance pass
		pause 300
		low 1
		goto startagain endif
	

goto ShortVid				'nuisance fail

ShortVid:

high 1
pause 300
low 1

for retrigger = 1 to 400						'@sim@
 		 pause 10
 		 
   	if sensor = 1 then startagain 
next retrigger 
goto CloseRec	
	
CamStart:				'start Cam
	high 1			
	pause 200
	low 1


startagain:  
for retrigger = 1 to 800						'@sim@
 		 pause 10
   	if sensor = 1 then startagain 
next retrigger 

CloseRec:				'close recording
high 1			
pause 300
low 1

	pause 2550			'restart buffer
	

Standby:
for ready = 1 to 5100 							'@sim@
		pause 10
	if sensor = 1 then high 1
		pause 300 
		low 1
	
		goto startagain endif    'Changed 'gosub' to 'goto'
					 	'STACK ERROR- > nested gosubs
'	
next ready
	
	pause 3000			'restart program buffer
								;goto start 	;@sim@
	goto main
 
Last edited:

eclectic

Moderator
Sorry, but I should have explained further
Type
Code:
before
and 
[/ code] 

after the program.
But remove any spaces first

[code]
symbol Power = b0 'Power button
symbol StSt = b1 'Start/Stop button
symbol Time = b2
symbol detect = b3
symbol ready = b4
symbol retrigger = b5
symbol heartbeat = b6
symbol Ncount =b7

symbol sensor = pin3 'PIR sensor 

let dirs = %00000011

;goto start ;@sim@

main1:
wait 2 'Program delay

high 1 'diagnostic sequence
pause 200
low 1
pause 300
high 1
pause 600
low 1
wait 4

high Power 'Cam priming sequence
pause 300
low Power

pause 4500

high 1
pause 300
low 1

wait 4

high Power
pause 2000
low Power

wait 3


goto main

Main:

high 1 'safety reset
pause 200
low 1

for heartbeat= 1 to 5600 
pause 10

if sensor = 1 then goto Start

next heartbeat 

high 1 'safety reset2 (heartbeat)
pause 200
low 1
goto main


Start:

high Power 'power cam
pause 200
low power 

let Ncount = 0
let detect = 0 
for Time = 1 to 10 'nuisance prevention
pause 350
if sensor = 1 then inc detect endif

if detect > 4 then inc Ncount endif 'sensitivity setting
next Time

;pause 3500

if Ncount > 0 then high 1 'nuisance pass
pause 300
low 1
goto startagain endif


goto ShortVid 'nuisance fail

ShortVid:

high 1
pause 300
low 1

for retrigger = 1 to 400 '@sim@
pause 10

if sensor = 1 then startagain 
next retrigger 
goto CloseRec 

CamStart: 'start Cam
high 1 
pause 200
low 1


startagain: 
for retrigger = 1 to 800 '@sim@
pause 10
if sensor = 1 then startagain 
next retrigger 

CloseRec: 'close recording
high 1 
pause 300
low 1

pause 2550 'restart buffer


Standby:
for ready = 1 to 5100 '@sim@
pause 10
if sensor = 1 then high 1
pause 300 
low 1

goto startagain endif 'Changed 'gosub' to 'goto'
'STACK ERROR- > nested gosubs
' 
next ready

pause 3000 'restart program buffer
;goto start ;@sim@
goto main
 

joshzstuff

New Member
vBulletin solved . . . now for picAXE code . . . :D

Sorry, but I should have explained further
Thanks eclectic!
All fixed.

I think I imagined the quote box was a 'code' box too, which didn't help :eek:
Thanks again.

Now, . . . if there is someone out there who knows about PicAxe code . . .:D
 

geoff07

Senior Member
This would be my first version if I was doing this. It can help to write the draft in 'pseudo-code' i.e. recognising the language elements you need but not necessarily all the syntax. Then the detail can be added later.

Using gosubs is much better practice than jumping around the place with gotos, mainly because you limit the problems that can happen if you make a mistake.

There is a simple loop that does nothing until the sensor is triggered. Then it turns on the cam, pauses, and exits back to the loop. Then the second gosub sets the watchdog timeout, starts the cam, and counts down the delay. Much the same for the last step. Meanwhile, the interrupt has reset the time delay to the 30 or 65 secs according to what the program was doing when the sensor fired. I guarantee that if you used a structure like this then in a couple of years you would still be able to read the code. That isn't always possible for unstructured code!

Best also to use simple names 'high power' rather than e.g. 'low 1' for the same reason.


Code:
do
     do
           dec watchdog
      until watchdog <> 0
     'sensor has triggered the interrupt
     gosub cam_on
     gosub start_cam
     gosub stop_cam
loop

cam_on:
'power it on
pause 2000
return

start_cam:
'start recording
watchdog_time = 30
do
pause 1000
   dec watchdog
until watchdog = 0
return


stop_cam:
watchdog_time = 65
do
pause 1000
   dec watchdog
until watchdog = 0
'turn off cam
return

interrupt:
'triggered if sensor fires
'reset timer to current delay
watchdog = watchdog_time
'setint
return
 
Last edited:

westaust55

Moderator
placing program code into a post window

Thanks eclectic!
All fixed.

I think I imagined the quote box was a 'code' box too, which didn't help :eek:
Thanks again.

Now, . . . if there is someone out there who knows about PicAxe code . . .:D
Another way to place the code markers around your program code/listing is:
  1. if editing an existing post then click the "GO ADVANCED" button at the bottom right of the post/edit window
  2. copy and paste the code/listing into your post.
  3. with all of your code selected, click in the hash (#) icon at the top of the post window. Hash icon is at the very right in the second row of icons. That will place the code markers at the begging and end of the selected text.
 
Last edited:

westaust55

Moderator
I'm wondering why you chose a word (w1) as your variable?
A byte variable can only hold a value between 0 and 255.

A word variable can hold a value between 0 and 65535.

In technical's example the loop control is:

for w1 = 1 to 3000

3000 is larger than 255 and thus a word variable is required.


EDIT:
In line with the above and with refernce to you program listing in post 5,
in most of the FOR&#8230;NEXT loop structures you have the counter incrementing from 0 through to values like 5600 and similar high values.
But at the top of the program you have SYMBOL statements defining the index variables as byte variables which can hold a maximum value of 255.
 
Last edited:

joshzstuff

New Member
Another way to place the code markers around your program code/listing is:
  1. if editing an existing post then click the "GO ADVANCED" button at the bottom right of the post/edit window
  2. copy and paste the code/listing into your post.
  3. with all of your code selected, click in the hash (#) icon at the top of the post window. Hash icon is at the very right in the second row of icons. That will place the code markers at the begging and end of the selected text.
Code:
So that's what that last Icon does . . Thanks!
Smiles don't work in code ;)




@ Geoff
This would be my first version if I was doing this. It can help to write the draft in 'pseudo-code' i.e. recognising the language elements you need but not necessarily all the syntax. Then the detail can be added later.
Understood, Thanks!

Using gosubs is much better practice than jumping around the place with gotos, mainly because you limit the problems that can happen if you make a mistake.
I agree, I had to my original code used gosub's instead (although apparently improperly implemented)
I ran into trouble with them so I removed them to keep the program from hanging.
If I post my revised code would you be able to tell me where I went wrong with my nesting depth?

This program works, it has the "gosub" parts removed, but I noted where they were.
(The errors are noted in the comments after the "goto" commands that replaced them)

Code:
symbol Power = b0		'Power button
symbol StSt = b1  	'Start/Stop button
symbol Time = b2
symbol detect = b3

symbol sensor = pin3	'PIR sensor 

let dirs = %00000011


Main:

	high 1			'safety reset
	pause 200
	low 1

for w3= 1 to 5600 		'heartbeat rate
		pause 10
	if sensor = 1 then goto Start
	
next w3 
	
	high 1			'safety reset2 (heartbeat)
	pause 200
	low 1
	goto main

Start:
	
	high Power			'power cam
	pause 200
	low power	

	pause 3500
	
	
CamStart:
	high 1			'start Cam
	pause 200
	low 1


startagain:  
for w1 = 1 to 800
 		 pause 10
   	if sensor = 1 then startagain 
next w1 


high 1				'close recording
pause 300
low 1

	pause 2550			'restart buffer
	

Standby:				'standby to restart
for w2= 1 to 5100
		pause 10
	if sensor = 1 then high 1
	pause 300 
	low 1
	
	goto startagain endif    'Changed 'gosub' to 'goto'
					 'STACK ERROR- > nested gosubs
	
next w2
	
	pause 3000			'restart program buffer
	
	goto main
I guarantee that if you used a structure like this then in a couple of years you would still be able to read the code. That isn't always possible for unstructured code!
Exactly Geoff, that is my aim!
Thanks for tips on how to do this.

Best also to use simple names 'high power' rather than e.g. 'low 1' for the same reason.
Yes again,
I had trouble with identifying symbols for "input 1", so I resorted to the terrible "high 1" code.
I wished to assign pin 1 to StSt (start stop button) How should I have done this?

I don't have the time at the moment to properly analyze your code and adopt your technique (which is exactly what I intend to do!)

I am going to get some proper sit down time to analyze it then get back about the interrupts.

Very helpful, Thanks again!
 
Last edited:

westaust55

Moderator
.
Yes again,
I had trouble with identifying symbols for "input 1", so I resorted to the terrible "high 1" code.
I wished to assign pin 1 to StSt (start stop button) How should I have done this?
SYMBOL power = 1 ; define a constant
:
:
HIGH power ; same as HIGH 1


and


SYMBOL STST = pin1
:
:
If StSt = 1 THEN . . . .
 
Last edited:

geoff07

Senior Member
A quick look suggests that you didn't have the return statements to end the subroutines, as I can't see any sign of then in the last listing, and a stack error is likely to be the result.

A gosub is a subroutine call, which is a jump out to another piece of code, followed by a jump back ('return') to the statement following the gosub. That way a subroutine can be a used as shorthand for a piece of code that is used in many places, or has a specific task and can be tested on its own. It also makes it much easier to see the wood for the trees when reading the code, as the fiddly details are hidden behind a simple statement when reading the main program. The return address is placed on the stack, allowing nested gosubs and multiple returns. If a return is missing things become strange.

Your objective is code that can be read through and understood. I suspect that the current version doesn't score too highly on that!

One way to help, apart from gosubs, is the text layout. Keep to tidy columns, indent loops e.g. the contents of For/Next loops, select/case, do/until etc by a couple of characters for each nested level. Keep comments starting in the same column, etc. No blank lines. With good coding, a simple structure with gosubs and meaningful names for everything, then it should be possible to grasp what is going on very quickly.

Have a look at some of the programs in the finished projects section for some pointers. There are examples there, including some of mine, where the code is expected to be maintainable for many years - that really drives careful structure and documentation.

Sorry to preach but good coding practices will make your life so much easier when it comes to debugging and future maintenance!
 

joshzstuff

New Member
Bingo!

westaust55, somehow I missed your post.
Now that I caught it I realize how valuable it was!
A byte variable can only hold a value between 0 and 255.

A word variable can hold a value between 0 and 65535.

In technical's example the loop control is:

for w1 = 1 to 3000

3000 is larger than 255 and thus a word variable is required.
OK, that makes perfect sense!
Somehow it took you explaining it for the manual to make sense
Thank You.

EDIT:
In line with the above and with refernce to you program listing in post 5,
in most of the FOR…NEXT loop structures you have the counter incrementing from 0 through to values like 5600 and similar high values.
But at the top of the program you have SYMBOL statements defining the index variables as byte variables which can hold a maximum value of 255.
Bingo!
Which is why my program broke.
When I added the new code I also changed the variables (a poor development practice I know:eek: )

Also, when I simulated it I commented out all but the first integer of the required "for" values to expedite the INC's
This resulted in me not catching the broken code!

Yikes!:eek:

This moves me to ask for advice on my simulation technique.
I have been using the method described above (even though I will not likely make this mistake again, it has revealed some obvious woes)

Is it common practice to use a similar method of expediting INC values etc. ?
If not I'd love to hear any tips on the subject.

Thanks again westaust55!
 

joshzstuff

New Member
very true

A quick look suggests that you didn't have the return statements to end the subroutines, as I can't see any sign of then in the last listing, and a stack error is likely to be the result.
Yep you are correct, I left them out. Thank You for your observation.

The return address is placed on the stack, allowing nested gosubs and multiple returns. If a return is missing things become strange.
Here I think is what lead to my confusion.
A) There is no error message for neglecting 'returns' (e.g. if you forget an "endif")
B) The program "worked" for a time because I placed the code in a linear fashion. (so it was basically a misused "goto" application)

That would explain why the program failed on the corresponding passes as the nesting limitation!

Your objective is code that can be read through and understood. I suspect that the current version doesn't score too highly on that!
Ouch!
Well, no . . . no it doesn't.
I believe this is a product of my development excitement outpacing my housekeeping and technique.

Ok, I deserved that, what else do you have for me?

One way to help, apart from gosubs, is the text layout. Keep to tidy columns, indent loops e.g. the contents of For/Next loops, select/case, do/until etc by a couple of characters for each nested level. Keep comments starting in the same column, etc. No blank lines. With good coding, a simple structure with gosubs and meaningful names for everything, then it should be possible to grasp what is going on very quickly.
I can definitely see where I can improve here.
(e.g. the "meaningful names" could improve with my variables)

Have a look at some of the programs in the finished projects section for some pointers. There are examples there, including some of mine, where the code is expected to be maintainable for many years - that really drives careful structure and documentation.
Will do
Sorry to preach but good coding practices will make your life so much easier when it comes to debugging and future maintenance!
No, your not merely preaching but your teaching.
Thanks for your advice.
I can't promise world class code tomorrow, but I'm definitely on the road to improvement.

Thank You for your help, I know it will help me write better code!
 
Top